Skip to content
Closed
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
2 changes: 1 addition & 1 deletion crates/oxc_codegen/examples/sourcemap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fn main() -> std::io::Result<()> {
return Ok(());
}

let CodegenReturn { code, map } = CodeGenerator::new()
let CodegenReturn { code, map, .. } = CodeGenerator::new()
.with_options(CodegenOptions {
source_map_path: Some(path.to_path_buf()),
..CodegenOptions::default()
Expand Down
72 changes: 46 additions & 26 deletions crates/oxc_codegen/src/comment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use rustc_hash::FxHashMap;
use oxc_ast::{Comment, CommentKind};
use oxc_syntax::identifier::is_line_terminator;

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

static ANNOTATION_MATCHER: Lazy<DoubleArrayAhoCorasick<usize>> = Lazy::new(|| {
let patterns = vec!["#__NO_SIDE_EFFECTS__", "@__NO_SIDE_EFFECTS__", "@__PURE__", "#__PURE__"];
Expand Down Expand Up @@ -49,18 +49,7 @@ impl<'a> Codegen<'a> {
ANNOTATION_MATCHER.find_iter(comment_content).count() != 0
}

fn is_legal_comment(&self, comment: &Comment) -> bool {
if self.options.comments {
if self.options.legal_comments.is_inline() || self.options.legal_comments.is_none() {
return comment.is_legal(self.source_text);
}
} else if self.options.legal_comments.is_inline() {
return comment.is_legal(self.source_text);
}
false
}

/// Weather to keep leading comments.
/// Whether to keep leading comments.
fn is_leading_comments(&self, comment: &Comment) -> bool {
comment.preceded_by_newline
&& (comment.is_jsdoc(self.source_text)
Expand Down Expand Up @@ -89,11 +78,36 @@ impl<'a> Codegen<'a> {
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.is_legal_comment(comment)
});
self.print_comments(start, &comments, unused_comments);

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) {
match &self.options.legal_comments {
LegalComment::None if self.options.comments => {
leading_comments.push(comment);
continue;
}
LegalComment::Inline => {
leading_comments.push(comment);
continue;
}
LegalComment::Eof | LegalComment::Linked(_) | LegalComment::External => {
self.legal_comments.push(comment);
continue;
}
LegalComment::None => {}
}
}
unused_comments.push(comment);
}

self.print_comments(start, &leading_comments, unused_comments);
}

pub(crate) fn print_annotation_comments(&mut self, node_start: u32) {
Expand Down Expand Up @@ -148,15 +162,21 @@ impl<'a> Codegen<'a> {
}
}

pub(crate) fn try_print_eof_legal_comments(&mut self, comments: &[Comment]) {
if !self.options.legal_comments.is_eof() {
return;
}
for c in comments {
if c.is_legal(self.source_text) {
self.print_comment(c);
self.print_hard_newline();
pub(crate) fn try_print_eof_legal_comments(&mut self) {
match self.options.legal_comments.clone() {
LegalComment::Eof => {
let comments = self.legal_comments.drain(..).collect::<Vec<_>>();
for c in comments {
self.print_comment(&c);
self.print_hard_newline();
}
}
LegalComment::Linked(path) => {
self.print_str("/*! For license information please see ");
self.print_str(&path);
self.print_str(" */");
}
_ => {}
}
}

Expand Down
35 changes: 21 additions & 14 deletions crates/oxc_codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ mod sourcemap_builder;
use std::borrow::Cow;

use oxc_ast::ast::{
BindingIdentifier, BlockStatement, Expression, IdentifierReference, Program, Statement,
BindingIdentifier, BlockStatement, Comment, Expression, IdentifierReference, Program, Statement,
};
use oxc_mangler::Mangler;
use oxc_span::{GetSpan, Span};
Expand All @@ -40,6 +40,7 @@ pub use crate::{
pub type CodeGenerator<'a> = Codegen<'a>;

/// Output from [`Codegen::build`]
#[non_exhaustive]
pub struct CodegenReturn {
/// The generated source code.
pub code: String,
Expand All @@ -48,6 +49,9 @@ pub struct CodegenReturn {
///
/// You must set [`CodegenOptions::source_map_path`] for this to be [`Some`].
pub map: Option<oxc_sourcemap::SourceMap>,

/// All the legal comments returned from [LegalComment::Linked] or [LegalComment::External].
pub legal_comments: Vec<Comment>,
}

/// A code generator for printing JavaScript and TypeScript code.
Expand All @@ -74,8 +78,6 @@ pub struct Codegen<'a> {
/// Original source code of the AST
source_text: &'a str,

comments: CommentsMap,

mangler: Option<Mangler>,

/// Output Code
Expand All @@ -96,6 +98,17 @@ pub struct Codegen<'a> {
start_of_stmt: usize,
start_of_arrow_expr: usize,
start_of_default_export: usize,

/// Track the current indentation level
indent: u32,

/// Fast path for [CodegenOptions::single_quote]
quote: u8,

// Builders
comments: CommentsMap,

legal_comments: Vec<Comment>,
/// Start of comment that needs to be moved to the before VariableDeclarator
///
/// For example:
Expand All @@ -110,13 +123,6 @@ pub struct Codegen<'a> {
/// ```
start_of_annotation_comment: Option<u32>,

/// Track the current indentation level
indent: u32,

/// Fast path for [CodegenOptions::single_quote]
quote: u8,

// Builders
sourcemap_builder: Option<SourcemapBuilder>,
}

Expand Down Expand Up @@ -148,8 +154,6 @@ impl<'a> Codegen<'a> {
Self {
options: CodegenOptions::default(),
source_text: "",
comments: CommentsMap::default(),
start_of_annotation_comment: None,
mangler: None,
code: CodeBuffer::default(),
needs_semicolon: false,
Expand All @@ -164,6 +168,9 @@ impl<'a> Codegen<'a> {
start_of_default_export: 0,
indent: 0,
quote: b'"',
comments: CommentsMap::default(),
start_of_annotation_comment: None,
legal_comments: vec![],
sourcemap_builder: None,
}
}
Expand Down Expand Up @@ -198,10 +205,10 @@ impl<'a> Codegen<'a> {
self.sourcemap_builder = Some(SourcemapBuilder::new(path, program.source_text));
}
program.print(&mut self, Context::default());
self.try_print_eof_legal_comments(&program.comments);
self.try_print_eof_legal_comments();
let code = self.code.into_string();
let map = self.sourcemap_builder.map(SourcemapBuilder::into_sourcemap);
CodegenReturn { code, map }
CodegenReturn { code, map, legal_comments: self.legal_comments }
}

/// Turn what's been built so far into a string. Like [`build`],
Expand Down
18 changes: 9 additions & 9 deletions crates/oxc_codegen/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::path::PathBuf;
/// Legal comment
///
/// <https://esbuild.github.io/api/#legal-comments>
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub enum LegalComment {
/// Do not preserve any legal comments (default).
#[default]
Expand All @@ -12,26 +12,26 @@ pub enum LegalComment {
Inline,
/// Move all legal comments to the end of the file.
Eof,
/// Move all legal comments to a .LEGAL.txt file and link to them with a comment.
Linked,
/// Return all legal comments and link then to them with a comment to the provided string.
Linked(String),
/// Move all legal comments to a .LEGAL.txt file but to not link to them.
External,
}

impl LegalComment {
/// Is None.
pub fn is_none(self) -> bool {
self == Self::None
pub fn is_none(&self) -> bool {
*self == Self::None
}

/// Is inline mode.
pub fn is_inline(self) -> bool {
self == Self::Inline
pub fn is_inline(&self) -> bool {
*self == Self::Inline
}

/// Is EOF mode.
pub fn is_eof(self) -> bool {
self == Self::Eof
pub fn is_eof(&self) -> bool {
*self == Self::Eof
}
}

Expand Down
21 changes: 20 additions & 1 deletion crates/oxc_codegen/tests/integration/legal_comments.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use oxc_codegen::{CodegenOptions, LegalComment};

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

fn cases() -> Vec<&'static str> {
vec![
Expand All @@ -21,3 +21,22 @@ fn legal_eof_comment() {
let options = CodegenOptions { legal_comments: LegalComment::Eof, ..Default::default() };
snapshot_options("legal_eof_comments", &cases(), &options);
}

#[test]
fn legal_linked_comment() {
let options = CodegenOptions {
legal_comments: LegalComment::Linked(String::from("test.js")),
..Default::default()
};
snapshot_options("legal_linked_comments", &cases(), &options);
}

#[test]
fn legal_external_comment() {
let options = CodegenOptions { legal_comments: LegalComment::External, ..Default::default() };
let code = "/* @license */\n/* @preserve */\nfoo;\n";
let ret = codegen_options(code, &options);
assert_eq!(ret.code, "foo;\n");
assert_eq!(ret.legal_comments[0].span.source_text(code), " @license ");
assert_eq!(ret.legal_comments[1].span.source_text(code), " @preserve ");
}
10 changes: 5 additions & 5 deletions crates/oxc_codegen/tests/integration/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@ pub mod ts;
pub mod unit;

use oxc_allocator::Allocator;
use oxc_codegen::{CodeGenerator, CodegenOptions};
use oxc_codegen::{CodeGenerator, CodegenOptions, CodegenReturn};
use oxc_parser::Parser;
use oxc_span::SourceType;

pub fn codegen(source_text: &str) -> String {
codegen_options(source_text, &CodegenOptions::default())
codegen_options(source_text, &CodegenOptions::default()).code
}

pub fn codegen_options(source_text: &str, options: &CodegenOptions) -> String {
pub fn codegen_options(source_text: &str, options: &CodegenOptions) -> CodegenReturn {
let allocator = Allocator::default();
let source_type = SourceType::ts();
let ret = Parser::new(&allocator, source_text, source_type).parse();
let mut options = options.clone();
options.single_quote = true;
CodeGenerator::new().with_options(options).build(&ret.program).code
CodeGenerator::new().with_options(options).build(&ret.program)
}

pub fn snapshot(name: &str, cases: &[&str]) {
Expand All @@ -33,7 +33,7 @@ pub fn snapshot_options(name: &str, cases: &[&str], options: &CodegenOptions) {
use std::fmt::Write;

let snapshot = cases.iter().enumerate().fold(String::new(), |mut w, (i, case)| {
let result = codegen_options(case, options);
let result = codegen_options(case, options).code;
write!(w, "########## {i}\n{case}\n----------\n{result}\n",).unwrap();
w
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
source: crates/oxc_codegen/tests/integration/main.rs
---
########## 0
/* @license */
/* @license */
foo;bar;
----------
foo;
bar;
/*! For license information please see test.js */
########## 1
/* @license */
/* @preserve */
foo;bar;
----------
foo;
bar;
/*! For license information please see test.js */
########## 2
/* @license */
//! KEEP
foo;bar;
----------
foo;
bar;
/*! For license information please see test.js */
########## 3
/* @license */
/*! KEEP */
foo;bar;
----------
foo;
bar;
/*! For license information please see test.js */