From 235cdba927d942fae217ee9f69010d126b2b426f Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Tue, 1 Oct 2024 09:27:37 +0000 Subject: [PATCH] refactor(transformer): use AstBuilder instance from TraverseCtx (#6209) A small modification of #6173. Closes #6172. --- crates/oxc_transformer/src/context.rs | 7 +- crates/oxc_transformer/src/lib.rs | 13 +- .../oxc_transformer/src/react/display_name.rs | 14 +- crates/oxc_transformer/src/react/jsx.rs | 130 ++++++------- crates/oxc_transformer/src/react/jsx_self.rs | 25 +-- .../oxc_transformer/src/react/jsx_source.rs | 66 ++++--- crates/oxc_transformer/src/react/mod.rs | 8 +- crates/oxc_transformer/src/react/refresh.rs | 45 ++--- .../src/typescript/annotations.rs | 26 ++- crates/oxc_transformer/src/typescript/enum.rs | 37 ++-- crates/oxc_transformer/src/typescript/mod.rs | 4 +- .../src/typescript/namespace.rs | 178 +++++++++--------- 12 files changed, 274 insertions(+), 279 deletions(-) diff --git a/crates/oxc_transformer/src/context.rs b/crates/oxc_transformer/src/context.rs index 55b270a121e26..ede7d3bdebeb7 100644 --- a/crates/oxc_transformer/src/context.rs +++ b/crates/oxc_transformer/src/context.rs @@ -4,8 +4,7 @@ use std::{ path::{Path, PathBuf}, }; -use oxc_allocator::Allocator; -use oxc_ast::{AstBuilder, Trivias}; +use oxc_ast::Trivias; use oxc_diagnostics::OxcDiagnostic; use oxc_span::SourceType; @@ -22,8 +21,6 @@ pub struct TransformCtx<'a> { pub trivias: Trivias, - pub ast: AstBuilder<'a>, - /// pub filename: String, @@ -45,7 +42,6 @@ pub struct TransformCtx<'a> { impl<'a> TransformCtx<'a> { pub fn new( - allocator: &'a Allocator, source_path: &Path, source_type: SourceType, source_text: &'a str, @@ -62,7 +58,6 @@ impl<'a> TransformCtx<'a> { Self { errors: RefCell::new(vec![]), - ast: AstBuilder::new(allocator), filename, source_path, source_type, diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index a6c8868c082f1..abe7f081777b4 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -8,6 +8,8 @@ //! * //! * +use oxc_ast::AstBuilder; + // Core mod common; mod compiler_assumptions; @@ -68,6 +70,7 @@ pub struct TransformerReturn { pub struct Transformer<'a> { ctx: TransformCtx<'a>, options: TransformOptions, + allocator: &'a Allocator, } impl<'a> Transformer<'a> { @@ -79,9 +82,8 @@ impl<'a> Transformer<'a> { trivias: Trivias, options: TransformOptions, ) -> Self { - let ctx = - TransformCtx::new(allocator, source_path, source_type, source_text, trivias, &options); - Self { ctx, options } + let ctx = TransformCtx::new(source_path, source_type, source_text, trivias, &options); + Self { ctx, options, allocator } } pub fn build_with_symbols_and_scopes( @@ -90,11 +92,12 @@ impl<'a> Transformer<'a> { scopes: ScopeTree, program: &mut Program<'a>, ) -> TransformerReturn { - let allocator = self.ctx.ast.allocator; + let allocator = self.allocator; + let ast_builder = AstBuilder::new(allocator); let mut transformer = TransformerImpl { x0_typescript: TypeScript::new(self.options.typescript, &self.ctx), - x1_react: React::new(self.options.react, &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), x2_es2019: ES2019::new(self.options.es2019), diff --git a/crates/oxc_transformer/src/react/display_name.rs b/crates/oxc_transformer/src/react/display_name.rs index 6beedf1e99299..54ae8ee586c5e 100644 --- a/crates/oxc_transformer/src/react/display_name.rs +++ b/crates/oxc_transformer/src/react/display_name.rs @@ -125,7 +125,7 @@ impl<'a, 'ctx> Traverse<'a> for ReactDisplayName<'a, 'ctx> { } }; - self.add_display_name(obj_expr, name); + Self::add_display_name(obj_expr, name, ctx); } } @@ -155,7 +155,11 @@ impl<'a, 'ctx> ReactDisplayName<'a, 'ctx> { } /// Add key value `displayName: name` to the `React.createClass` object. - fn add_display_name(&self, obj_expr: &mut ObjectExpression<'a>, name: Atom<'a>) { + fn add_display_name( + obj_expr: &mut ObjectExpression<'a>, + name: Atom<'a>, + ctx: &TraverseCtx<'a>, + ) { const DISPLAY_NAME: &str = "displayName"; // Not safe with existing display name. let not_safe = obj_expr.properties.iter().any(|prop| { @@ -166,11 +170,11 @@ impl<'a, 'ctx> ReactDisplayName<'a, 'ctx> { } obj_expr.properties.insert( 0, - self.ctx.ast.object_property_kind_object_property( + ctx.ast.object_property_kind_object_property( SPAN, PropertyKind::Init, - self.ctx.ast.property_key_identifier_name(SPAN, DISPLAY_NAME), - self.ctx.ast.expression_string_literal(SPAN, name), + ctx.ast.property_key_identifier_name(SPAN, DISPLAY_NAME), + ctx.ast.expression_string_literal(SPAN, name), None, false, false, diff --git a/crates/oxc_transformer/src/react/jsx.rs b/crates/oxc_transformer/src/react/jsx.rs index 95801a01eedde..9447a9fb4cdb7 100644 --- a/crates/oxc_transformer/src/react/jsx.rs +++ b/crates/oxc_transformer/src/react/jsx.rs @@ -325,6 +325,7 @@ impl<'a> Pragma<'a> { fn parse( pragma: Option<&String>, default_property_name: &'static str, + ast: AstBuilder<'a>, ctx: &TransformCtx<'a>, ) -> Self { if let Some(pragma) = pragma { @@ -340,12 +341,12 @@ impl<'a> Pragma<'a> { if property_name.is_empty() || parts.next().is_some() { return Self::invalid(default_property_name, ctx); } - Some(ctx.ast.atom(property_name)) + Some(ast.atom(property_name)) } None => None, }; - let object = ctx.ast.atom(object_name); + let object = ast.atom(object_name); Self { object, property } } else { Self::default(default_property_name) @@ -372,14 +373,14 @@ impl<'a> Pragma<'a> { } impl<'a, 'ctx> ReactJsx<'a, 'ctx> { - pub fn new(options: JsxOptions, ctx: &'ctx TransformCtx<'a>) -> Self { + pub fn new(options: JsxOptions, ast: AstBuilder<'a>, ctx: &'ctx TransformCtx<'a>) -> Self { let bindings = match options.runtime { JsxRuntime::Classic => { if options.import_source.is_some() { ctx.error(diagnostics::import_source_cannot_be_set()); } - let pragma = Pragma::parse(options.pragma.as_ref(), "createElement", ctx); - let pragma_frag = Pragma::parse(options.pragma_frag.as_ref(), "Fragment", ctx); + let pragma = Pragma::parse(options.pragma.as_ref(), "createElement", ast, ctx); + let pragma_frag = Pragma::parse(options.pragma_frag.as_ref(), "Fragment", ast, ctx); Bindings::Classic(ClassicBindings { pragma, pragma_frag }) } JsxRuntime::Automatic => { @@ -400,7 +401,7 @@ impl<'a, 'ctx> ReactJsx<'a, 'ctx> { } Ok(source_len) => source_len, }; - let jsx_runtime_importer = ctx.ast.atom(&format!( + let jsx_runtime_importer = ast.atom(&format!( "{}/jsx-{}runtime", import_source, if is_development { "dev-" } else { "" } @@ -466,12 +467,8 @@ impl<'a, 'ctx> ReactJsx<'a, 'ctx> { self.ctx.source_type.is_script() } - fn ast(&self) -> AstBuilder<'a> { - self.ctx.ast - } - fn insert_filename_var_statement(&mut self, ctx: &mut TraverseCtx<'a>) { - let Some(declarator) = self.jsx_source.get_filename_var_declarator() else { return }; + let Some(declarator) = self.jsx_source.get_filename_var_declarator(ctx) else { return }; // If is a module, add filename statements before `import`s. If script, then after `require`s. // This is the same behavior as Babel. @@ -482,7 +479,7 @@ impl<'a, 'ctx> ReactJsx<'a, 'ctx> { let stmt = Statement::VariableDeclaration(ctx.ast.alloc_variable_declaration( SPAN, VariableDeclarationKind::Var, - self.ctx.ast.vec1(declarator), + ctx.ast.vec1(declarator), false, )); self.ctx.top_level_statements.insert_statement(stmt); @@ -504,13 +501,13 @@ impl<'a, 'ctx> ReactJsx<'a, 'ctx> { let is_automatic = !is_classic; let is_development = self.options.development; - let mut arguments = self.ast().vec(); + let mut arguments = ctx.ast.vec(); // The key prop in `
` let mut key_prop = None; // The object properties for the second argument of `React.createElement` - let mut properties = self.ast().vec(); + let mut properties = ctx.ast.vec(); let mut self_attr_span = None; let mut source_attr_span = None; @@ -540,9 +537,9 @@ impl<'a, 'ctx> ReactJsx<'a, 'ctx> { // Add attribute to prop object let kind = PropertyKind::Init; - let key = self.get_attribute_name(&attr.name); + let key = Self::get_attribute_name(&attr.name, ctx); let value = self.transform_jsx_attribute_value(attr.value.as_ref(), ctx); - let object_property = self.ast().object_property_kind_object_property( + let object_property = ctx.ast.object_property_kind_object_property( attr.span, kind, key, value, None, false, false, false, ); properties.push(object_property); @@ -555,7 +552,7 @@ impl<'a, 'ctx> ReactJsx<'a, 'ctx> { { arguments.push(Argument::from({ // SAFETY: `ast.copy` is unsound! We need to fix. - unsafe { self.ast().copy(&spread.argument) } + unsafe { ctx.ast.copy(&spread.argument) } })); continue; } @@ -565,13 +562,13 @@ impl<'a, 'ctx> ReactJsx<'a, 'ctx> { match &spread.argument { Expression::ObjectExpression(expr) if !expr.has_proto() => { // SAFETY: `ast.copy` is unsound! We need to fix. - properties.extend(unsafe { self.ast().copy(&expr.properties) }); + properties.extend(unsafe { ctx.ast.copy(&expr.properties) }); } expr => { // SAFETY: `ast.copy` is unsound! We need to fix. - let argument = unsafe { self.ast().copy(expr) }; - let object_property = self - .ast() + let argument = unsafe { ctx.ast.copy(expr) }; + let object_property = ctx + .ast .object_property_kind_spread_element(spread.span, argument); properties.push(object_property); } @@ -588,7 +585,7 @@ impl<'a, 'ctx> ReactJsx<'a, 'ctx> { // Append children to object properties in automatic mode if is_automatic { - let mut children = self.ast().vec_from_iter( + let mut children = ctx.ast.vec_from_iter( children.iter().filter_map(|child| self.transform_jsx_child(child, ctx)), ); children_len = children.len(); @@ -596,16 +593,16 @@ impl<'a, 'ctx> ReactJsx<'a, 'ctx> { let value = if children_len == 1 { children.pop().unwrap() } else { - let elements = self - .ast() + let elements = ctx + .ast .vec_from_iter(children.into_iter().map(ArrayExpressionElement::from)); need_jsxs = true; - self.ast().expression_array(SPAN, elements, None) + ctx.ast.expression_array(SPAN, elements, None) }; - properties.push(self.ast().object_property_kind_object_property( + properties.push(ctx.ast.object_property_kind_object_property( SPAN, PropertyKind::Init, - self.ast().property_key_identifier_name(SPAN, "children"), + ctx.ast.property_key_identifier_name(SPAN, "children"), value, None, false, @@ -621,7 +618,7 @@ impl<'a, 'ctx> ReactJsx<'a, 'ctx> { if let Some(span) = self_attr_span { self.jsx_self.report_error(span); } else { - properties.push(self.jsx_self.get_object_property_kind_for_jsx_plugin()); + properties.push(ReactJsxSelf::get_object_property_kind_for_jsx_plugin(ctx)); } } @@ -649,7 +646,7 @@ impl<'a, 'ctx> ReactJsx<'a, 'ctx> { } } - self.transform_element_name(&e.opening_element.name) + self.transform_element_name(&e.opening_element.name, ctx) } JSXElementOrFragment::Fragment(_) => self.get_fragment(ctx), }; @@ -657,11 +654,11 @@ impl<'a, 'ctx> ReactJsx<'a, 'ctx> { // If runtime is automatic that means we always to add `{ .. }` as the second argument even if it's empty if is_automatic || !properties.is_empty() { - let object_expression = self.ast().expression_object(SPAN, properties, None); + let object_expression = ctx.ast.expression_object(SPAN, properties, None); arguments.push(Argument::from(object_expression)); } else if arguments.len() == 1 { // If not and second argument doesn't exist, we should add `null` as the second argument - let null_expr = self.ast().expression_null_literal(SPAN); + let null_expr = ctx.ast.expression_null_literal(SPAN); arguments.push(Argument::from(null_expr)); } @@ -671,12 +668,12 @@ impl<'a, 'ctx> ReactJsx<'a, 'ctx> { if key_prop.is_some() { arguments.push(Argument::from(self.transform_jsx_attribute_value(key_prop, ctx))); } else if is_development { - arguments.push(Argument::from(self.ctx.ast.void_0(SPAN))); + arguments.push(Argument::from(ctx.ast.void_0(SPAN))); } // isStaticChildren if is_development { - arguments.push(Argument::from(self.ctx.ast.expression_boolean_literal( + arguments.push(Argument::from(ctx.ast.expression_boolean_literal( SPAN, if is_fragment { false } else { children_len > 1 }, ))); @@ -700,7 +697,7 @@ impl<'a, 'ctx> ReactJsx<'a, 'ctx> { if let Some(span) = self_attr_span { self.jsx_self.report_error(span); } else { - arguments.push(Argument::from(self.ctx.ast.expression_this(SPAN))); + arguments.push(Argument::from(ctx.ast.expression_this(SPAN))); } } } @@ -716,27 +713,31 @@ impl<'a, 'ctx> ReactJsx<'a, 'ctx> { } let callee = self.get_create_element(has_key_after_props_spread, need_jsxs, ctx); - self.ast().expression_call(e.span(), callee, NONE, arguments, false) + ctx.ast.expression_call(e.span(), callee, NONE, arguments, false) } - fn transform_element_name(&self, name: &JSXElementName<'a>) -> Expression<'a> { + fn transform_element_name( + &self, + name: &JSXElementName<'a>, + ctx: &TraverseCtx<'a>, + ) -> Expression<'a> { match name { JSXElementName::Identifier(ident) => { - self.ast().expression_string_literal(ident.span, ident.name.clone()) + ctx.ast.expression_string_literal(ident.span, ident.name.clone()) } JSXElementName::IdentifierReference(ident) => { - self.ast().expression_from_identifier_reference(ident.as_ref().clone()) + ctx.ast.expression_from_identifier_reference(ident.as_ref().clone()) } JSXElementName::MemberExpression(member_expr) => { - self.transform_jsx_member_expression(member_expr) + Self::transform_jsx_member_expression(member_expr, ctx) } JSXElementName::NamespacedName(namespaced) => { if self.options.throw_if_namespace { self.ctx.error(diagnostics::namespace_does_not_support(namespaced.span)); } - self.ast().expression_string_literal(namespaced.span, namespaced.to_string()) + ctx.ast.expression_string_literal(namespaced.span, namespaced.to_string()) } - JSXElementName::ThisExpression(expr) => self.ast().expression_this(expr.span), + JSXElementName::ThisExpression(expr) => ctx.ast.expression_this(expr.span), } } @@ -786,25 +787,26 @@ impl<'a, 'ctx> ReactJsx<'a, 'ctx> { } else { bindings.import_jsx(ctx) }; - self.ast().expression_from_identifier_reference(ident) + ctx.ast.expression_from_identifier_reference(ident) } } } - fn transform_jsx_member_expression(&self, expr: &JSXMemberExpression<'a>) -> Expression<'a> { + fn transform_jsx_member_expression( + expr: &JSXMemberExpression<'a>, + ctx: &TraverseCtx<'a>, + ) -> Expression<'a> { let object = match &expr.object { JSXMemberExpressionObject::IdentifierReference(ident) => { - self.ast().expression_from_identifier_reference(ident.as_ref().clone()) + ctx.ast.expression_from_identifier_reference(ident.as_ref().clone()) } JSXMemberExpressionObject::MemberExpression(expr) => { - self.transform_jsx_member_expression(expr) - } - JSXMemberExpressionObject::ThisExpression(expr) => { - self.ast().expression_this(expr.span) + Self::transform_jsx_member_expression(expr, ctx) } + JSXMemberExpressionObject::ThisExpression(expr) => ctx.ast.expression_this(expr.span), }; let property = IdentifierName::new(expr.property.span, expr.property.name.clone()); - self.ast().member_expression_static(expr.span, object, property, false).into() + ctx.ast.member_expression_static(expr.span, object, property, false).into() } fn transform_jsx_attribute_value( @@ -815,7 +817,7 @@ impl<'a, 'ctx> ReactJsx<'a, 'ctx> { match value { Some(JSXAttributeValue::StringLiteral(s)) => { let jsx_text = Self::decode_entities(s.value.as_str()); - self.ast().expression_string_literal(s.span, jsx_text) + ctx.ast.expression_string_literal(s.span, jsx_text) } Some(JSXAttributeValue::Element(e)) => { self.transform_jsx(&JSXElementOrFragment::Element(e), ctx) @@ -826,13 +828,13 @@ impl<'a, 'ctx> ReactJsx<'a, 'ctx> { Some(JSXAttributeValue::ExpressionContainer(c)) => match &c.expression { e @ match_expression!(JSXExpression) => { // SAFETY: `ast.copy` is unsound! We need to fix. - unsafe { self.ast().copy(e.to_expression()) } + unsafe { ctx.ast.copy(e.to_expression()) } } JSXExpression::EmptyExpression(e) => { - self.ast().expression_boolean_literal(e.span, true) + ctx.ast.expression_boolean_literal(e.span, true) } }, - None => self.ast().expression_boolean_literal(SPAN, true), + None => ctx.ast.expression_boolean_literal(SPAN, true), } } @@ -842,11 +844,11 @@ impl<'a, 'ctx> ReactJsx<'a, 'ctx> { ctx: &mut TraverseCtx<'a>, ) -> Option> { match child { - JSXChild::Text(text) => self.transform_jsx_text(text), + JSXChild::Text(text) => Self::transform_jsx_text(text, ctx), JSXChild::ExpressionContainer(e) => match &e.expression { e @ match_expression!(JSXExpression) => { // SAFETY: `ast.copy` is unsound! We need to fix. - Some(unsafe { self.ast().copy(e.to_expression()) }) + Some(unsafe { ctx.ast.copy(e.to_expression()) }) } JSXExpression::EmptyExpression(_) => None, }, @@ -863,28 +865,28 @@ impl<'a, 'ctx> ReactJsx<'a, 'ctx> { } } - fn get_attribute_name(&self, name: &JSXAttributeName<'a>) -> PropertyKey<'a> { + fn get_attribute_name(name: &JSXAttributeName<'a>, ctx: &TraverseCtx<'a>) -> PropertyKey<'a> { match name { JSXAttributeName::Identifier(ident) => { let name = ident.name.clone(); if ident.name.contains('-') { - let expr = self.ast().expression_string_literal(ident.span, name); - self.ast().property_key_expression(expr) + let expr = ctx.ast.expression_string_literal(ident.span, name); + ctx.ast.property_key_expression(expr) } else { - self.ast().property_key_identifier_name(ident.span, name) + ctx.ast.property_key_identifier_name(ident.span, name) } } JSXAttributeName::NamespacedName(namespaced) => { - let name = self.ast().atom(&namespaced.to_string()); - let expr = self.ast().expression_string_literal(namespaced.span, name); - self.ast().property_key_expression(expr) + let name = ctx.ast.atom(&namespaced.to_string()); + let expr = ctx.ast.expression_string_literal(namespaced.span, name); + ctx.ast.property_key_expression(expr) } } } - fn transform_jsx_text(&self, text: &JSXText<'a>) -> Option> { + fn transform_jsx_text(text: &JSXText<'a>, ctx: &TraverseCtx<'a>) -> Option> { Self::fixup_whitespace_and_decode_entities(text.value.as_str()) - .map(|s| self.ast().expression_string_literal(text.span, s)) + .map(|s| ctx.ast.expression_string_literal(text.span, s)) } /// JSX trims whitespace at the end and beginning of lines, except that the diff --git a/crates/oxc_transformer/src/react/jsx_self.rs b/crates/oxc_transformer/src/react/jsx_self.rs index e83c38aae2b81..991ad4f7ffbe4 100644 --- a/crates/oxc_transformer/src/react/jsx_self.rs +++ b/crates/oxc_transformer/src/react/jsx_self.rs @@ -51,9 +51,9 @@ impl<'a, 'ctx> Traverse<'a> for ReactJsxSelf<'a, 'ctx> { fn enter_jsx_opening_element( &mut self, elem: &mut JSXOpeningElement<'a>, - _ctx: &mut TraverseCtx<'a>, + ctx: &mut TraverseCtx<'a>, ) { - self.add_self_this_attribute(elem); + self.add_self_this_attribute(elem, ctx); } } @@ -84,12 +84,13 @@ impl<'a, 'ctx> ReactJsxSelf<'a, 'ctx> { true } - pub fn get_object_property_kind_for_jsx_plugin(&self) -> ObjectPropertyKind<'a> { + pub fn get_object_property_kind_for_jsx_plugin( + ctx: &mut TraverseCtx<'a>, + ) -> ObjectPropertyKind<'a> { let kind = PropertyKind::Init; - let key = self.ctx.ast.property_key_identifier_name(SPAN, SELF); - let value = self.ctx.ast.expression_this(SPAN); - self.ctx - .ast + let key = ctx.ast.property_key_identifier_name(SPAN, SELF); + let value = ctx.ast.expression_this(SPAN); + ctx.ast .object_property_kind_object_property(SPAN, kind, key, value, None, false, false, false) } @@ -99,7 +100,7 @@ impl<'a, 'ctx> ReactJsxSelf<'a, 'ctx> { /// `
` /// ^^^^^^^^^^^^^ - fn add_self_this_attribute(&self, elem: &mut JSXOpeningElement<'a>) { + fn add_self_this_attribute(&self, elem: &mut JSXOpeningElement<'a>, ctx: &TraverseCtx<'a>) { // Check if `__self` attribute already exists for item in &elem.attributes { if let JSXAttributeItem::Attribute(attribute) = item { @@ -112,12 +113,12 @@ impl<'a, 'ctx> ReactJsxSelf<'a, 'ctx> { } } - let name = self.ctx.ast.jsx_attribute_name_jsx_identifier(SPAN, SELF); + let name = ctx.ast.jsx_attribute_name_jsx_identifier(SPAN, SELF); let value = { - let jsx_expr = JSXExpression::from(self.ctx.ast.expression_this(SPAN)); - self.ctx.ast.jsx_attribute_value_jsx_expression_container(SPAN, jsx_expr) + let jsx_expr = JSXExpression::from(ctx.ast.expression_this(SPAN)); + ctx.ast.jsx_attribute_value_jsx_expression_container(SPAN, jsx_expr) }; - let attribute = self.ctx.ast.jsx_attribute_item_jsx_attribute(SPAN, name, Some(value)); + let attribute = ctx.ast.jsx_attribute_item_jsx_attribute(SPAN, name, Some(value)); elem.attributes.push(attribute); } } diff --git a/crates/oxc_transformer/src/react/jsx_source.rs b/crates/oxc_transformer/src/react/jsx_source.rs index 71acef7fdf286..97538a71b224f 100644 --- a/crates/oxc_transformer/src/react/jsx_source.rs +++ b/crates/oxc_transformer/src/react/jsx_source.rs @@ -59,8 +59,8 @@ impl<'a, 'ctx> ReactJsxSource<'a, 'ctx> { } impl<'a, 'ctx> Traverse<'a> for ReactJsxSource<'a, 'ctx> { - fn exit_program(&mut self, _program: &mut Program<'a>, _ctx: &mut TraverseCtx<'a>) { - if let Some(stmt) = self.get_filename_var_statement() { + fn exit_program(&mut self, _program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { + if let Some(stmt) = self.get_filename_var_statement(ctx) { self.ctx.top_level_statements.insert_statement(stmt); } } @@ -89,10 +89,9 @@ impl<'a, 'ctx> ReactJsxSource<'a, 'ctx> { ctx: &mut TraverseCtx<'a>, ) -> ObjectPropertyKind<'a> { let kind = PropertyKind::Init; - let key = self.ctx.ast.property_key_identifier_name(SPAN, SOURCE); + let key = ctx.ast.property_key_identifier_name(SPAN, SOURCE); let value = self.get_source_object(line, column, ctx); - self.ctx - .ast + ctx.ast .object_property_kind_object_property(SPAN, kind, key, value, None, false, false, false) } @@ -125,17 +124,15 @@ impl<'a, 'ctx> ReactJsxSource<'a, 'ctx> { } } - let key = self.ctx.ast.jsx_attribute_name_jsx_identifier(SPAN, SOURCE); + let key = ctx.ast.jsx_attribute_name_jsx_identifier(SPAN, SOURCE); // TODO: We shouldn't calculate line + column from scratch each time as it's expensive. // Build a table of byte indexes of each line's start on first usage, and save it. // Then calculate line and column from that. let (line, column) = self.get_line_column(elem.span.start); let object = self.get_source_object(line, column, ctx); - let value = self - .ctx - .ast - .jsx_attribute_value_jsx_expression_container(SPAN, JSXExpression::from(object)); - let attribute_item = self.ctx.ast.jsx_attribute_item_jsx_attribute(SPAN, key, Some(value)); + let value = + ctx.ast.jsx_attribute_value_jsx_expression_container(SPAN, JSXExpression::from(object)); + let attribute_item = ctx.ast.jsx_attribute_item_jsx_attribute(SPAN, key, Some(value)); elem.attributes.push(attribute_item); } @@ -149,74 +146,75 @@ impl<'a, 'ctx> ReactJsxSource<'a, 'ctx> { let kind = PropertyKind::Init; let filename = { - let key = self.ctx.ast.property_key_identifier_name(SPAN, "fileName"); + let key = ctx.ast.property_key_identifier_name(SPAN, "fileName"); let ident = self.get_filename_var(ctx).create_read_reference(ctx); - let value = self.ctx.ast.expression_from_identifier_reference(ident); - self.ctx.ast.object_property_kind_object_property( + let value = ctx.ast.expression_from_identifier_reference(ident); + ctx.ast.object_property_kind_object_property( SPAN, kind, key, value, None, false, false, false, ) }; let line_number = { - let key = self.ctx.ast.property_key_identifier_name(SPAN, "lineNumber"); - let value = self.ctx.ast.expression_numeric_literal( + let key = ctx.ast.property_key_identifier_name(SPAN, "lineNumber"); + let value = ctx.ast.expression_numeric_literal( SPAN, line as f64, line.to_string(), NumberBase::Decimal, ); - self.ctx.ast.object_property_kind_object_property( + ctx.ast.object_property_kind_object_property( SPAN, kind, key, value, None, false, false, false, ) }; let column_number = { - let key = self.ctx.ast.property_key_identifier_name(SPAN, "columnNumber"); - let value = self.ctx.ast.expression_numeric_literal( + let key = ctx.ast.property_key_identifier_name(SPAN, "columnNumber"); + let value = ctx.ast.expression_numeric_literal( SPAN, column as f64, column.to_string(), NumberBase::Decimal, ); - self.ctx.ast.object_property_kind_object_property( + ctx.ast.object_property_kind_object_property( SPAN, kind, key, value, None, false, false, false, ) }; - let mut properties = self.ctx.ast.vec_with_capacity(3); + let mut properties = ctx.ast.vec_with_capacity(3); properties.push(filename); properties.push(line_number); properties.push(column_number); - self.ctx.ast.expression_object(SPAN, properties, None) + ctx.ast.expression_object(SPAN, properties, None) } - pub fn get_filename_var_statement(&self) -> Option> { - let decl = self.get_filename_var_declarator()?; + pub fn get_filename_var_statement(&self, ctx: &mut TraverseCtx<'a>) -> Option> { + let decl = self.get_filename_var_declarator(ctx)?; - let var_decl = Statement::VariableDeclaration(self.ctx.ast.alloc_variable_declaration( + let var_decl = Statement::VariableDeclaration(ctx.ast.alloc_variable_declaration( SPAN, VariableDeclarationKind::Var, - self.ctx.ast.vec1(decl), + ctx.ast.vec1(decl), false, )); Some(var_decl) } - pub fn get_filename_var_declarator(&self) -> Option> { + pub fn get_filename_var_declarator( + &self, + ctx: &mut TraverseCtx<'a>, + ) -> Option> { let filename_var = self.filename_var.as_ref()?; let var_kind = VariableDeclarationKind::Var; let id = { let ident = filename_var.create_binding_identifier(); - let ident = self.ctx.ast.binding_pattern_kind_from_binding_identifier(ident); - self.ctx.ast.binding_pattern(ident, NONE, false) + let ident = ctx.ast.binding_pattern_kind_from_binding_identifier(ident); + ctx.ast.binding_pattern(ident, NONE, false) }; let decl = { - let init = self - .ctx - .ast - .expression_string_literal(SPAN, self.ctx.source_path.to_string_lossy()); - self.ctx.ast.variable_declarator(SPAN, var_kind, id, Some(init), false) + let init = + ctx.ast.expression_string_literal(SPAN, self.ctx.source_path.to_string_lossy()); + ctx.ast.variable_declarator(SPAN, var_kind, id, Some(init), false) }; Some(decl) } diff --git a/crates/oxc_transformer/src/react/mod.rs b/crates/oxc_transformer/src/react/mod.rs index c7ecde43c0392..56132065e12cb 100644 --- a/crates/oxc_transformer/src/react/mod.rs +++ b/crates/oxc_transformer/src/react/mod.rs @@ -9,7 +9,7 @@ mod refresh; mod utils; use oxc_allocator::Vec; -use oxc_ast::ast::*; +use oxc_ast::{ast::*, AstBuilder}; use oxc_traverse::{Traverse, TraverseCtx}; use refresh::ReactRefresh; @@ -43,7 +43,7 @@ pub struct React<'a, 'ctx> { // Constructors impl<'a, 'ctx> React<'a, 'ctx> { - pub fn new(mut options: JsxOptions, 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 { update_options_with_comments(&mut options, ctx); options.conform(); @@ -53,14 +53,14 @@ impl<'a, 'ctx> React<'a, 'ctx> { } = options; let refresh = options.refresh.clone(); Self { - jsx: ReactJsx::new(options, ctx), + jsx: ReactJsx::new(options, ast, ctx), display_name: ReactDisplayName::new(ctx), jsx_plugin, display_name_plugin, jsx_self_plugin, jsx_source_plugin, refresh_plugin: refresh.is_some(), - refresh: ReactRefresh::new(&refresh.unwrap_or_default(), ctx), + refresh: ReactRefresh::new(&refresh.unwrap_or_default(), ast, ctx), } } } diff --git a/crates/oxc_transformer/src/react/refresh.rs b/crates/oxc_transformer/src/react/refresh.rs index 8f2961a1b7d4c..be6d331e72882 100644 --- a/crates/oxc_transformer/src/react/refresh.rs +++ b/crates/oxc_transformer/src/react/refresh.rs @@ -112,10 +112,14 @@ pub struct ReactRefresh<'a, 'ctx> { } impl<'a, 'ctx> ReactRefresh<'a, 'ctx> { - pub fn new(options: &ReactRefreshOptions, ctx: &'ctx TransformCtx<'a>) -> Self { + pub fn new( + options: &ReactRefreshOptions, + ast: AstBuilder<'a>, + ctx: &'ctx TransformCtx<'a>, + ) -> Self { Self { - refresh_reg: RefreshIdentifierResolver::parse(&options.refresh_reg, ctx.ast), - refresh_sig: RefreshIdentifierResolver::parse(&options.refresh_sig, ctx.ast), + refresh_reg: RefreshIdentifierResolver::parse(&options.refresh_reg, ast), + refresh_sig: RefreshIdentifierResolver::parse(&options.refresh_sig, ast), emit_full_signatures: options.emit_full_signatures, signature_declarator_items: Vec::new(), registrations: Vec::default(), @@ -173,7 +177,7 @@ impl<'a, 'ctx> Traverse<'a> for ReactRefresh<'a, 'ctx> { Self::create_identifier_reference_from_binding_identifier(&binding_identifier, ctx), )); arguments.push(ctx.ast.argument_expression( - ctx.ast.expression_string_literal(SPAN, self.ctx.ast.atom(&persistent_id)), + ctx.ast.expression_string_literal(SPAN, ctx.ast.atom(&persistent_id)), )); new_statements.push(ctx.ast.statement_expression( SPAN, @@ -192,9 +196,9 @@ impl<'a, 'ctx> Traverse<'a> for ReactRefresh<'a, 'ctx> { fn enter_statements( &mut self, _stmts: &mut oxc_allocator::Vec<'a, Statement<'a>>, - _ctx: &mut TraverseCtx<'a>, + ctx: &mut TraverseCtx<'a>, ) { - self.signature_declarator_items.push(self.ctx.ast.vec()); + self.signature_declarator_items.push(ctx.ast.vec()); } fn exit_statements( @@ -315,7 +319,7 @@ impl<'a, 'ctx> Traverse<'a> for ReactRefresh<'a, 'ctx> { } arguments.insert(0, Argument::from(ctx.ast.move_expression(expr))); - *expr = self.ctx.ast.expression_call( + *expr = ctx.ast.expression_call( SPAN, Self::create_identifier_reference_from_binding_identifier(&binding_identifier, ctx), NONE, @@ -411,14 +415,14 @@ impl<'a, 'ctx> Traverse<'a> for ReactRefresh<'a, 'ctx> { Some(symbol_id), ReferenceFlags::Read, ); - let mut expr = self.ctx.ast.expression_from_identifier_reference(ident); + let mut expr = ctx.ast.expression_from_identifier_reference(ident); if is_member_expression { // binding_name.hook_name - expr = Expression::from(self.ctx.ast.member_expression_static( + expr = Expression::from(ctx.ast.member_expression_static( SPAN, expr, - self.ctx.ast.identifier_name(SPAN, hook_name.clone()), + ctx.ast.identifier_name(SPAN, hook_name.clone()), false, )); } @@ -634,29 +638,27 @@ impl<'a, 'ctx> ReactRefresh<'a, 'ctx> { if force_reset || !custom_hooks_in_scope.is_empty() { arguments.push( - self.ctx.ast.argument_expression( - self.ctx.ast.expression_boolean_literal(SPAN, force_reset), - ), + ctx.ast.argument_expression(ctx.ast.expression_boolean_literal(SPAN, force_reset)), ); } if !custom_hooks_in_scope.is_empty() { // function () { return custom_hooks_in_scope } - let formal_parameters = self.ctx.ast.formal_parameters( + let formal_parameters = ctx.ast.formal_parameters( SPAN, FormalParameterKind::FormalParameter, - self.ctx.ast.vec(), + ctx.ast.vec(), NONE, ); - let function_body = self.ctx.ast.function_body( + let function_body = ctx.ast.function_body( SPAN, - self.ctx.ast.vec(), - self.ctx.ast.vec1(self.ctx.ast.statement_return( + ctx.ast.vec(), + ctx.ast.vec1(ctx.ast.statement_return( SPAN, - Some(self.ctx.ast.expression_array(SPAN, custom_hooks_in_scope, None)), + Some(ctx.ast.expression_array(SPAN, custom_hooks_in_scope, None)), )), ); - let function = self.ctx.ast.function( + let function = ctx.ast.function( FunctionType::FunctionExpression, SPAN, None, @@ -671,8 +673,7 @@ impl<'a, 'ctx> ReactRefresh<'a, 'ctx> { ); let scope_id = ctx.create_child_scope_of_current(ScopeFlags::Function); function.scope_id.set(Some(scope_id)); - arguments - .push(self.ctx.ast.argument_expression(ctx.ast.expression_from_function(function))); + arguments.push(ctx.ast.argument_expression(ctx.ast.expression_from_function(function))); } // TODO: Handle var hoisted in ctx API diff --git a/crates/oxc_transformer/src/typescript/annotations.rs b/crates/oxc_transformer/src/typescript/annotations.rs index b6f608800e22f..1281769de9079 100644 --- a/crates/oxc_transformer/src/typescript/annotations.rs +++ b/crates/oxc_transformer/src/typescript/annotations.rs @@ -153,9 +153,9 @@ impl<'a, 'ctx> Traverse<'a> for TypeScriptAnnotations<'a, 'ctx> { // still considered a module if no_modules_remaining && some_modules_deleted && self.ctx.module_imports.is_empty() { let export_decl = ModuleDeclaration::ExportNamedDeclaration( - self.ctx.ast.plain_export_named_declaration(SPAN, self.ctx.ast.vec(), None), + ctx.ast.plain_export_named_declaration(SPAN, ctx.ast.vec(), None), ); - program.body.push(self.ctx.ast.statement_module_declaration(export_decl)); + program.body.push(ctx.ast.statement_module_declaration(export_decl)); } } @@ -217,23 +217,23 @@ impl<'a, 'ctx> Traverse<'a> for TypeScriptAnnotations<'a, 'ctx> { }); } - fn enter_expression(&mut self, expr: &mut Expression<'a>, _ctx: &mut TraverseCtx<'a>) { + fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { if expr.is_typescript_syntax() { let inner_expr = expr.get_inner_expression_mut(); - *expr = self.ctx.ast.move_expression(inner_expr); + *expr = ctx.ast.move_expression(inner_expr); } } fn enter_simple_assignment_target( &mut self, target: &mut SimpleAssignmentTarget<'a>, - _ctx: &mut TraverseCtx<'a>, + ctx: &mut TraverseCtx<'a>, ) { if let Some(expr) = target.get_expression_mut() { match expr.get_inner_expression_mut() { // `foo!++` to `foo++` inner_expr @ Expression::Identifier(_) => { - let inner_expr = self.ctx.ast.move_expression(inner_expr); + let inner_expr = ctx.ast.move_expression(inner_expr); let Expression::Identifier(ident) = inner_expr else { unreachable!(); }; @@ -241,7 +241,7 @@ impl<'a, 'ctx> Traverse<'a> for TypeScriptAnnotations<'a, 'ctx> { } // `foo.bar!++` to `foo.bar++` inner_expr @ match_member_expression!(Expression) => { - let inner_expr = self.ctx.ast.move_expression(inner_expr); + let inner_expr = ctx.ast.move_expression(inner_expr); let member_expr = inner_expr.into_member_expression(); *target = SimpleAssignmentTarget::from(member_expr); } @@ -256,12 +256,12 @@ impl<'a, 'ctx> Traverse<'a> for TypeScriptAnnotations<'a, 'ctx> { fn enter_assignment_target( &mut self, target: &mut AssignmentTarget<'a>, - _ctx: &mut TraverseCtx<'a>, + ctx: &mut TraverseCtx<'a>, ) { if let Some(expr) = target.get_expression_mut() { let inner_expr = expr.get_inner_expression_mut(); if inner_expr.is_member_expression() { - let inner_expr = self.ctx.ast.move_expression(inner_expr); + let inner_expr = ctx.ast.move_expression(inner_expr); let member_expr = inner_expr.into_member_expression(); *target = AssignmentTarget::from(member_expr); } @@ -334,11 +334,7 @@ impl<'a, 'ctx> Traverse<'a> for TypeScriptAnnotations<'a, 'ctx> { def.value .body .get_or_insert_with(|| { - self.ctx.ast.alloc_function_body( - SPAN, - self.ctx.ast.vec(), - self.ctx.ast.vec(), - ) + ctx.ast.alloc_function_body(SPAN, ctx.ast.vec(), ctx.ast.vec()) }) .statements .splice( @@ -427,7 +423,7 @@ impl<'a, 'ctx> Traverse<'a> for TypeScriptAnnotations<'a, 'ctx> { matches!(stmt, Statement::ExpressionStatement(stmt) if stmt.expression.is_super_call_expression()) }); if has_super_call { - let mut new_stmts = self.ctx.ast.vec(); + let mut new_stmts = ctx.ast.vec(); for stmt in stmts.drain(..) { let is_super_call = matches!(stmt, Statement::ExpressionStatement(ref stmt) if stmt.expression.is_super_call_expression()); new_stmts.push(stmt); diff --git a/crates/oxc_transformer/src/typescript/enum.rs b/crates/oxc_transformer/src/typescript/enum.rs index 3099d9e0bd8a2..e4b4edcfd7efa 100644 --- a/crates/oxc_transformer/src/typescript/enum.rs +++ b/crates/oxc_transformer/src/typescript/enum.rs @@ -11,20 +11,17 @@ use oxc_syntax::{ use oxc_traverse::{Traverse, TraverseCtx}; use rustc_hash::FxHashMap; -use crate::TransformCtx; - -pub struct TypeScriptEnum<'a, 'ctx> { - ctx: &'ctx TransformCtx<'a>, +pub struct TypeScriptEnum<'a> { enums: FxHashMap, FxHashMap, ConstantValue>>, } -impl<'a, 'ctx> TypeScriptEnum<'a, 'ctx> { - pub fn new(ctx: &'ctx TransformCtx<'a>) -> Self { - Self { ctx, enums: FxHashMap::default() } +impl<'a> TypeScriptEnum<'a> { + pub fn new() -> Self { + Self { enums: FxHashMap::default() } } } -impl<'a, 'ctx> Traverse<'a> for TypeScriptEnum<'a, 'ctx> { +impl<'a> Traverse<'a> for TypeScriptEnum<'a> { fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { let new_stmt = match stmt { Statement::TSEnumDeclaration(ts_enum_decl) => { @@ -47,7 +44,7 @@ impl<'a, 'ctx> Traverse<'a> for TypeScriptEnum<'a, 'ctx> { } } -impl<'a, 'ctx> TypeScriptEnum<'a, 'ctx> { +impl<'a> TypeScriptEnum<'a> { /// ```TypeScript /// enum Foo { /// X = 1, @@ -262,7 +259,7 @@ impl<'a, 'ctx> TypeScriptEnum<'a, 'ctx> { match constant_value { ConstantValue::Number(v) => { prev_constant_value = Some(ConstantValue::Number(v)); - self.get_initializer_expr(v) + Self::get_initializer_expr(v, ctx) } ConstantValue::String(str) => { prev_constant_value = None; @@ -280,7 +277,7 @@ impl<'a, 'ctx> TypeScriptEnum<'a, 'ctx> { let constant_value = ConstantValue::Number(value); prev_constant_value = Some(constant_value.clone()); previous_enum_members.insert(member_name.clone(), constant_value); - self.get_initializer_expr(value) + Self::get_initializer_expr(value, ctx) } ConstantValue::String(_) => unreachable!(), } @@ -292,10 +289,10 @@ impl<'a, 'ctx> TypeScriptEnum<'a, 'ctx> { }; // 1 + Foo["x"] - let one = self.get_number_literal_expression(1.0); + let one = Self::get_number_literal_expression(1.0, ctx); ast.expression_binary(SPAN, one, BinaryOperator::Addition, self_ref) } else { - self.get_number_literal_expression(0.0) + Self::get_number_literal_expression(0.0, ctx) }; let is_str = init.is_string_literal(); @@ -337,23 +334,23 @@ impl<'a, 'ctx> TypeScriptEnum<'a, 'ctx> { statements } - fn get_number_literal_expression(&self, value: f64) -> Expression<'a> { - self.ctx.ast.expression_numeric_literal(SPAN, value, value.to_string(), NumberBase::Decimal) + fn get_number_literal_expression(value: f64, ctx: &TraverseCtx<'a>) -> Expression<'a> { + ctx.ast.expression_numeric_literal(SPAN, value, value.to_string(), NumberBase::Decimal) } - fn get_initializer_expr(&self, value: f64) -> Expression<'a> { + fn get_initializer_expr(value: f64, ctx: &TraverseCtx<'a>) -> Expression<'a> { let is_negative = value < 0.0; // Infinity let expr = if value.is_infinite() { - self.ctx.ast.expression_identifier_reference(SPAN, "Infinity") + ctx.ast.expression_identifier_reference(SPAN, "Infinity") } else { let value = if is_negative { -value } else { value }; - self.get_number_literal_expression(value) + Self::get_number_literal_expression(value, ctx) }; if is_negative { - self.ctx.ast.expression_unary(SPAN, UnaryOperator::UnaryNegation, expr) + ctx.ast.expression_unary(SPAN, UnaryOperator::UnaryNegation, expr) } else { expr } @@ -366,7 +363,7 @@ enum ConstantValue { String(String), } -impl<'a, 'ctx> TypeScriptEnum<'a, 'ctx> { +impl<'a> TypeScriptEnum<'a> { /// Evaluate the expression to a constant value. /// Refer to [babel](https://github.com/babel/babel/blob/610897a9a96c5e344e77ca9665df7613d2f88358/packages/babel-plugin-transform-typescript/src/enum.ts#L241C1-L394C2) fn computed_constant_value( diff --git a/crates/oxc_transformer/src/typescript/mod.rs b/crates/oxc_transformer/src/typescript/mod.rs index d18bd77996a4d..833d7dc2edcbc 100644 --- a/crates/oxc_transformer/src/typescript/mod.rs +++ b/crates/oxc_transformer/src/typescript/mod.rs @@ -42,7 +42,7 @@ pub struct TypeScript<'a, 'ctx> { ctx: &'ctx TransformCtx<'a>, annotations: TypeScriptAnnotations<'a, 'ctx>, - r#enum: TypeScriptEnum<'a, 'ctx>, + r#enum: TypeScriptEnum<'a>, namespace: TypeScriptNamespace<'a, 'ctx>, module: TypeScriptModule<'a, 'ctx>, rewrite_extensions: Option, @@ -54,7 +54,7 @@ impl<'a, 'ctx> TypeScript<'a, 'ctx> { Self { ctx, annotations: TypeScriptAnnotations::new(&options, ctx), - r#enum: TypeScriptEnum::new(ctx), + r#enum: TypeScriptEnum::new(), namespace: TypeScriptNamespace::new(&options, ctx), module: TypeScriptModule::new(ctx), rewrite_extensions: TypeScriptRewriteExtensions::new(&options), diff --git a/crates/oxc_transformer/src/typescript/namespace.rs b/crates/oxc_transformer/src/typescript/namespace.rs index 8ea935bb598a2..35a57cea51cc2 100644 --- a/crates/oxc_transformer/src/typescript/namespace.rs +++ b/crates/oxc_transformer/src/typescript/namespace.rs @@ -43,9 +43,9 @@ impl<'a, 'ctx> Traverse<'a> for TypeScriptNamespace<'a, 'ctx> { // Recreate the statements vec for memory efficiency. // Inserting the `let` declaration multiple times will reallocate the whole statements vec // every time a namespace declaration is encountered. - let mut new_stmts = self.ctx.ast.vec(); + let mut new_stmts = ctx.ast.vec(); - for stmt in self.ctx.ast.move_vec(&mut program.body) { + for stmt in ctx.ast.move_vec(&mut program.body) { match stmt { Statement::TSModuleDeclaration(decl) => { if !decl.declare { @@ -56,15 +56,16 @@ impl<'a, 'ctx> Traverse<'a> for TypeScriptNamespace<'a, 'ctx> { if let Some(transformed_stmt) = self.handle_nested( { // SAFETY: `ast.copy` is unsound! We need to fix. - unsafe { self.ctx.ast.copy(&decl) }.unbox() + unsafe { ctx.ast.copy(&decl) }.unbox() }, None, ctx, ) { let name = decl.id.name(); if names.insert(name.clone()) { - new_stmts - .push(Statement::from(self.create_variable_declaration(name))); + new_stmts.push(Statement::from(Self::create_variable_declaration( + name, ctx, + ))); } new_stmts.push(transformed_stmt); continue; @@ -84,18 +85,17 @@ impl<'a, 'ctx> Traverse<'a> for TypeScriptNamespace<'a, 'ctx> { if let Some(transformed_stmt) = self.handle_nested( { // SAFETY: `ast.copy` is unsound! We need to fix. - unsafe { self.ctx.ast.copy(decl) } + unsafe { ctx.ast.copy(decl) } }, None, ctx, ) { let name = decl.id.name(); if names.insert(name.clone()) { - let declaration = self.create_variable_declaration(name); - let export_named_decl = self - .ctx - .ast - .plain_export_named_declaration_declaration( + let declaration = + Self::create_variable_declaration(name, ctx); + let export_named_decl = + ctx.ast.plain_export_named_declaration_declaration( SPAN, declaration, ); @@ -144,7 +144,7 @@ impl<'a, 'ctx> TypeScriptNamespace<'a, 'ctx> { &self, decl: TSModuleDeclaration<'a>, parent_export: Option>, - ctx: &mut TraverseCtx, + ctx: &mut TraverseCtx<'a>, ) -> Option> { // Skip empty declaration e.g. `namespace x;` let body = decl.body?; @@ -159,7 +159,7 @@ impl<'a, 'ctx> TypeScriptNamespace<'a, 'ctx> { // Reuse `TSModuleDeclaration`'s scope in transformed function let scope_id = decl.scope_id.get().unwrap(); let symbol_id = ctx.generate_uid(&real_name, scope_id, SymbolFlags::FunctionScopedVariable); - let name = self.ctx.ast.atom(ctx.symbols().get_name(symbol_id)); + let name = ctx.ast.atom(ctx.symbols().get_name(symbol_id)); let directives; let namespace_top_level; @@ -177,14 +177,14 @@ impl<'a, 'ctx> TypeScriptNamespace<'a, 'ctx> { TSModuleDeclarationBody::TSModuleDeclaration(declaration) => { let declaration = Declaration::TSModuleDeclaration(declaration); let export_named_decl = - self.ctx.ast.plain_export_named_declaration_declaration(SPAN, declaration); + ctx.ast.plain_export_named_declaration_declaration(SPAN, declaration); let stmt = Statement::ExportNamedDeclaration(export_named_decl); - directives = self.ctx.ast.vec(); - namespace_top_level = self.ctx.ast.vec1(stmt); + directives = ctx.ast.vec(); + namespace_top_level = ctx.ast.vec1(stmt); } } - let mut new_stmts = self.ctx.ast.vec(); + let mut new_stmts = ctx.ast.vec(); for stmt in namespace_top_level { match stmt { @@ -197,9 +197,10 @@ impl<'a, 'ctx> TypeScriptNamespace<'a, 'ctx> { let module_name = decl.id.name().clone(); if let Some(transformed) = self.handle_nested(decl.unbox(), None, ctx) { if names.insert(module_name.clone()) { - new_stmts.push(Statement::from( - self.create_variable_declaration(module_name.clone()), - )); + new_stmts.push(Statement::from(Self::create_variable_declaration( + module_name.clone(), + ctx, + ))); } new_stmts.push(transformed); } @@ -217,11 +218,12 @@ impl<'a, 'ctx> TypeScriptNamespace<'a, 'ctx> { Declaration::TSEnumDeclaration(_) | Declaration::FunctionDeclaration(_) | Declaration::ClassDeclaration(_) => { - self.add_declaration( + Self::add_declaration( decl, name.clone(), &mut names, &mut new_stmts, + ctx, ); } Declaration::VariableDeclaration(var_decl) => { @@ -231,7 +233,7 @@ impl<'a, 'ctx> TypeScriptNamespace<'a, 'ctx> { } }); let stmts = - self.handle_variable_declaration(var_decl, name.clone()); + Self::handle_variable_declaration(var_decl, name.clone(), ctx); new_stmts.extend(stmts); } Declaration::TSModuleDeclaration(module_decl) => { @@ -243,12 +245,15 @@ impl<'a, 'ctx> TypeScriptNamespace<'a, 'ctx> { let module_name = module_decl.id.name().clone(); if let Some(transformed) = self.handle_nested( module_decl.unbox(), - Some(self.ctx.ast.expression_identifier_reference(SPAN, &name)), + Some(ctx.ast.expression_identifier_reference(SPAN, &name)), ctx, ) { if names.insert(module_name.clone()) { new_stmts.push(Statement::from( - self.create_variable_declaration(module_name.clone()), + Self::create_variable_declaration( + module_name.clone(), + ctx, + ), )); } new_stmts.push(transformed); @@ -281,7 +286,7 @@ impl<'a, 'ctx> TypeScriptNamespace<'a, 'ctx> { return None; } - Some(self.transform_namespace( + Some(Self::transform_namespace( name, real_name, new_stmts, @@ -294,46 +299,40 @@ impl<'a, 'ctx> TypeScriptNamespace<'a, 'ctx> { // `namespace Foo { }` -> `let Foo; (function (_Foo) { })(Foo || (Foo = {}));` // ^^^^^^^ - fn create_variable_declaration(&self, name: Atom<'a>) -> Declaration<'a> { + fn create_variable_declaration(name: Atom<'a>, ctx: &TraverseCtx<'a>) -> Declaration<'a> { let kind = VariableDeclarationKind::Let; let declarations = { - let pattern_kind = self.ctx.ast.binding_pattern_kind_binding_identifier(SPAN, name); - let binding = self.ctx.ast.binding_pattern(pattern_kind, NONE, false); - let decl = self.ctx.ast.variable_declarator(SPAN, kind, binding, None, false); - self.ctx.ast.vec1(decl) + let pattern_kind = ctx.ast.binding_pattern_kind_binding_identifier(SPAN, name); + let binding = ctx.ast.binding_pattern(pattern_kind, NONE, false); + let decl = ctx.ast.variable_declarator(SPAN, kind, binding, None, false); + ctx.ast.vec1(decl) }; - self.ctx.ast.declaration_variable(SPAN, kind, declarations, false) + ctx.ast.declaration_variable(SPAN, kind, declarations, false) } // `namespace Foo { }` -> `let Foo; (function (_Foo) { })(Foo || (Foo = {}));` // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #[allow(clippy::needless_pass_by_value, clippy::too_many_arguments)] fn transform_namespace( - &self, arg_name: Atom<'a>, real_name: Atom<'a>, stmts: Vec<'a, Statement<'a>>, directives: Vec<'a, Directive<'a>>, parent_export: Option>, scope_id: ScopeId, - ctx: &mut TraverseCtx, + ctx: &mut TraverseCtx<'a>, ) -> Statement<'a> { // `(function (_N) { var x; })(N || (N = {}))`; // ^^^^^^^^^^^^^^^^^^^^^^^^^^ let callee = { - let body = self.ctx.ast.function_body(SPAN, directives, stmts); + let body = ctx.ast.function_body(SPAN, directives, stmts); let params = { - let ident = self.ctx.ast.binding_pattern_kind_binding_identifier(SPAN, arg_name); - let pattern = self.ctx.ast.binding_pattern(ident, NONE, false); - let items = self.ctx.ast.vec1(self.ctx.ast.plain_formal_parameter(SPAN, pattern)); - self.ctx.ast.formal_parameters( - SPAN, - FormalParameterKind::FormalParameter, - items, - NONE, - ) + let ident = ctx.ast.binding_pattern_kind_binding_identifier(SPAN, arg_name); + let pattern = ctx.ast.binding_pattern(ident, NONE, false); + let items = ctx.ast.vec1(ctx.ast.plain_formal_parameter(SPAN, pattern)); + ctx.ast.formal_parameters(SPAN, FormalParameterKind::FormalParameter, items, NONE) }; - let function = self.ctx.ast.plain_function( + let function = ctx.ast.plain_function( FunctionType::FunctionExpression, SPAN, None, @@ -343,8 +342,8 @@ impl<'a, 'ctx> TypeScriptNamespace<'a, 'ctx> { function.scope_id.set(Some(scope_id)); *ctx.scopes_mut().get_flags_mut(scope_id) = ScopeFlags::Function | ScopeFlags::StrictMode; - let function_expr = self.ctx.ast.expression_from_function(function); - self.ctx.ast.expression_parenthesized(SPAN, function_expr) + let function_expr = ctx.ast.expression_from_function(function); + ctx.ast.expression_parenthesized(SPAN, function_expr) }; // (function (_N) { var M; (function (_M) { var x; })(M || (M = _N.M || (_N.M = {})));})(N || (N = {})); @@ -352,16 +351,16 @@ impl<'a, 'ctx> TypeScriptNamespace<'a, 'ctx> { // Nested namespace arguments Normal namespace arguments let arguments = { // M - let logical_left = self.ctx.ast.expression_identifier_reference(SPAN, &real_name); + let logical_left = ctx.ast.expression_identifier_reference(SPAN, &real_name); // (_N.M = {}) or (N = {}) let mut logical_right = { // _N.M // SAFETY: `ast.copy` is unsound! We need to fix. - let parent_export = unsafe { self.ctx.ast.copy(&parent_export) }; + let parent_export = unsafe { ctx.ast.copy(&parent_export) }; let assign_left = if let Some(parent_export) = parent_export { - self.ctx.ast.simple_assignment_target_member_expression( - self.ctx.ast.member_expression_static( + ctx.ast.simple_assignment_target_member_expression( + ctx.ast.member_expression_static( SPAN, parent_export, IdentifierName::new(SPAN, real_name.clone()), @@ -370,85 +369,87 @@ impl<'a, 'ctx> TypeScriptNamespace<'a, 'ctx> { ) } else { // _N - self.ctx - .ast - .simple_assignment_target_identifier_reference(SPAN, real_name.clone()) + ctx.ast.simple_assignment_target_identifier_reference(SPAN, real_name.clone()) }; - let assign_right = self.ctx.ast.expression_object(SPAN, self.ctx.ast.vec(), None); + let assign_right = ctx.ast.expression_object(SPAN, ctx.ast.vec(), None); let op = AssignmentOperator::Assign; - let assign_expr = self.ctx.ast.expression_assignment( + let assign_expr = ctx.ast.expression_assignment( SPAN, op, - self.ctx.ast.assignment_target_simple(assign_left), + ctx.ast.assignment_target_simple(assign_left), assign_right, ); - self.ctx.ast.expression_parenthesized(SPAN, assign_expr) + ctx.ast.expression_parenthesized(SPAN, assign_expr) }; // (M = _N.M || (_N.M = {})) if let Some(parent_export) = parent_export { let assign_left = - self.ctx.ast.simple_assignment_target_identifier_reference(SPAN, &real_name); + ctx.ast.simple_assignment_target_identifier_reference(SPAN, &real_name); let assign_right = { let property = IdentifierName::new(SPAN, real_name.clone()); let logical_left = - self.ctx.ast.member_expression_static(SPAN, parent_export, property, false); + ctx.ast.member_expression_static(SPAN, parent_export, property, false); let op = LogicalOperator::Or; - self.ctx.ast.expression_logical(SPAN, logical_left.into(), op, logical_right) + ctx.ast.expression_logical(SPAN, logical_left.into(), op, logical_right) }; let op = AssignmentOperator::Assign; logical_right = - self.ctx.ast.expression_assignment(SPAN, op, assign_left.into(), assign_right); - logical_right = self.ctx.ast.expression_parenthesized(SPAN, logical_right); + ctx.ast.expression_assignment(SPAN, op, assign_left.into(), assign_right); + logical_right = ctx.ast.expression_parenthesized(SPAN, logical_right); } let op = LogicalOperator::Or; - let expr = self.ctx.ast.expression_logical(SPAN, logical_left, op, logical_right); - self.ctx.ast.vec1(self.ctx.ast.argument_expression(expr)) + let expr = ctx.ast.expression_logical(SPAN, logical_left, op, logical_right); + ctx.ast.vec1(ctx.ast.argument_expression(expr)) }; - let expr = self.ctx.ast.expression_call(SPAN, callee, NONE, arguments, false); - self.ctx.ast.statement_expression(SPAN, expr) + let expr = ctx.ast.expression_call(SPAN, callee, NONE, arguments, false); + ctx.ast.statement_expression(SPAN, expr) } /// Add assignment statement for decl id /// function id() {} -> function id() {}; Name.id = id; fn add_declaration( - &self, decl: Declaration<'a>, name: Atom<'a>, names: &mut FxHashSet>, new_stmts: &mut Vec<'a, Statement<'a>>, + ctx: &TraverseCtx<'a>, ) { // This function is only called with a function, class, or enum declaration, // all of which are guaranteed to have an `id` let ident = decl.id().unwrap(); let item_name = ident.name.clone(); new_stmts.push(Statement::from(decl)); - let assignment_statement = self.create_assignment_statement(name, item_name.clone()); - let assignment_statement = self.ctx.ast.statement_expression(SPAN, assignment_statement); + let assignment_statement = Self::create_assignment_statement(name, item_name.clone(), ctx); + let assignment_statement = ctx.ast.statement_expression(SPAN, assignment_statement); new_stmts.push(assignment_statement); names.insert(item_name); } // name.item_name = item_name - fn create_assignment_statement(&self, name: Atom<'a>, item_name: Atom<'a>) -> Expression<'a> { - let object = self.ctx.ast.expression_identifier_reference(SPAN, name); - let property = self.ctx.ast.identifier_name(SPAN, &item_name); - let left = self.ctx.ast.member_expression_static(SPAN, object, property, false); + fn create_assignment_statement( + name: Atom<'a>, + item_name: Atom<'a>, + ctx: &TraverseCtx<'a>, + ) -> Expression<'a> { + let object = ctx.ast.expression_identifier_reference(SPAN, name); + let property = ctx.ast.identifier_name(SPAN, &item_name); + let left = ctx.ast.member_expression_static(SPAN, object, property, false); let left = AssignmentTarget::from(left); - let right = self.ctx.ast.expression_identifier_reference(SPAN, item_name); + let right = ctx.ast.expression_identifier_reference(SPAN, item_name); let op = AssignmentOperator::Assign; - self.ctx.ast.expression_assignment(SPAN, op, left, right) + ctx.ast.expression_assignment(SPAN, op, left, right) } /// Convert `export const foo = 1` to `Namespace.foo = 1`; #[allow(clippy::needless_pass_by_value)] fn handle_variable_declaration( - &self, mut var_decl: Box<'a, VariableDeclaration<'a>>, name: Atom<'a>, + ctx: &TraverseCtx<'a>, ) -> Vec<'a, Statement<'a>> { let is_all_binding_identifier = var_decl .declarations @@ -464,41 +465,38 @@ impl<'a, 'ctx> TypeScriptNamespace<'a, 'ctx> { }; if let Some(init) = &mut declarator.init { declarator.init = Some( - self.ctx.ast.expression_assignment( + ctx.ast.expression_assignment( SPAN, AssignmentOperator::Assign, - self.ctx - .ast + ctx.ast .simple_assignment_target_member_expression( - self.ctx.ast.member_expression_static( + ctx.ast.member_expression_static( SPAN, - self.ctx.ast.expression_identifier_reference(SPAN, &name), - self.ctx.ast.identifier_name(SPAN, property_name), + ctx.ast.expression_identifier_reference(SPAN, &name), + ctx.ast.identifier_name(SPAN, property_name), false, ), ) .into(), - self.ctx.ast.move_expression(init), + ctx.ast.move_expression(init), ), ); } }); - return self.ctx.ast.vec1(Statement::VariableDeclaration(var_decl)); + return ctx.ast.vec1(Statement::VariableDeclaration(var_decl)); } // Now we have pattern in declarators // `export const [a] = 1` transforms to `const [a] = 1; N.a = a` - let mut assignments = self.ctx.ast.vec(); + let mut assignments = ctx.ast.vec(); var_decl.bound_names(&mut |id| { - assignments.push(self.create_assignment_statement(name.clone(), id.name.clone())); + assignments.push(Self::create_assignment_statement(name.clone(), id.name.clone(), ctx)); }); - let mut stmts = self.ctx.ast.vec_with_capacity(2); + let mut stmts = ctx.ast.vec_with_capacity(2); stmts.push(Statement::VariableDeclaration(var_decl)); stmts.push( - self.ctx - .ast - .statement_expression(SPAN, self.ctx.ast.expression_sequence(SPAN, assignments)), + ctx.ast.statement_expression(SPAN, ctx.ast.expression_sequence(SPAN, assignments)), ); stmts }