diff --git a/crates/oxc_ast/src/ast/comment.rs b/crates/oxc_ast/src/ast/comment.rs index a54e2595128e4..9ca7d9f6c47fb 100644 --- a/crates/oxc_ast/src/ast/comment.rs +++ b/crates/oxc_ast/src/ast/comment.rs @@ -38,6 +38,43 @@ pub enum CommentPosition { Trailing = 1, } +/// Annotation comment that has special meaning. +#[ast] +#[generate_derive(CloneIn, ContentEq)] +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)] +pub enum CommentAnnotation { + /// No Annotation + #[default] + None = 0, + + /// `/** jsdoc */` + /// + Jsdoc = 1, + + /// Legal Comment + /// e.g. `/* @license */`, `/* @preserve */`, or starts with `//!` or `/*!`. + /// + /// + Legal = 2, + + /// `/* #__PURE__ */` + /// + Pure = 3, + + /// `/* #__NO_SIDE_EFFECTS__ */` + NoSideEffects = 4, + + /// Webpack magic comment + /// e.g. `/* webpackChunkName */` + /// + Webpack = 5, + + /// Vite comment + /// e.g. `/* @vite-ignore */` + /// + Vite = 6, +} + /// A comment in source code. #[ast] #[generate_derive(CloneIn, ContentEq)] @@ -64,6 +101,9 @@ pub struct Comment { /// Whether this comment has a tailing newline. pub followed_by_newline: bool, + + /// Comment Annotation + pub annotation: CommentAnnotation, } impl Comment { @@ -78,6 +118,15 @@ impl Comment { position: CommentPosition::Trailing, preceded_by_newline: false, followed_by_newline: false, + annotation: CommentAnnotation::None, + } + } + + /// Gets the span of the comment content. + pub fn content_span(&self) -> Span { + match self.kind { + CommentKind::Line => Span::new(self.span.start + 2, self.span.end), + CommentKind::Block => Span::new(self.span.start + 2, self.span.end - 2), } } @@ -101,35 +150,43 @@ impl Comment { self.position == CommentPosition::Trailing } - /// Returns `true` if this comment is a JSDoc comment. Implies `is_leading` - /// and `is_block`. - pub fn is_jsdoc(&self, source_text: &str) -> bool { - self.is_leading() && self.is_block() && { - let span = self.content_span(); - !span.is_empty() && source_text.as_bytes()[span.start as usize] == b'*' - } + /// Is comment with special meaning. + pub fn is_annotation(self) -> bool { + self.annotation != CommentAnnotation::None + } + + /// Returns `true` if this comment is a JSDoc comment. Implies `is_leading` and `is_block`. + pub fn is_jsdoc(self) -> bool { + self.is_leading() && self.annotation == CommentAnnotation::Jsdoc } /// Legal comments /// /// A "legal comment" is considered to be any statement-level comment /// that contains `@license` or `@preserve` or that starts with `//!` or `/*!`. + /// /// - pub fn is_legal(&self, source_text: &str) -> bool { - if !self.is_leading() { - return false; - } - let source_text = self.content_span().source_text(source_text); - source_text.starts_with('!') - || source_text.contains("@license") - || source_text.contains("@preserve") + pub fn is_legal(self) -> bool { + self.is_leading() && self.annotation == CommentAnnotation::Legal } - /// Gets the span of the comment content. - pub fn content_span(&self) -> Span { - match self.kind { - CommentKind::Line => Span::new(self.span.start + 2, self.span.end), - CommentKind::Block => Span::new(self.span.start + 2, self.span.end - 2), - } + /// Is `/* @__PURE__*/`. + pub fn is_pure(self) -> bool { + self.annotation == CommentAnnotation::Pure + } + + /// Is `/* @__NO_SIDE_EFFECTS__*/`. + pub fn is_no_side_effects(self) -> bool { + self.annotation == CommentAnnotation::NoSideEffects + } + + /// Is webpack magic comment. + pub fn is_webpack(self) -> bool { + self.annotation == CommentAnnotation::Webpack + } + + /// Is vite special comment. + pub fn is_vite(self) -> bool { + self.annotation == CommentAnnotation::Vite } } diff --git a/crates/oxc_ast/src/generated/assert_layouts.rs b/crates/oxc_ast/src/generated/assert_layouts.rs index facfff7f064c2..0e11e40af14a9 100644 --- a/crates/oxc_ast/src/generated/assert_layouts.rs +++ b/crates/oxc_ast/src/generated/assert_layouts.rs @@ -1396,7 +1396,10 @@ const _: () = { assert!(size_of::() == 1); assert!(align_of::() == 1); - assert!(size_of::() == 16); + assert!(size_of::() == 1); + assert!(align_of::() == 1); + + assert!(size_of::() == 24); assert!(align_of::() == 8); assert!(offset_of!(Comment, span) == 0); assert!(offset_of!(Comment, attached_to) == 8); @@ -1404,6 +1407,7 @@ const _: () = { assert!(offset_of!(Comment, position) == 13); assert!(offset_of!(Comment, preceded_by_newline) == 14); assert!(offset_of!(Comment, followed_by_newline) == 15); + assert!(offset_of!(Comment, annotation) == 16); }; #[cfg(target_pointer_width = "32")] @@ -2795,7 +2799,10 @@ const _: () = { assert!(size_of::() == 1); assert!(align_of::() == 1); - assert!(size_of::() == 16); + assert!(size_of::() == 1); + assert!(align_of::() == 1); + + assert!(size_of::() == 20); assert!(align_of::() == 4); assert!(offset_of!(Comment, span) == 0); assert!(offset_of!(Comment, attached_to) == 8); @@ -2803,6 +2810,7 @@ const _: () = { assert!(offset_of!(Comment, position) == 13); assert!(offset_of!(Comment, preceded_by_newline) == 14); assert!(offset_of!(Comment, followed_by_newline) == 15); + assert!(offset_of!(Comment, annotation) == 16); }; #[cfg(not(any(target_pointer_width = "64", target_pointer_width = "32")))] diff --git a/crates/oxc_ast/src/generated/derive_clone_in.rs b/crates/oxc_ast/src/generated/derive_clone_in.rs index c6c06004127a3..d9186c6d398ef 100644 --- a/crates/oxc_ast/src/generated/derive_clone_in.rs +++ b/crates/oxc_ast/src/generated/derive_clone_in.rs @@ -4154,6 +4154,21 @@ impl<'alloc> CloneIn<'alloc> for CommentPosition { } } +impl<'alloc> CloneIn<'alloc> for CommentAnnotation { + type Cloned = CommentAnnotation; + fn clone_in(&self, _: &'alloc Allocator) -> Self::Cloned { + match self { + Self::None => CommentAnnotation::None, + Self::Jsdoc => CommentAnnotation::Jsdoc, + Self::Legal => CommentAnnotation::Legal, + Self::Pure => CommentAnnotation::Pure, + Self::NoSideEffects => CommentAnnotation::NoSideEffects, + Self::Webpack => CommentAnnotation::Webpack, + Self::Vite => CommentAnnotation::Vite, + } + } +} + impl<'alloc> CloneIn<'alloc> for Comment { type Cloned = Comment; fn clone_in(&self, allocator: &'alloc Allocator) -> Self::Cloned { @@ -4164,6 +4179,7 @@ impl<'alloc> CloneIn<'alloc> for Comment { position: CloneIn::clone_in(&self.position, allocator), preceded_by_newline: CloneIn::clone_in(&self.preceded_by_newline, allocator), followed_by_newline: CloneIn::clone_in(&self.followed_by_newline, allocator), + annotation: CloneIn::clone_in(&self.annotation, allocator), } } } diff --git a/crates/oxc_ast/src/generated/derive_content_eq.rs b/crates/oxc_ast/src/generated/derive_content_eq.rs index ff30c5314d026..ac586b9736972 100644 --- a/crates/oxc_ast/src/generated/derive_content_eq.rs +++ b/crates/oxc_ast/src/generated/derive_content_eq.rs @@ -2497,6 +2497,12 @@ impl ContentEq for CommentPosition { } } +impl ContentEq for CommentAnnotation { + fn content_eq(&self, other: &Self) -> bool { + self == other + } +} + impl ContentEq for Comment { fn content_eq(&self, other: &Self) -> bool { ContentEq::content_eq(&self.attached_to, &other.attached_to) @@ -2504,5 +2510,6 @@ impl ContentEq for Comment { && ContentEq::content_eq(&self.position, &other.position) && ContentEq::content_eq(&self.preceded_by_newline, &other.preceded_by_newline) && ContentEq::content_eq(&self.followed_by_newline, &other.followed_by_newline) + && ContentEq::content_eq(&self.annotation, &other.annotation) } } diff --git a/crates/oxc_ast/src/lib.rs b/crates/oxc_ast/src/lib.rs index 832ce2320b688..22c44f2058dfd 100644 --- a/crates/oxc_ast/src/lib.rs +++ b/crates/oxc_ast/src/lib.rs @@ -71,7 +71,7 @@ mod generated { pub use generated::{ast_builder, ast_kind}; pub use crate::{ - ast::comment::{Comment, CommentKind, CommentPosition}, + ast::comment::{Comment, CommentAnnotation, CommentKind, CommentPosition}, ast_builder::AstBuilder, ast_builder_impl::NONE, ast_kind::{AstKind, AstType}, diff --git a/crates/oxc_codegen/src/comment.rs b/crates/oxc_codegen/src/comment.rs index 93a75c0708a19..0e991d4a3627c 100644 --- a/crates/oxc_codegen/src/comment.rs +++ b/crates/oxc_codegen/src/comment.rs @@ -9,7 +9,12 @@ pub type CommentsMap = FxHashMap>; impl Codegen<'_> { pub(crate) fn build_comments(&mut self, comments: &[Comment]) { + self.comments.reserve(comments.len()); for comment in comments { + // Omit pure comments because they are handled separately. + if comment.is_pure() || comment.is_no_side_effects() { + continue; + } self.comments.entry(comment.attached_to).or_default().push(*comment); } } @@ -18,34 +23,9 @@ impl Codegen<'_> { self.comments.contains_key(&start) } - pub(crate) fn has_non_annotation_comment(&self, start: u32) -> bool { - if self.options.print_annotation_comments() { - self.comments.get(&start).is_some_and(|comments| { - comments.iter().any(|comment| !self.is_pure_comment(comment)) - }) - } else { - self.has_comment(start) - } - } - - /// `#__PURE__` Notation Specification - /// - /// - fn is_pure_comment(&self, comment: &Comment) -> bool { - let s = comment.content_span().source_text(self.source_text).trim_start(); - if let Some(s) = s.strip_prefix(['@', '#']) { - s.starts_with("__PURE__") || s.starts_with("__NO_SIDE_EFFECTS__") - } else { - false - } - } - /// Whether to keep leading comments. - fn is_leading_comments(&self, comment: &Comment) -> bool { - comment.preceded_by_newline - && (comment.is_jsdoc(self.source_text) && !self.is_pure_comment(comment)) - && !comment.content_span().source_text(self.source_text).chars().all(|c| c == '*') - // webpack comment `/*****/` + fn should_keep_leading_comment(comment: &Comment) -> bool { + comment.preceded_by_newline && comment.is_annotation() } pub(crate) fn print_leading_comments(&mut self, start: u32) { @@ -55,26 +35,18 @@ impl Codegen<'_> { let Some(comments) = self.comments.remove(&start) else { return; }; - let (comments, unused_comments): (Vec<_>, Vec<_>) = - comments.into_iter().partition(|comment| self.is_leading_comments(comment)); - self.print_comments(start, &comments, unused_comments); + let comments = + comments.into_iter().filter(Self::should_keep_leading_comment).collect::>(); + self.print_comments(&comments); } - pub(crate) fn get_statement_comments( - &mut self, - start: u32, - ) -> Option<(Vec, Vec)> { + pub(crate) fn get_statement_comments(&mut self, start: u32) -> Option> { let comments = self.comments.remove(&start)?; let mut leading_comments = vec![]; - let mut unused_comments = vec![]; for comment in comments { - if self.is_leading_comments(&comment) { - leading_comments.push(comment); - continue; - } - if comment.is_legal(self.source_text) { + if comment.is_legal() { match &self.options.legal_comments { LegalComment::None if self.options.comments => { leading_comments.push(comment); @@ -91,33 +63,28 @@ impl Codegen<'_> { LegalComment::None => {} } } - unused_comments.push(comment); + if Self::should_keep_leading_comment(&comment) { + leading_comments.push(comment); + continue; + } } - Some((leading_comments, unused_comments)) + Some(leading_comments) } /// A statement comment also includes legal comments #[inline] pub(crate) fn print_statement_comments(&mut self, start: u32) { - if !self.print_comments { - return; - } - if let Some((comments, unused)) = self.get_statement_comments(start) { - self.print_comments(start, &comments, unused); + if self.print_comments { + if let Some(comments) = self.get_statement_comments(start) { + self.print_comments(&comments); + } } } pub(crate) fn print_expr_comments(&mut self, start: u32) -> bool { let Some(comments) = self.comments.remove(&start) else { return false }; - let (annotation_comments, comments): (Vec<_>, Vec<_>) = - comments.into_iter().partition(|comment| self.is_pure_comment(comment)); - - if !annotation_comments.is_empty() { - self.comments.insert(start, annotation_comments); - } - for comment in &comments { self.print_hard_newline(); self.print_indent(); @@ -132,12 +99,7 @@ impl Codegen<'_> { } } - pub(crate) fn print_comments( - &mut self, - start: u32, - comments: &[Comment], - unused_comments: Vec, - ) { + pub(crate) fn print_comments(&mut self, comments: &[Comment]) { for (i, comment) in comments.iter().enumerate() { if i == 0 { if comment.preceded_by_newline { @@ -160,7 +122,7 @@ impl Codegen<'_> { if comment.preceded_by_newline { self.print_hard_newline(); self.print_indent(); - } else if comment.is_legal(self.source_text) { + } else if comment.is_legal() { self.print_hard_newline(); } } @@ -173,10 +135,6 @@ impl Codegen<'_> { } } } - - if !unused_comments.is_empty() { - self.comments.insert(start, unused_comments); - } } fn print_comment(&mut self, comment: &Comment) { diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index 5a743258f5563..128450e39f151 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -801,7 +801,7 @@ impl Gen for FunctionBody<'_> { None }; let is_empty = if self.is_empty() { - comments_at_end.is_none() || comments_at_end.as_ref().is_some_and(|c| c.0.is_empty()) + comments_at_end.is_none() || comments_at_end.as_ref().is_some_and(Vec::is_empty) } else { false }; @@ -814,8 +814,8 @@ impl Gen for FunctionBody<'_> { stmt.print(p, ctx); } // Print trailing statement comments. - if let Some((comments, unused)) = comments_at_end { - p.print_comments(span_end - 1, &comments, unused); + if let Some(comments) = comments_at_end { + p.print_comments(&comments); } }); p.needs_semicolon = false; diff --git a/crates/oxc_codegen/src/lib.rs b/crates/oxc_codegen/src/lib.rs index 012da904afe5c..8611ca551a476 100644 --- a/crates/oxc_codegen/src/lib.rs +++ b/crates/oxc_codegen/src/lib.rs @@ -482,13 +482,17 @@ impl<'a> Codegen<'a> { if index != 0 { self.print_comma(); } - if self.print_comments && self.has_non_annotation_comment(item.span().start) { - self.print_expr_comments(item.span().start); - self.print_indent(); - } else { - self.print_soft_newline(); - self.print_indent(); + if self.print_comments { + let start = item.span().start; + if self.has_comment(start) { + self.print_expr_comments(start); + self.print_indent(); + item.print(self, ctx); + continue; + } } + self.print_soft_newline(); + self.print_indent(); item.print(self, ctx); } } diff --git a/crates/oxc_codegen/tests/integration/snapshots/pure_comments.snap b/crates/oxc_codegen/tests/integration/snapshots/pure_comments.snap index b1b4cb07ab829..210b0dbf4bad3 100644 --- a/crates/oxc_codegen/tests/integration/snapshots/pure_comments.snap +++ b/crates/oxc_codegen/tests/integration/snapshots/pure_comments.snap @@ -224,9 +224,7 @@ const builtInSymbols = new Set( ) ---------- -const builtInSymbols = new Set( - /* @__PURE__ */ Object.getOwnPropertyNames(Symbol).filter((key) => key !== 'arguments' && key !== 'caller') -); +const builtInSymbols = new Set(/* @__PURE__ */ Object.getOwnPropertyNames(Symbol).filter((key) => key !== 'arguments' && key !== 'caller')); ########## 14 (/* @__PURE__ */ new Foo()).bar(); diff --git a/crates/oxc_parser/src/lexer/trivia_builder.rs b/crates/oxc_parser/src/lexer/trivia_builder.rs index b6f55cc5290c3..d4f7b13aa670a 100644 --- a/crates/oxc_parser/src/lexer/trivia_builder.rs +++ b/crates/oxc_parser/src/lexer/trivia_builder.rs @@ -1,4 +1,4 @@ -use oxc_ast::ast::{Comment, CommentKind, CommentPosition}; +use oxc_ast::ast::{Comment, CommentAnnotation, CommentKind, CommentPosition}; use oxc_span::Span; use super::{Kind, Token}; @@ -119,8 +119,8 @@ impl TriviaBuilder { !self.saw_newline && !matches!(self.previous_kind, Kind::Eq | Kind::LParen) } - fn add_comment(&mut self, comment: Comment, source_text: &str) { - self.parse_pure_comment(comment, source_text); + fn add_comment(&mut self, mut comment: Comment, source_text: &str) { + self.parse_annotation(&mut comment, source_text); // The comments array is an ordered vec, only add the comment if its not added before, // to avoid situations where the parser needs to rewind and tries to reinsert the comment. if let Some(last_comment) = self.comments.last() { @@ -129,7 +129,6 @@ impl TriviaBuilder { } } - let mut comment = comment; // This newly added comment may be preceded by a newline. comment.preceded_by_newline = self.saw_newline; if comment.is_line() { @@ -144,22 +143,54 @@ impl TriviaBuilder { self.comments.push(comment); } - /// Parse `#__PURE__` and `#__NO_SIDE_EFFECTS__` Notation - /// - /// - #[inline] // inline because code path is hot. - fn parse_pure_comment(&mut self, comment: Comment, source_text: &str) { - let Some(s) = source_text[(comment.span.start + 2) as usize..] - .trim_ascii_start() - .strip_prefix(['@', '#']) - else { + /// Parse Notation + fn parse_annotation(&mut self, comment: &mut Comment, source_text: &str) { + let mut s = comment.content_span().source_text(source_text); + + if s.starts_with('!') { + comment.annotation = CommentAnnotation::Legal; + return; + } + + if comment.is_block() && s.starts_with('*') { + // Ignore webpack comment `/*****/` + if !s.bytes().all(|c| c == b'*') { + comment.annotation = CommentAnnotation::Jsdoc; + return; + } + } + + s = s.trim_ascii_start(); + + if let Some(ss) = s.strip_prefix('@') { + if ss.starts_with("vite") { + comment.annotation = CommentAnnotation::Vite; + return; + } + if ss.starts_with("license") || ss.starts_with("preserve") { + comment.annotation = CommentAnnotation::Legal; + return; + } + s = ss; + } else if let Some(ss) = s.strip_prefix('#') { + s = ss; + } else if s.starts_with("webpack") { + comment.annotation = CommentAnnotation::Webpack; + return; + } else { + if s.contains("@license") || s.contains("@preserve") { + comment.annotation = CommentAnnotation::Legal; + } return; - }; + } + let Some(s) = s.strip_prefix("__") else { return }; if s.starts_with("PURE__") { + comment.annotation = CommentAnnotation::Pure; self.has_pure_comment = true; } if s.starts_with("NO_SIDE_EFFECTS__") { + comment.annotation = CommentAnnotation::NoSideEffects; self.has_no_side_effects_comment = true; } } @@ -168,7 +199,7 @@ impl TriviaBuilder { #[cfg(test)] mod test { use oxc_allocator::Allocator; - use oxc_ast::{Comment, CommentKind, CommentPosition}; + use oxc_ast::{Comment, CommentAnnotation, CommentKind, CommentPosition}; use oxc_span::{SourceType, Span}; use crate::Parser; @@ -197,6 +228,7 @@ mod test { attached_to: 70, preceded_by_newline: true, followed_by_newline: true, + annotation: CommentAnnotation::None, }, Comment { span: Span::new(33, 45), @@ -205,6 +237,7 @@ mod test { attached_to: 70, preceded_by_newline: true, followed_by_newline: true, + annotation: CommentAnnotation::None, }, Comment { span: Span::new(54, 69), @@ -213,6 +246,7 @@ mod test { attached_to: 70, preceded_by_newline: true, followed_by_newline: false, + annotation: CommentAnnotation::None, }, Comment { span: Span::new(76, 92), @@ -221,6 +255,7 @@ mod test { attached_to: 0, preceded_by_newline: false, followed_by_newline: false, + annotation: CommentAnnotation::None, }, Comment { span: Span::new(93, 106), @@ -229,6 +264,7 @@ mod test { attached_to: 0, preceded_by_newline: false, followed_by_newline: true, + annotation: CommentAnnotation::None, }, Comment { span: Span::new(115, 138), @@ -237,6 +273,7 @@ mod test { attached_to: 147, preceded_by_newline: true, followed_by_newline: true, + annotation: CommentAnnotation::None, }, ]; @@ -261,6 +298,7 @@ token /* Trailing 1 */ attached_to: 36, preceded_by_newline: true, followed_by_newline: true, + annotation: CommentAnnotation::None, }, Comment { span: Span::new(42, 58), @@ -269,6 +307,7 @@ token /* Trailing 1 */ attached_to: 0, preceded_by_newline: false, followed_by_newline: true, + annotation: CommentAnnotation::None, }, ]; assert_eq!(comments, expected); @@ -277,10 +316,10 @@ token /* Trailing 1 */ #[test] fn comment_attachments3() { let source_text = " -/** +/* * A **/ -/** +/* * B **/ token @@ -288,20 +327,22 @@ token /* Trailing 1 */ let comments = get_comments(source_text); let expected = vec![ Comment { - span: Span::new(1, 14), + span: Span::new(1, 13), kind: CommentKind::Block, position: CommentPosition::Leading, - attached_to: 30, + attached_to: 28, preceded_by_newline: true, followed_by_newline: true, + annotation: CommentAnnotation::None, }, Comment { - span: Span::new(15, 28), + span: Span::new(14, 26), kind: CommentKind::Block, position: CommentPosition::Leading, - attached_to: 30, + attached_to: 28, preceded_by_newline: true, followed_by_newline: true, + annotation: CommentAnnotation::None, }, ]; assert_eq!(comments, expected); @@ -325,6 +366,7 @@ token /* Trailing 1 */ attached_to: 57, preceded_by_newline: false, followed_by_newline: true, + annotation: CommentAnnotation::None, }, Comment { span: Span::new(96, 116), @@ -333,6 +375,7 @@ token /* Trailing 1 */ attached_to: 129, preceded_by_newline: false, followed_by_newline: true, + annotation: CommentAnnotation::None, }, ]; assert_eq!(comments, expected); @@ -355,6 +398,7 @@ token /* Trailing 1 */ attached_to: 55, preceded_by_newline: false, followed_by_newline: true, + annotation: CommentAnnotation::None, }, Comment { span: Span::new(79, 99), @@ -363,8 +407,37 @@ token /* Trailing 1 */ attached_to: 116, preceded_by_newline: false, followed_by_newline: true, + annotation: CommentAnnotation::None, }, ]; assert_eq!(comments, expected); } + + #[test] + fn comment_parsing() { + let data = [ + ("/*! legal */", CommentAnnotation::Legal), + ("/* @preserve */", CommentAnnotation::Legal), + ("/* @license */", CommentAnnotation::Legal), + ("/* foo @preserve */", CommentAnnotation::Legal), + ("/* foo @license */", CommentAnnotation::Legal), + ("/** jsdoc */", CommentAnnotation::Jsdoc), + ("/**/", CommentAnnotation::None), + ("/***/", CommentAnnotation::None), + ("/****/", CommentAnnotation::None), + ("/* @vite-ignore */", CommentAnnotation::Vite), + ("/* @vite-xxx */", CommentAnnotation::Vite), + ("/* webpackChunkName: 'my-chunk-name' */", CommentAnnotation::Webpack), + ("/* @__PURE__ */", CommentAnnotation::Pure), + ("/* @__NO_SIDE_EFFECTS__ */", CommentAnnotation::NoSideEffects), + ("/* #__PURE__ */", CommentAnnotation::Pure), + ("/* #__NO_SIDE_EFFECTS__ */", CommentAnnotation::NoSideEffects), + ]; + + for (source_text, expected) in data { + let comments = get_comments(source_text); + assert_eq!(comments.len(), 1, "{source_text}"); + assert_eq!(comments[0].annotation, expected, "{source_text}"); + } + } } diff --git a/crates/oxc_semantic/src/jsdoc/builder.rs b/crates/oxc_semantic/src/jsdoc/builder.rs index c7c45c915d546..f7c66795ac9d5 100644 --- a/crates/oxc_semantic/src/jsdoc/builder.rs +++ b/crates/oxc_semantic/src/jsdoc/builder.rs @@ -16,7 +16,7 @@ pub struct JSDocBuilder<'a> { impl<'a> JSDocBuilder<'a> { pub fn new(source_text: &'a str, comments: &[Comment]) -> Self { let mut not_attached_docs: FxHashMap> = FxHashMap::default(); - for comment in comments.iter().filter(|comment| comment.is_jsdoc(source_text)) { + for comment in comments.iter().filter(|comment| comment.is_jsdoc()) { not_attached_docs .entry(comment.attached_to) .or_default() diff --git a/crates/oxc_semantic/src/jsdoc/parser/jsdoc.rs b/crates/oxc_semantic/src/jsdoc/parser/jsdoc.rs index 249b93608314f..17d6818a7bef8 100644 --- a/crates/oxc_semantic/src/jsdoc/parser/jsdoc.rs +++ b/crates/oxc_semantic/src/jsdoc/parser/jsdoc.rs @@ -98,9 +98,6 @@ line2 3, ), (" /** * list */ ", "* list", " * list ", 0), - ("/***/", "", "", 0), - ("/****/", "*", "*", 0), - ("/*****/", "**", "**", 0), ( " /** diff --git a/crates/oxc_transformer/src/jsx/comments.rs b/crates/oxc_transformer/src/jsx/comments.rs index 51b97d92918f8..9e68459778d83 100644 --- a/crates/oxc_transformer/src/jsx/comments.rs +++ b/crates/oxc_transformer/src/jsx/comments.rs @@ -327,7 +327,7 @@ pub fn cold_branch T, T>(f: F) -> T { #[cfg(test)] mod tests { - use oxc_ast::CommentPosition; + use oxc_ast::{CommentAnnotation, CommentPosition}; use oxc_span::Span; use super::*; @@ -367,6 +367,7 @@ mod tests { attached_to: 0, preceded_by_newline: true, followed_by_newline: true, + annotation: CommentAnnotation::None, }; (comment, source_text) } diff --git a/crates/oxc_transformer/src/plugins/module_runner_transform.rs b/crates/oxc_transformer/src/plugins/module_runner_transform.rs index d85ece64dcf37..b25e59f4d71cd 100644 --- a/crates/oxc_transformer/src/plugins/module_runner_transform.rs +++ b/crates/oxc_transformer/src/plugins/module_runner_transform.rs @@ -1996,12 +1996,7 @@ console.log(bar);", let expected = " - const __vite_ssr_import_0__ = await __vite_ssr_import__( - './f', - /*;;*/ - /*;;*/ - { importedNames: ['f'] } - ); + const __vite_ssr_import_0__ = await __vite_ssr_import__('./f', { importedNames: ['f'] }); let x = 0; diff --git a/tasks/coverage/snapshots/transformer_typescript.snap b/tasks/coverage/snapshots/transformer_typescript.snap index 0928eb9ef7287..a669bc50dacfe 100644 --- a/tasks/coverage/snapshots/transformer_typescript.snap +++ b/tasks/coverage/snapshots/transformer_typescript.snap @@ -2,8 +2,7 @@ commit: 15392346 transformer_typescript Summary: AST Parsed : 6531/6531 (100.00%) -Positive Passed: 6527/6531 (99.94%) -Mismatch: tasks/coverage/typescript/tests/cases/compiler/commentsAfterSpread.ts +Positive Passed: 6528/6531 (99.95%) Mismatch: tasks/coverage/typescript/tests/cases/conformance/classes/propertyMemberDeclarations/autoAccessor2.ts Mismatch: tasks/coverage/typescript/tests/cases/conformance/esDecorators/classDeclaration/fields/esDecorators-classDeclaration-fields-staticPrivateAccessor.ts Mismatch: tasks/coverage/typescript/tests/cases/conformance/jsx/inline/inlineJsxAndJsxFragPragmaOverridesCompilerOptions.tsx