mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-15 06:39:17 +00:00
fix(parser): preserve terminal license comments (#25400)
## Summary - Preserve legal comments whose `@license` marker ends exactly at comment-content EOF. - Add parser and minifier regression coverage. ## Details The optimized marker scan excluded the final valid start for the eight-byte `@license` marker. Comments such as `/*x@license*/` were parsed but classified as ordinary comments and could therefore be dropped from legal-comment output or extraction. The parser hot path now includes that final candidate while retaining unchecked reads, with an explicit remaining-length guard before reading the nine-byte `@preserve` marker. ## Ecosystem compatibility This makes Oxc recognize terminal markers such as `/*x@license*/` and `//x@license`, matching tested behavior in esbuild 0.28.1 and SWC 1.15.47 when their legal/`"some"` comment retention is enabled.
This commit is contained in:
1 parent
95ece63e63
commit
c49f4ce415
2 files changed
+27
-9
No files matched your search
@@ -700,6 +700,11 @@ fn preserve_at_license_legal_comment_when_dce_removes_anchor() {
|
||||
"/* @license */\nconsole.log('val');",
|
||||
CompressOptions::dce(),
|
||||
);
|
||||
test_with_options(
|
||||
"/*x@license*/\nconst foo = 'val';\nconsole.log(foo);",
|
||||
"/*x@license*/\nconsole.log('val');",
|
||||
CompressOptions::dce(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -369,31 +369,34 @@ impl<'a> TriviaBuilder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::inline_always)]
|
||||
#[inline(always)]
|
||||
fn contains_license_or_preserve_comment(s: &str) -> bool {
|
||||
const LICENSE_LEN: usize = b"@license".len();
|
||||
const PRESERVE_LEN: usize = b"@preserve".len();
|
||||
let hay = s.as_bytes();
|
||||
|
||||
if hay.len() < 9 {
|
||||
if hay.len() < LICENSE_LEN {
|
||||
return false;
|
||||
}
|
||||
|
||||
let search_len = hay.len() - 8;
|
||||
let search_len = hay.len() - LICENSE_LEN + 1;
|
||||
|
||||
for i in memchr_iter(b'@', &hay[..search_len]) {
|
||||
debug_assert!(i < search_len);
|
||||
// SAFETY: we `i` has a max val of len of bytes - 8, so accessing `i + 1` is safe
|
||||
debug_assert!(hay.len() - i >= LICENSE_LEN);
|
||||
// SAFETY: `search_len` only includes candidate starts with at least `LICENSE_LEN` bytes
|
||||
// remaining, so `i + 1` and the full `@license` range are in bounds.
|
||||
match unsafe { hay.get_unchecked(i + 1) } {
|
||||
// spellchecker:off
|
||||
b'l'
|
||||
// SAFETY: we `i` has a max val of len of bytes - 8, so accessing `i + 7` is safe
|
||||
if unsafe { hay.get_unchecked(i + 2..i + 1 + 7) } == b"icense" =>
|
||||
// SAFETY: The candidate bound proves the full `@license` range is in bounds.
|
||||
if unsafe { hay.get_unchecked(i + 2..i + LICENSE_LEN) } == b"icense" =>
|
||||
{
|
||||
return true;
|
||||
}
|
||||
b'p'
|
||||
// SAFETY: we `i` has a max val of len of bytes - 8, so accessing `i + 8` is safe
|
||||
if unsafe { hay.get_unchecked(i + 2..i + 1 + 8) } == b"reserve" =>
|
||||
b'p' if hay.len() - i >= PRESERVE_LEN
|
||||
// SAFETY: The preceding guard proves the full `@preserve` range is in bounds.
|
||||
&& unsafe { hay.get_unchecked(i + 2..i + PRESERVE_LEN) } == b"reserve" =>
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -783,10 +786,20 @@ function bar() {}";
|
||||
("/* @license */", CommentContent::Legal),
|
||||
("/* foo @preserve */", CommentContent::Legal),
|
||||
("/* foo @license */", CommentContent::Legal),
|
||||
("/* foo @preserve*/", CommentContent::Legal),
|
||||
("/* foo @license*/", CommentContent::Legal),
|
||||
("/* foo @licensed*/", CommentContent::Legal),
|
||||
("/* foo @License*/", CommentContent::None),
|
||||
// spellchecker:disable-next-line
|
||||
("/* foo @licens*/", CommentContent::None),
|
||||
// spellchecker:disable-next-line
|
||||
("/* foo @preserv*/", CommentContent::None),
|
||||
("/* @foo @preserve */", CommentContent::Legal),
|
||||
("/* @foo @license */", CommentContent::Legal),
|
||||
("/** foo @preserve */", CommentContent::JsdocLegal),
|
||||
("/** foo @license */", CommentContent::JsdocLegal),
|
||||
("/** foo @license*/", CommentContent::JsdocLegal),
|
||||
("// foo @license", CommentContent::Legal),
|
||||
("/** jsdoc */", CommentContent::Jsdoc),
|
||||
("/**/", CommentContent::None),
|
||||
("/***/", CommentContent::None),
|
||||
|
||||
Reference in new issue
Block a user