diff --git a/crates/oxc_transformer/src/es2015/arrow_functions.rs b/crates/oxc_transformer/src/es2015/arrow_functions.rs index 4843258123df9..12b0f56b9ea05 100644 --- a/crates/oxc_transformer/src/es2015/arrow_functions.rs +++ b/crates/oxc_transformer/src/es2015/arrow_functions.rs @@ -99,6 +99,9 @@ impl<'a> ArrowFunctions<'a> { } impl<'a> Traverse<'a> for ArrowFunctions<'a> { + // Note: No visitors for `TSModuleBlock` because `this` is not legal in TS module blocks. + // + /// Insert `var _this = this;` for the global scope. fn exit_program(&mut self, program: &mut Program<'a>, _ctx: &mut TraverseCtx<'a>) { debug_assert!(self.inside_arrow_function_stack.len() == 1); @@ -168,6 +171,18 @@ impl<'a> Traverse<'a> for ArrowFunctions<'a> { self.inside_arrow_function_stack.pop().unwrap(); } + fn enter_static_block(&mut self, _block: &mut StaticBlock<'a>, _ctx: &mut TraverseCtx<'a>) { + self.this_var_stack.push(None); + // No need to push to `inside_arrow_function_stack` because `enter_class` already pushed `false` + } + + fn exit_static_block(&mut self, block: &mut StaticBlock<'a>, _ctx: &mut TraverseCtx<'a>) { + let this_var = self.this_var_stack.pop().unwrap(); + if let Some(this_var) = this_var { + self.insert_this_var_statement_at_the_top_of_statements(&mut block.body, &this_var); + } + } + fn enter_jsx_element_name( &mut self, element_name: &mut JSXElementName<'a>, @@ -255,13 +270,16 @@ impl<'a> ArrowFunctions<'a> { let target_scope_id = ctx .scopes() .ancestors(ctx.current_scope_id()) + // We're inside arrow function, so parent scope can't be what we're looking for. + // It's either the arrow function, or a block nested within arrow function. .skip(1) .find(|&scope_id| { let scope_flags = ctx.scopes().get_flags(scope_id); - // Function but not arrow function - scope_flags & (ScopeFlags::Function | ScopeFlags::Arrow) == ScopeFlags::Function + scope_flags.intersects( + ScopeFlags::Function | ScopeFlags::Top | ScopeFlags::ClassStaticBlock, + ) && !scope_flags.contains(ScopeFlags::Arrow) }) - .unwrap_or(ctx.scopes().root_scope_id()); + .unwrap(); this_var.replace(BoundIdentifier::new_uid( "this", diff --git a/crates/oxc_transformer/src/es2015/mod.rs b/crates/oxc_transformer/src/es2015/mod.rs index 6500930fa4e41..e1a007dedac9b 100644 --- a/crates/oxc_transformer/src/es2015/mod.rs +++ b/crates/oxc_transformer/src/es2015/mod.rs @@ -95,6 +95,18 @@ impl<'a> Traverse<'a> for ES2015<'a> { } } + fn enter_static_block(&mut self, block: &mut StaticBlock<'a>, ctx: &mut TraverseCtx<'a>) { + if self.options.arrow_function.is_some() { + self.arrow_functions.enter_static_block(block, ctx); + } + } + + fn exit_static_block(&mut self, block: &mut StaticBlock<'a>, ctx: &mut TraverseCtx<'a>) { + if self.options.arrow_function.is_some() { + self.arrow_functions.exit_static_block(block, ctx); + } + } + fn enter_variable_declarator( &mut self, node: &mut VariableDeclarator<'a>, diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index 39f2ca91408fa..bcde55095e51c 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -167,6 +167,14 @@ impl<'a> Traverse<'a> for Transformer<'a> { self.x0_typescript.enter_class_body(body, ctx); } + fn enter_static_block(&mut self, block: &mut StaticBlock<'a>, ctx: &mut TraverseCtx<'a>) { + self.x3_es2015.enter_static_block(block, ctx); + } + + fn exit_static_block(&mut self, block: &mut StaticBlock<'a>, ctx: &mut TraverseCtx<'a>) { + self.x3_es2015.exit_static_block(block, ctx); + } + fn enter_ts_module_declaration( &mut self, decl: &mut TSModuleDeclaration<'a>, diff --git a/tasks/transform_conformance/oxc.snap.md b/tasks/transform_conformance/oxc.snap.md index c1c31d7dbd9f4..afe94fbacf4ee 100644 --- a/tasks/transform_conformance/oxc.snap.md +++ b/tasks/transform_conformance/oxc.snap.md @@ -1,6 +1,6 @@ commit: 3bcfee23 -Passed: 43/53 +Passed: 44/54 # All Passed: * babel-plugin-transform-nullish-coalescing-operator diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-arrow-functions/test/fixtures/arrow-in-class-static-block/input.js b/tasks/transform_conformance/tests/babel-plugin-transform-arrow-functions/test/fixtures/arrow-in-class-static-block/input.js new file mode 100644 index 0000000000000..b97bacf168224 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-arrow-functions/test/fixtures/arrow-in-class-static-block/input.js @@ -0,0 +1,6 @@ +let f; +class C { + static { + f = () => this; + } +} diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-arrow-functions/test/fixtures/arrow-in-class-static-block/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-arrow-functions/test/fixtures/arrow-in-class-static-block/output.js new file mode 100644 index 0000000000000..d6799b25c2b1f --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-arrow-functions/test/fixtures/arrow-in-class-static-block/output.js @@ -0,0 +1,9 @@ +let f; +class C { + static { + var _this = this; + f = function() { + return _this; + }; + } +}