Skip to content

Commit

Permalink
feat(codegen): print vite / webpack special comments (#6021)
Browse files Browse the repository at this point in the history
Related: #1046 (comment)
Close: #6024
  • Loading branch information
Dunqing committed Sep 26, 2024
1 parent efabfc8 commit cca433f
Show file tree
Hide file tree
Showing 6 changed files with 260 additions and 82 deletions.
92 changes: 68 additions & 24 deletions crates/oxc_codegen/src/comment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,54 @@ impl<'a> Codegen<'a> {
}
}

pub fn has_annotation_comments(&self, start: u32) -> bool {
pub fn has_comment(&self, start: u32) -> bool {
self.comments.contains_key(&start)
}

pub fn has_annotation_comment(&self, start: u32) -> bool {
let Some(source_text) = self.source_text else { return false };
self.comments.get(&start).is_some_and(|comments| {
comments.iter().any(|comment| Self::is_annotation_comments(comment, source_text))
comments.iter().any(|comment| Self::is_annotation_comment(comment, source_text))
})
}

pub fn has_non_annotation_comment(&self, start: u32) -> bool {
let Some(source_text) = self.source_text else { return false };
self.comments.get(&start).is_some_and(|comments| {
comments.iter().any(|comment| !Self::is_annotation_comment(comment, source_text))
})
}

/// Weather to keep leading comments.
fn is_leading_comments(comment: &Comment, source_text: &str) -> bool {
(comment.is_jsdoc(source_text) || (comment.is_line() && Self::is_annotation_comments(comment, source_text)))
(comment.is_jsdoc(source_text) || (comment.is_line() && Self::is_annotation_comment(comment, source_text)))
&& comment.preceded_by_newline
// webpack comment `/*****/`
&& !comment.span.source_text(source_text).chars().all(|c| c == '*')
}

fn print_comment(&mut self, comment: &Comment, source_text: &str) {
let comment_source = comment.real_span().source_text(source_text);
match comment.kind {
CommentKind::Line => {
self.print_str(comment_source);
}
CommentKind::Block => {
// Print block comments with our own indentation.
let lines = comment_source.split(is_line_terminator);
for line in lines {
if !line.starts_with("/*") {
self.print_indent();
}
self.print_str(line.trim_start());
if !line.ends_with("*/") {
self.print_hard_newline();
}
}
}
}
}

pub(crate) fn print_leading_comments(&mut self, start: u32) {
if self.options.minify {
return;
Expand Down Expand Up @@ -68,25 +101,7 @@ impl<'a> Codegen<'a> {
self.print_indent();
}

let comment_source = comment.real_span().source_text(source_text);
match comment.kind {
CommentKind::Line => {
self.print_str(comment_source);
}
CommentKind::Block => {
// Print block comments with our own indentation.
let lines = comment_source.split(is_line_terminator);
for line in lines {
if !line.starts_with("/*") {
self.print_indent();
}
self.print_str(line.trim_start());
if !line.ends_with("*/") {
self.print_hard_newline();
}
}
}
}
self.print_comment(comment, source_text);
}

if comments.last().is_some_and(|c| c.is_line() || c.followed_by_newline) {
Expand All @@ -99,7 +114,7 @@ impl<'a> Codegen<'a> {
}
}

fn is_annotation_comments(comment: &Comment, source_text: &str) -> bool {
fn is_annotation_comment(comment: &Comment, source_text: &str) -> bool {
let comment_content = comment.span.source_text(source_text);
ANNOTATION_MATCHER.find_iter(comment_content).count() != 0
}
Expand All @@ -116,11 +131,40 @@ impl<'a> Codegen<'a> {
let Some(comments) = self.comments.remove(&start) else { return };

for comment in comments {
if !Self::is_annotation_comments(&comment, source_text) {
if !Self::is_annotation_comment(&comment, source_text) {
continue;
}
self.print_str(comment.real_span().source_text(source_text));
self.print_hard_space();
}
}

pub(crate) fn print_expr_comments(&mut self, start: u32) -> bool {
if self.options.minify {
return false;
}
let Some(source_text) = self.source_text else { return false };
let Some(comments) = self.comments.remove(&start) else { return false };

let (annotation_comments, comments): (Vec<_>, Vec<_>) = comments
.into_iter()
.partition(|comment| Self::is_annotation_comment(comment, source_text));

if !annotation_comments.is_empty() {
self.comments.insert(start, annotation_comments);
}

for comment in &comments {
self.print_hard_newline();
self.print_indent();
self.print_comment(comment, source_text);
}

if comments.is_empty() {
false
} else {
self.print_hard_newline();
true
}
}
}
62 changes: 56 additions & 6 deletions crates/oxc_codegen/src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ impl<'a> Gen for VariableDeclaration<'a> {
&& p.start_of_annotation_comment.is_none()
&& matches!(self.kind, VariableDeclarationKind::Const)
&& matches!(self.declarations.first(), Some(VariableDeclarator { init: Some(init), .. }) if init.is_function())
&& p.has_annotation_comments(self.span.start)
&& p.has_annotation_comment(self.span.start)
{
p.start_of_annotation_comment = Some(self.span.start);
}
Expand Down Expand Up @@ -834,7 +834,7 @@ impl<'a> Gen for ExportNamedDeclaration<'a> {
if matches!(var_decl.kind, VariableDeclarationKind::Const) =>
{
if matches!(var_decl.declarations.first(), Some(VariableDeclarator { init: Some(init), .. }) if init.is_function())
&& p.has_annotation_comments(self.span.start)
&& p.has_annotation_comment(self.span.start)
{
p.start_of_annotation_comment = Some(self.span.start);
}
Expand Down Expand Up @@ -1368,7 +1368,7 @@ impl<'a> GenExpr for CallExpression<'a> {
fn gen_expr(&self, p: &mut Codegen, precedence: Precedence, ctx: Context) {
let is_export_default = p.start_of_default_export == p.code_len();
let mut wrap = precedence >= Precedence::New || ctx.intersects(Context::FORBID_CALL);
if p.has_annotation_comments(self.span.start) && precedence >= Precedence::Postfix {
if p.has_annotation_comment(self.span.start) && precedence >= Precedence::Postfix {
wrap = true;
}

Expand All @@ -1386,7 +1386,19 @@ impl<'a> GenExpr for CallExpression<'a> {
type_parameters.print(p, ctx);
}
p.print_char(b'(');
p.print_list(&self.arguments, ctx);
let has_comment = (self.span.end > 0 && p.has_comment(self.span.end - 1))
|| self.arguments.iter().any(|item| p.has_comment(item.span().start));
if has_comment {
p.indent();
p.print_list_with_comments(&self.arguments, ctx);
// Handle `/* comment */);`
if !p.print_expr_comments(self.span.end - 1) {
p.print_soft_newline();
}
p.dedent();
} else {
p.print_list(&self.arguments, ctx);
}
p.print_char(b')');
p.add_source_mapping(self.span.end);
});
Expand Down Expand Up @@ -1949,14 +1961,40 @@ impl<'a> GenExpr for SequenceExpression<'a> {
impl<'a> GenExpr for ImportExpression<'a> {
fn gen_expr(&self, p: &mut Codegen, precedence: Precedence, ctx: Context) {
let wrap = precedence >= Precedence::New || ctx.intersects(Context::FORBID_CALL);
let has_comment = (self.span.end > 0 && p.has_comment(self.span.end - 1))
|| p.has_comment(self.source.span().start)
|| self.arguments.first().is_some_and(|argument| p.has_comment(argument.span().start));

p.wrap(wrap, |p| {
p.add_source_mapping(self.span.start);
p.print_str("import(");
if has_comment {
p.indent();
}
if p.print_expr_comments(self.source.span().start) {
p.print_indent();
} else if has_comment {
p.print_soft_newline();
p.print_indent();
}
self.source.print_expr(p, Precedence::Comma, Context::empty());
if !self.arguments.is_empty() {
p.print_comma();
if has_comment {
p.print_soft_newline();
p.print_indent();
} else {
p.print_soft_space();
}
p.print_expressions(&self.arguments, Precedence::Comma, Context::empty());
}
if has_comment {
// Handle `/* comment */);`
if !p.print_expr_comments(self.span.end - 1) {
p.print_soft_newline();
}
p.dedent();
}
p.print_char(b')');
});
}
Expand Down Expand Up @@ -2024,7 +2062,7 @@ impl<'a> GenExpr for ChainExpression<'a> {
impl<'a> GenExpr for NewExpression<'a> {
fn gen_expr(&self, p: &mut Codegen, precedence: Precedence, ctx: Context) {
let mut wrap = precedence >= self.precedence();
if p.has_annotation_comments(self.span.start) && precedence >= Precedence::Postfix {
if p.has_annotation_comment(self.span.start) && precedence >= Precedence::Postfix {
wrap = true;
}
p.wrap(wrap, |p| {
Expand All @@ -2034,7 +2072,19 @@ impl<'a> GenExpr for NewExpression<'a> {
p.print_str("new ");
self.callee.print_expr(p, Precedence::New, Context::FORBID_CALL);
p.print_char(b'(');
p.print_list(&self.arguments, ctx);
let has_comment = p.has_comment(self.span.end - 1)
|| self.arguments.iter().any(|item| p.has_comment(item.span().start));
if has_comment {
p.indent();
p.print_list_with_comments(&self.arguments, ctx);
// Handle `/* comment */);`
if !p.print_expr_comments(self.span.end - 1) {
p.print_soft_newline();
}
p.dedent();
} else {
p.print_list(&self.arguments, ctx);
}
p.print_char(b')');
});
}
Expand Down
18 changes: 17 additions & 1 deletion crates/oxc_codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use oxc_ast::{
Trivias,
};
use oxc_mangler::Mangler;
use oxc_span::Span;
use oxc_span::{GetSpan, Span};
use oxc_syntax::{
identifier::is_identifier_part,
operator::{BinaryOperator, UnaryOperator, UpdateOperator},
Expand Down Expand Up @@ -468,6 +468,22 @@ impl<'a> Codegen<'a> {
}
}

fn print_list_with_comments<T: Gen + GetSpan>(&mut self, items: &[T], ctx: Context) {
for (index, item) in items.iter().enumerate() {
if index != 0 {
self.print_comma();
}
if 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();
}
item.print(self, ctx);
}
}

fn print_expressions<T: GenExpr>(&mut self, items: &[T], precedence: Precedence, ctx: Context) {
for (index, item) in items.iter().enumerate() {
if index != 0 {
Expand Down
Loading

0 comments on commit cca433f

Please sign in to comment.