From 788e444c1fac8d069533711b3b2b1ddbe6e8c37d Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Tue, 1 Oct 2024 09:57:46 +0000 Subject: [PATCH] perf(transformer): parse options from comments only once (#6152) Both React and TypeScript transforms were repeating the same work - iterating through comments to find `@jsx` pragmas. Instead, perform this search only once, using the optimized pragma search introduced in #6151. --- crates/oxc_transformer/src/lib.rs | 6 ++- crates/oxc_transformer/src/react/comments.rs | 30 +++++++++++---- crates/oxc_transformer/src/react/mod.rs | 3 +- crates/oxc_transformer/src/typescript/mod.rs | 9 ++--- .../oxc_transformer/src/typescript/options.rs | 37 ------------------- 5 files changed, 32 insertions(+), 53 deletions(-) diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index abe7f081777b4..d88cae8c48c9c 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -87,7 +87,7 @@ impl<'a> Transformer<'a> { } pub fn build_with_symbols_and_scopes( - self, + mut self, symbols: SymbolTable, scopes: ScopeTree, program: &mut Program<'a>, @@ -95,8 +95,10 @@ impl<'a> Transformer<'a> { let allocator = self.allocator; let ast_builder = AstBuilder::new(allocator); + react::update_options_with_comments(&mut self.options, &self.ctx); + let mut transformer = TransformerImpl { - x0_typescript: TypeScript::new(self.options.typescript, &self.ctx), + x0_typescript: TypeScript::new(&self.options.typescript, &self.ctx), x1_react: React::new(self.options.react, ast_builder, &self.ctx), x2_es2021: ES2021::new(self.options.es2021, &self.ctx), x2_es2020: ES2020::new(self.options.es2020, &self.ctx), diff --git a/crates/oxc_transformer/src/react/comments.rs b/crates/oxc_transformer/src/react/comments.rs index 355c67e4f7f9e..0470ba2cc97fd 100644 --- a/crates/oxc_transformer/src/react/comments.rs +++ b/crates/oxc_transformer/src/react/comments.rs @@ -1,7 +1,9 @@ +use std::borrow::Cow; + use oxc_ast::{Comment, CommentKind}; use oxc_syntax::identifier::is_irregular_whitespace; -use crate::{JsxOptions, JsxRuntime, TransformCtx}; +use crate::{JsxRuntime, TransformCtx, TransformOptions}; /// Scan through all comments and find the following pragmas: /// @@ -14,23 +16,32 @@ use crate::{JsxOptions, JsxRuntime, TransformCtx}; /// otherwise `JSDoc` could be used instead. /// /// This behavior is aligned with Babel. -pub(crate) fn update_options_with_comments(options: &mut JsxOptions, ctx: &TransformCtx) { +pub(crate) fn update_options_with_comments(options: &mut TransformOptions, ctx: &TransformCtx) { for comment in ctx.trivias.comments() { update_options_with_comment(options, comment, ctx.source_text); } } -fn update_options_with_comment(options: &mut JsxOptions, comment: &Comment, source_text: &str) { +fn update_options_with_comment( + options: &mut TransformOptions, + comment: &Comment, + source_text: &str, +) { let Some((keyword, remainder)) = find_jsx_pragma(comment, source_text) else { return }; match keyword { // @jsx "" => { - options.pragma = Some(remainder.to_string()); + // Don't set React option unless React transform is enabled + // otherwise can cause error in `ReactJsx::new` + if options.react.jsx_plugin || options.react.development { + options.react.pragma = Some(remainder.to_string()); + } + options.typescript.jsx_pragma = Cow::from(remainder.to_string()); } // @jsxRuntime "Runtime" => { - options.runtime = match remainder { + options.react.runtime = match remainder { "classic" => JsxRuntime::Classic, "automatic" => JsxRuntime::Automatic, _ => return, @@ -38,11 +49,16 @@ fn update_options_with_comment(options: &mut JsxOptions, comment: &Comment, sour } // @jsxImportSource "ImportSource" => { - options.import_source = Some(remainder.to_string()); + options.react.import_source = Some(remainder.to_string()); } // @jsxFrag "Frag" => { - options.pragma_frag = Some(remainder.to_string()); + // Don't set React option unless React transform is enabled + // otherwise can cause error in `ReactJsx::new` + if options.react.jsx_plugin || options.react.development { + options.react.pragma_frag = Some(remainder.to_string()); + } + options.typescript.jsx_pragma_frag = Cow::from(remainder.to_string()); } _ => {} } diff --git a/crates/oxc_transformer/src/react/mod.rs b/crates/oxc_transformer/src/react/mod.rs index 56132065e12cb..8d841c64e776f 100644 --- a/crates/oxc_transformer/src/react/mod.rs +++ b/crates/oxc_transformer/src/react/mod.rs @@ -20,7 +20,7 @@ pub use self::{ }; use crate::TransformCtx; -use comments::update_options_with_comments; +pub(crate) use comments::update_options_with_comments; /// [Preset React](https://babel.dev/docs/babel-preset-react) /// @@ -45,7 +45,6 @@ pub struct React<'a, 'ctx> { impl<'a, 'ctx> React<'a, 'ctx> { pub fn new(mut options: JsxOptions, ast: AstBuilder<'a>, ctx: &'ctx TransformCtx<'a>) -> Self { if options.jsx_plugin || options.development { - update_options_with_comments(&mut options, ctx); options.conform(); } let JsxOptions { diff --git a/crates/oxc_transformer/src/typescript/mod.rs b/crates/oxc_transformer/src/typescript/mod.rs index 833d7dc2edcbc..44ef9d327e598 100644 --- a/crates/oxc_transformer/src/typescript/mod.rs +++ b/crates/oxc_transformer/src/typescript/mod.rs @@ -49,15 +49,14 @@ pub struct TypeScript<'a, 'ctx> { } impl<'a, 'ctx> TypeScript<'a, 'ctx> { - pub fn new(mut options: TypeScriptOptions, ctx: &'ctx TransformCtx<'a>) -> Self { - options.update_with_comments(ctx); + pub fn new(options: &TypeScriptOptions, ctx: &'ctx TransformCtx<'a>) -> Self { Self { ctx, - annotations: TypeScriptAnnotations::new(&options, ctx), + annotations: TypeScriptAnnotations::new(options, ctx), r#enum: TypeScriptEnum::new(), - namespace: TypeScriptNamespace::new(&options, ctx), + namespace: TypeScriptNamespace::new(options, ctx), module: TypeScriptModule::new(ctx), - rewrite_extensions: TypeScriptRewriteExtensions::new(&options), + rewrite_extensions: TypeScriptRewriteExtensions::new(options), } } } diff --git a/crates/oxc_transformer/src/typescript/options.rs b/crates/oxc_transformer/src/typescript/options.rs index 56c3e82222525..f52af65c6620c 100644 --- a/crates/oxc_transformer/src/typescript/options.rs +++ b/crates/oxc_transformer/src/typescript/options.rs @@ -5,8 +5,6 @@ use serde::{ Deserialize, Deserializer, }; -use crate::context::TransformCtx; - fn default_for_jsx_pragma() -> Cow<'static, str> { Cow::Borrowed("React.createElement") } @@ -60,41 +58,6 @@ pub struct TypeScriptOptions { pub rewrite_import_extensions: Option, } -impl TypeScriptOptions { - /// Scan through all comments and find the following pragmas - /// - /// * @jsx React.createElement - /// * @jsxFrag React.Fragment - /// - /// The comment does not need to be a jsdoc, - /// otherwise `JSDoc` could be used instead. - /// - /// This behavior is aligned with babel. - pub(crate) fn update_with_comments(&mut self, ctx: &TransformCtx) { - for comment in ctx.trivias.comments() { - let mut comment = comment.span.source_text(ctx.source_text).trim_start(); - // strip leading jsdoc comment `*` and then whitespaces - while let Some(cur_comment) = comment.strip_prefix('*') { - comment = cur_comment.trim_start(); - } - // strip leading `@` - let Some(comment) = comment.strip_prefix('@') else { continue }; - - // read jsxFrag - if let Some(pragma_frag) = comment.strip_prefix("jsxFrag").map(str::trim) { - self.jsx_pragma_frag = Cow::from(pragma_frag.to_string()); - continue; - } - - // Put this condition at the end to avoid breaking @jsxXX - // read jsx - if let Some(pragma) = comment.strip_prefix("jsx").map(str::trim) { - self.jsx_pragma = Cow::from(pragma.to_string()); - } - } - } -} - impl Default for TypeScriptOptions { fn default() -> Self { Self {