improve(transformer): create Atom for react import source lazily (#3578)

In JSX transform:

* Don't generate an `Atom` for react importer unless it's needed.
* Re-use the same string slice as `jsx_runtime_importer`.
* Reduce size of `Bindings`.
This commit is contained in:
overlookmotel 2024-06-07 09:18:46 +01:00 committed by GitHub
parent 4a075cccd6
commit 3d0e790401
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 29 additions and 22 deletions

View file

@ -17,7 +17,7 @@ pub fn import_source_cannot_be_set() -> OxcDiagnostic {
}
pub fn invalid_import_source() -> OxcDiagnostic {
OxcDiagnostic::warn("import_source cannot be an empty string")
OxcDiagnostic::warn("importSource cannot be an empty string or longer than u32::MAX bytes")
.with_help("Fix `importSource` option.")
}

View file

@ -68,8 +68,8 @@ struct ClassicBindings<'a> {
struct AutomaticScriptBindings<'a> {
ctx: Ctx<'a>,
react_importer: Atom<'a>,
jsx_runtime_importer: Atom<'a>,
react_importer_len: u32,
require_create_element: Option<BoundIdentifier<'a>>,
require_jsx: Option<BoundIdentifier<'a>>,
is_development: bool,
@ -78,14 +78,14 @@ struct AutomaticScriptBindings<'a> {
impl<'a> AutomaticScriptBindings<'a> {
fn new(
ctx: Ctx<'a>,
react_importer: Atom<'a>,
jsx_runtime_importer: Atom<'a>,
react_importer_len: u32,
is_development: bool,
) -> Self {
Self {
ctx,
react_importer,
jsx_runtime_importer,
react_importer_len,
require_create_element: None,
require_jsx: None,
is_development,
@ -94,7 +94,7 @@ impl<'a> AutomaticScriptBindings<'a> {
fn require_create_element(&mut self, ctx: &mut TraverseCtx<'a>) -> IdentifierReference<'a> {
if self.require_create_element.is_none() {
let source = self.react_importer.clone();
let source = get_import_source(&self.jsx_runtime_importer, self.react_importer_len);
let id = self.add_require_statement("react", source, true, ctx);
self.require_create_element = Some(id);
}
@ -132,8 +132,8 @@ impl<'a> AutomaticScriptBindings<'a> {
struct AutomaticModuleBindings<'a> {
ctx: Ctx<'a>,
react_importer: Atom<'a>,
jsx_runtime_importer: Atom<'a>,
react_importer_len: u32,
import_create_element: Option<BoundIdentifier<'a>>,
import_fragment: Option<BoundIdentifier<'a>>,
import_jsx: Option<BoundIdentifier<'a>>,
@ -144,14 +144,14 @@ struct AutomaticModuleBindings<'a> {
impl<'a> AutomaticModuleBindings<'a> {
fn new(
ctx: Ctx<'a>,
react_importer: Atom<'a>,
jsx_runtime_importer: Atom<'a>,
react_importer_len: u32,
is_development: bool,
) -> Self {
Self {
ctx,
react_importer,
jsx_runtime_importer,
react_importer_len,
import_create_element: None,
import_fragment: None,
import_jsx: None,
@ -162,7 +162,7 @@ impl<'a> AutomaticModuleBindings<'a> {
fn import_create_element(&mut self, ctx: &mut TraverseCtx<'a>) -> IdentifierReference<'a> {
if self.import_create_element.is_none() {
let source = self.react_importer.clone();
let source = get_import_source(&self.jsx_runtime_importer, self.react_importer_len);
let id = self.add_import_statement("createElement", source, ctx);
self.import_create_element = Some(id);
}
@ -231,6 +231,11 @@ impl<'a> AutomaticModuleBindings<'a> {
}
}
#[inline]
fn get_import_source<'a>(jsx_runtime_importer: &Atom<'a>, react_importer_len: u32) -> Atom<'a> {
Atom::from(&jsx_runtime_importer.as_str()[..react_importer_len as usize])
}
#[derive(Clone)]
pub struct BoundIdentifier<'a> {
pub name: Atom<'a>,
@ -326,45 +331,47 @@ impl<'a> ReactJsx<'a> {
}
let is_development = options.development;
#[allow(clippy::single_match_else)]
let (react_importer, jsx_runtime_importer) = match options.import_source.as_ref() {
#[allow(clippy::single_match_else, clippy::cast_possible_truncation)]
let (jsx_runtime_importer, source_len) = match options.import_source.as_ref() {
Some(import_source) => {
let react_importer = if import_source.is_empty() {
ctx.error(diagnostics::invalid_import_source());
Atom::from("react")
} else {
ctx.ast.new_atom(import_source)
let mut import_source = &**import_source;
let source_len = match u32::try_from(import_source.len()) {
Ok(0) | Err(_) => {
ctx.error(diagnostics::invalid_import_source());
import_source = "react";
import_source.len() as u32
}
Ok(source_len) => source_len,
};
let jsx_runtime_importer = ctx.ast.new_atom(&format!(
"{}/jsx-{}runtime",
react_importer,
import_source,
if is_development { "dev-" } else { "" }
));
(react_importer, jsx_runtime_importer)
(jsx_runtime_importer, source_len)
}
None => {
let react_importer = Atom::from("react");
let jsx_runtime_importer = if is_development {
Atom::from("react/jsx-dev-runtime")
} else {
Atom::from("react/jsx-runtime")
};
(react_importer, jsx_runtime_importer)
(jsx_runtime_importer, "react".len() as u32)
}
};
if ctx.source_type.is_script() {
Bindings::AutomaticScript(AutomaticScriptBindings::new(
Rc::clone(&ctx),
react_importer,
jsx_runtime_importer,
source_len,
is_development,
))
} else {
Bindings::AutomaticModule(AutomaticModuleBindings::new(
Rc::clone(&ctx),
react_importer,
jsx_runtime_importer,
source_len,
is_development,
))
}