Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion crates/oxc_ast/src/ast/comment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,10 @@ impl Comment {
/// Is comment with special meaning.
#[inline]
pub fn is_annotation(self) -> bool {
self.content != CommentContent::None && self.content != CommentContent::Legal
self.content != CommentContent::None
&& self.content != CommentContent::Legal
&& self.content != CommentContent::Jsdoc
&& self.content != CommentContent::JsdocLegal
}

/// Returns `true` if this comment is a JSDoc comment. Implies `is_leading` and `is_block`.
Expand Down
12 changes: 6 additions & 6 deletions crates/oxc_codegen/src/comment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,13 @@ use std::borrow::Cow;
use oxc_ast::{Comment, CommentKind, ast::Program};
use oxc_syntax::identifier::is_line_terminator;

use crate::{Codegen, LegalComment};
use crate::{Codegen, LegalComment, options::CommentOptions};

pub type CommentsMap = FxHashMap</* attached_to */ u32, Vec<Comment>>;

impl Codegen<'_> {
pub(crate) fn build_comments(&mut self, comments: &[Comment]) {
if !self.options.comments
&& self.options.legal_comments.is_none()
&& !self.options.annotation_comments
{
if self.options.comments == CommentOptions::disabled() {
return;
}
for comment in comments {
Expand All @@ -26,6 +23,9 @@ impl Codegen<'_> {
if comment.is_legal() && self.options.print_legal_comment() {
add = true;
}
if comment.is_jsdoc() && self.options.print_jsdoc_comment() {
add = true;
}
if comment.is_annotation() && self.options.print_annotation_comment() {
add = true;
}
Expand Down Expand Up @@ -151,7 +151,7 @@ impl Codegen<'_> {
&mut self,
program: &Program<'_>,
) -> Vec<Comment> {
let legal_comments = &self.options.legal_comments;
let legal_comments = &self.options.comments.legal;
if matches!(legal_comments, LegalComment::None | LegalComment::Inline) {
return vec![];
}
Expand Down
7 changes: 4 additions & 3 deletions crates/oxc_codegen/src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2032,9 +2032,10 @@ impl GenExpr for ImportExpression<'_> {
fn gen_expr(&self, p: &mut Codegen, precedence: Precedence, ctx: Context) {
let wrap = precedence >= Precedence::New || ctx.intersects(Context::FORBID_CALL);

let has_comment_before_right_paren =
p.options.comments && self.span.end > 0 && p.has_comment(self.span.end - 1);
let has_comment = p.options.comments
let has_comment_before_right_paren = p.options.print_annotation_comment()
&& self.span.end > 0
&& p.has_comment(self.span.end - 1);
let has_comment = p.options.print_annotation_comment()
&& (has_comment_before_right_paren
|| p.has_comment(self.source.span().start)
|| self
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use crate::{
pub use crate::{
context::Context,
r#gen::{Gen, GenExpr},
options::{CodegenOptions, LegalComment},
options::{CodegenOptions, CommentOptions, LegalComment},
};

/// Output from [`Codegen::build`]
Expand Down
109 changes: 64 additions & 45 deletions crates/oxc_codegen/src/options.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::path::PathBuf;

/// Codegen Options.
#[derive(Debug, Clone)]
#[derive(Debug, Default, Clone)]
pub struct CodegenOptions {
/// Use single quotes instead of double quotes.
///
Expand All @@ -13,33 +13,12 @@ pub struct CodegenOptions {
/// Default is `false`.
pub minify: bool,

/// Print normal comments?
/// Print comments?
///
/// At present, only some leading comments are preserved.
///
/// Does not control legal and annotation comments.
///
/// Default is `true`.
pub comments: bool,

/// Print annotation comments.
///
/// * jsdoc: `/** jsdoc */`
/// * pure: `/* #__PURE__ */` and `/* #__NO_SIDE_EFFECTS__ */`
/// * webpack: `/* webpackChunkName */`
/// * vite: `/* @vite-ignore */`
/// * coverage: `v8 ignore`, `c8 ignore`, `node:coverage`, `istanbul ignore`
///
/// Default is `true`.
pub annotation_comments: bool,

/// Print legal comments.
///
/// * starts with `//!` or `/*!`.
/// * contains `/* @license */` or `/* @preserve */`
///
/// Default is [LegalComment::Inline].
pub legal_comments: LegalComment,
/// Default is [CodegenOptions::default].
pub comments: CommentOptions,

/// Enable sourcemap.
///
Expand All @@ -49,56 +28,96 @@ pub struct CodegenOptions {
pub source_map_path: Option<PathBuf>,
}

impl Default for CodegenOptions {
fn default() -> Self {
Self {
single_quote: false,
minify: false,
comments: true,
annotation_comments: true,
legal_comments: LegalComment::Inline,
source_map_path: None,
}
}
}

impl CodegenOptions {
/// Minify whitespace and remove comments.
pub fn minify() -> Self {
Self {
single_quote: false,
minify: true,
comments: false,
annotation_comments: false,
legal_comments: LegalComment::None,
comments: CommentOptions::disabled(),
source_map_path: None,
}
}

#[inline]
pub(crate) fn print_normal_comment(&self) -> bool {
self.comments
self.comments.normal
}

#[inline]
pub(crate) fn print_legal_comment(&self) -> bool {
self.legal_comments.is_inline()
self.comments.legal.is_inline()
}

#[inline]
pub(crate) fn print_jsdoc_comment(&self) -> bool {
self.comments.jsdoc
}

#[inline]
pub(crate) fn print_annotation_comment(&self) -> bool {
self.annotation_comments
self.comments.annotation
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
/// Comment Options
pub struct CommentOptions {
/// Print normal comments that do not have special meanings.
///
/// At present only statement level comments are printed.
///
/// Default is `true`.
pub normal: bool,

/// Print jsdoc comments.
///
/// * jsdoc: `/** jsdoc */`
///
/// Default is `true`.
pub jsdoc: bool,

/// Print annotation comments.
///
/// * pure: `/* #__PURE__ */` and `/* #__NO_SIDE_EFFECTS__ */`
/// * webpack: `/* webpackChunkName */`
/// * vite: `/* @vite-ignore */`
/// * coverage: `v8 ignore`, `c8 ignore`, `node:coverage`, `istanbul ignore`
///
/// Default is `true`.
pub annotation: bool,

/// Print legal comments.
///
/// * starts with `//!` or `/*!`.
/// * contains `/* @license */` or `/* @preserve */`
///
/// Default is [LegalComment::Inline].
pub legal: LegalComment,
}

impl Default for CommentOptions {
fn default() -> Self {
Self { normal: true, jsdoc: true, annotation: true, legal: LegalComment::default() }
}
}

impl CommentOptions {
/// Disable Comments.
pub fn disabled() -> Self {
Self { normal: false, jsdoc: false, annotation: false, legal: LegalComment::None }
}
}

/// Legal comment
///
/// <https://esbuild.github.io/api/#legal-comments>
#[derive(Debug, Clone, Eq, PartialEq)]
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub enum LegalComment {
/// Do not preserve any legal comments.
None,
/// Preserve all legal comments (default).
#[default]
Inline,
/// Move all legal comments to the end of the file.
Eof,
Expand Down
100 changes: 58 additions & 42 deletions crates/oxc_codegen/tests/integration/comments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ catch(e) {
}

pub mod legal {
use oxc_codegen::{CodegenOptions, LegalComment};
use oxc_codegen::{CodegenOptions, CommentOptions, LegalComment};

use crate::{codegen_options, snapshot, snapshot_options};

Expand Down Expand Up @@ -217,16 +217,18 @@ pub mod legal {

#[test]
fn legal_eof_comment() {
let options =
CodegenOptions { legal_comments: LegalComment::Eof, ..CodegenOptions::default() };
let options = CodegenOptions {
comments: CommentOptions { legal: LegalComment::Eof, ..CommentOptions::default() },
..CodegenOptions::default()
};
snapshot_options("legal_eof_comments", &cases(), &options);
}

#[test]
fn legal_eof_minify_comment() {
let options = CodegenOptions {
minify: true,
legal_comments: LegalComment::Eof,
comments: CommentOptions { legal: LegalComment::Eof, ..CommentOptions::default() },
..CodegenOptions::default()
};
snapshot_options("legal_eof_minify_comments", &cases(), &options);
Expand All @@ -235,16 +237,21 @@ pub mod legal {
#[test]
fn legal_linked_comment() {
let options = CodegenOptions {
legal_comments: LegalComment::Linked(String::from("test.js")),
comments: CommentOptions {
legal: LegalComment::Linked(String::from("test.js")),
..CommentOptions::default()
},
..CodegenOptions::default()
};
snapshot_options("legal_linked_comments", &cases(), &options);
}

#[test]
fn legal_external_comment() {
let options =
CodegenOptions { legal_comments: LegalComment::External, ..CodegenOptions::default() };
let options = CodegenOptions {
comments: CommentOptions { legal: LegalComment::External, ..CommentOptions::default() },
..CodegenOptions::default()
};
let code = "/* @license */\n/* @preserve */\nfoo;\n";
let ret = codegen_options(code, &options);
assert_eq!(ret.code, "foo;\n");
Expand Down Expand Up @@ -473,7 +480,7 @@ delete /* @__PURE__ */ (() => {})();",
}

pub mod options {
use oxc_codegen::{CodegenOptions, LegalComment};
use oxc_codegen::{CodegenOptions, CommentOptions, LegalComment};

use crate::codegen_options;

Expand All @@ -490,42 +497,51 @@ function foo() {
//! Function Legal Comment
}
x(/* Normal Comment */);
x(/** Call Expression Annotation Comment */ token);
x(/** Call Expression Jsdoc Comment */ token);
}";

for comments in [true, false] {
for annotation in [true, false] {
for legal in [LegalComment::Inline, LegalComment::Eof, LegalComment::None] {
let options = CodegenOptions {
comments,
annotation_comments: annotation,
legal_comments: legal.clone(),
..CodegenOptions::default()
};
let printed = codegen_options(code, &options).code;

if comments {
assert!(printed.contains("Normal Comment"));
} else {
assert!(!printed.contains("Normal Comment"));
}

if annotation {
assert!(printed.contains("JSDoc Comment"));
assert!(printed.contains("__PURE__"));
assert!(printed.contains("Call Expression Annotation Comment"));
} else {
assert!(!printed.contains("JSDoc Comment"));
assert!(!printed.contains("__PURE__"));
assert!(!printed.contains("Call Expression Annotation Comment"));
}

if legal.is_none() {
assert!(!printed.contains("Top Legal Comment"));
assert!(!printed.contains("Function Legal Comment"));
} else {
assert!(printed.contains("Top Legal Comment"));
assert!(printed.contains("Function Legal Comment"));
for normal in [true, false] {
for jsdoc in [true, false] {
for annotation in [true, false] {
for legal in [LegalComment::Inline, LegalComment::Eof, LegalComment::None] {
let options = CodegenOptions {
comments: CommentOptions {
normal,
jsdoc,
annotation,
legal: legal.clone(),
},
..CodegenOptions::default()
};
let printed = codegen_options(code, &options).code;

if normal {
assert!(printed.contains("Normal Comment"));
} else {
assert!(!printed.contains("Normal Comment"));
}

if jsdoc {
assert!(printed.contains("JSDoc Comment"));
assert!(printed.contains("Call Expression Jsdoc Comment"));
} else {
assert!(!printed.contains("JSDoc Comment"));
assert!(!printed.contains("Call Expression Jsdoc Comment"));
}

if annotation {
assert!(printed.contains("__PURE__"));
} else {
assert!(!printed.contains("__PURE__"));
}

if legal.is_none() {
assert!(!printed.contains("Top Legal Comment"));
assert!(!printed.contains("Function Legal Comment"));
} else {
assert!(printed.contains("Top Legal Comment"));
assert!(printed.contains("Function Legal Comment"));
}
}
}
}
Expand Down
Loading
Loading