mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
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.
This commit is contained in:
parent
54c1c53e69
commit
788e444c1f
5 changed files with 32 additions and 53 deletions
|
|
@ -87,7 +87,7 @@ impl<'a> Transformer<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_with_symbols_and_scopes(
|
pub fn build_with_symbols_and_scopes(
|
||||||
self,
|
mut self,
|
||||||
symbols: SymbolTable,
|
symbols: SymbolTable,
|
||||||
scopes: ScopeTree,
|
scopes: ScopeTree,
|
||||||
program: &mut Program<'a>,
|
program: &mut Program<'a>,
|
||||||
|
|
@ -95,8 +95,10 @@ impl<'a> Transformer<'a> {
|
||||||
let allocator = self.allocator;
|
let allocator = self.allocator;
|
||||||
let ast_builder = AstBuilder::new(allocator);
|
let ast_builder = AstBuilder::new(allocator);
|
||||||
|
|
||||||
|
react::update_options_with_comments(&mut self.options, &self.ctx);
|
||||||
|
|
||||||
let mut transformer = TransformerImpl {
|
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),
|
x1_react: React::new(self.options.react, ast_builder, &self.ctx),
|
||||||
x2_es2021: ES2021::new(self.options.es2021, &self.ctx),
|
x2_es2021: ES2021::new(self.options.es2021, &self.ctx),
|
||||||
x2_es2020: ES2020::new(self.options.es2020, &self.ctx),
|
x2_es2020: ES2020::new(self.options.es2020, &self.ctx),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use oxc_ast::{Comment, CommentKind};
|
use oxc_ast::{Comment, CommentKind};
|
||||||
use oxc_syntax::identifier::is_irregular_whitespace;
|
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:
|
/// Scan through all comments and find the following pragmas:
|
||||||
///
|
///
|
||||||
|
|
@ -14,23 +16,32 @@ use crate::{JsxOptions, JsxRuntime, TransformCtx};
|
||||||
/// otherwise `JSDoc` could be used instead.
|
/// otherwise `JSDoc` could be used instead.
|
||||||
///
|
///
|
||||||
/// This behavior is aligned with Babel.
|
/// 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() {
|
for comment in ctx.trivias.comments() {
|
||||||
update_options_with_comment(options, comment, ctx.source_text);
|
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 };
|
let Some((keyword, remainder)) = find_jsx_pragma(comment, source_text) else { return };
|
||||||
|
|
||||||
match keyword {
|
match keyword {
|
||||||
// @jsx
|
// @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
|
// @jsxRuntime
|
||||||
"Runtime" => {
|
"Runtime" => {
|
||||||
options.runtime = match remainder {
|
options.react.runtime = match remainder {
|
||||||
"classic" => JsxRuntime::Classic,
|
"classic" => JsxRuntime::Classic,
|
||||||
"automatic" => JsxRuntime::Automatic,
|
"automatic" => JsxRuntime::Automatic,
|
||||||
_ => return,
|
_ => return,
|
||||||
|
|
@ -38,11 +49,16 @@ fn update_options_with_comment(options: &mut JsxOptions, comment: &Comment, sour
|
||||||
}
|
}
|
||||||
// @jsxImportSource
|
// @jsxImportSource
|
||||||
"ImportSource" => {
|
"ImportSource" => {
|
||||||
options.import_source = Some(remainder.to_string());
|
options.react.import_source = Some(remainder.to_string());
|
||||||
}
|
}
|
||||||
// @jsxFrag
|
// @jsxFrag
|
||||||
"Frag" => {
|
"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());
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ pub use self::{
|
||||||
};
|
};
|
||||||
use crate::TransformCtx;
|
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)
|
/// [Preset React](https://babel.dev/docs/babel-preset-react)
|
||||||
///
|
///
|
||||||
|
|
@ -45,7 +45,6 @@ pub struct React<'a, 'ctx> {
|
||||||
impl<'a, 'ctx> React<'a, 'ctx> {
|
impl<'a, 'ctx> React<'a, 'ctx> {
|
||||||
pub fn new(mut options: JsxOptions, ast: AstBuilder<'a>, ctx: &'ctx TransformCtx<'a>) -> Self {
|
pub fn new(mut options: JsxOptions, ast: AstBuilder<'a>, ctx: &'ctx TransformCtx<'a>) -> Self {
|
||||||
if options.jsx_plugin || options.development {
|
if options.jsx_plugin || options.development {
|
||||||
update_options_with_comments(&mut options, ctx);
|
|
||||||
options.conform();
|
options.conform();
|
||||||
}
|
}
|
||||||
let JsxOptions {
|
let JsxOptions {
|
||||||
|
|
|
||||||
|
|
@ -49,15 +49,14 @@ pub struct TypeScript<'a, 'ctx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'ctx> TypeScript<'a, 'ctx> {
|
impl<'a, 'ctx> TypeScript<'a, 'ctx> {
|
||||||
pub fn new(mut options: TypeScriptOptions, ctx: &'ctx TransformCtx<'a>) -> Self {
|
pub fn new(options: &TypeScriptOptions, ctx: &'ctx TransformCtx<'a>) -> Self {
|
||||||
options.update_with_comments(ctx);
|
|
||||||
Self {
|
Self {
|
||||||
ctx,
|
ctx,
|
||||||
annotations: TypeScriptAnnotations::new(&options, ctx),
|
annotations: TypeScriptAnnotations::new(options, ctx),
|
||||||
r#enum: TypeScriptEnum::new(),
|
r#enum: TypeScriptEnum::new(),
|
||||||
namespace: TypeScriptNamespace::new(&options, ctx),
|
namespace: TypeScriptNamespace::new(options, ctx),
|
||||||
module: TypeScriptModule::new(ctx),
|
module: TypeScriptModule::new(ctx),
|
||||||
rewrite_extensions: TypeScriptRewriteExtensions::new(&options),
|
rewrite_extensions: TypeScriptRewriteExtensions::new(options),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,6 @@ use serde::{
|
||||||
Deserialize, Deserializer,
|
Deserialize, Deserializer,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::context::TransformCtx;
|
|
||||||
|
|
||||||
fn default_for_jsx_pragma() -> Cow<'static, str> {
|
fn default_for_jsx_pragma() -> Cow<'static, str> {
|
||||||
Cow::Borrowed("React.createElement")
|
Cow::Borrowed("React.createElement")
|
||||||
}
|
}
|
||||||
|
|
@ -60,41 +58,6 @@ pub struct TypeScriptOptions {
|
||||||
pub rewrite_import_extensions: Option<RewriteExtensionsMode>,
|
pub rewrite_import_extensions: Option<RewriteExtensionsMode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
impl Default for TypeScriptOptions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue