From 84742d8894759e391c3e0304ce922ff6e7913b54 Mon Sep 17 00:00:00 2001 From: hi-ogawa <4232207+hi-ogawa@users.noreply.github.com> Date: Wed, 23 Apr 2025 10:07:52 +0000 Subject: [PATCH] fix(transformer/module_runner): fix export default live binding (#10560) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aligning with https://github.com/vitejs/vite/pull/19842 (Hopefully this is the last one 🙂) --- .../src/plugins/module_runner_transform.rs | 185 +++++++++++++----- 1 file changed, 136 insertions(+), 49 deletions(-) diff --git a/crates/oxc_transformer/src/plugins/module_runner_transform.rs b/crates/oxc_transformer/src/plugins/module_runner_transform.rs index bea8273976780..cd7c6851dd9db 100644 --- a/crates/oxc_transformer/src/plugins/module_runner_transform.rs +++ b/crates/oxc_transformer/src/plugins/module_runner_transform.rs @@ -99,6 +99,7 @@ impl<'a> ModuleRunnerTransform<'a> { } const SSR_MODULE_EXPORTS_KEY: Atom<'static> = Atom::new_const("__vite_ssr_exports__"); +const SSR_EXPORT_DEFAULT_KEY: Atom<'static> = Atom::new_const("__vite_ssr_export_default__"); const SSR_IMPORT_KEY: Atom<'static> = Atom::new_const("__vite_ssr_import__"); const SSR_DYNAMIC_IMPORT_KEY: Atom<'static> = Atom::new_const("__vite_ssr_dynamic_import__"); const SSR_EXPORT_ALL_KEY: Atom<'static> = Atom::new_const("__vite_ssr_exportAll__"); @@ -166,7 +167,12 @@ impl<'a> ModuleRunnerTransform<'a> { ); } Statement::ExportDefaultDeclaration(export) => { - Self::transform_export_default_declaration(&mut new_stmts, export, ctx); + Self::transform_export_default_declaration( + &mut new_stmts, + &mut hoist_exports, + export, + ctx, + ); } _ => { new_stmts.push(stmt); @@ -536,11 +542,13 @@ impl<'a> ModuleRunnerTransform<'a> { /// export default function () {} /// export default {} /// // to - /// __vite_ssr_exports__.default = function () {} - /// __vite_ssr_exports__.default = {} + /// Object.defineProperty(__vite_ssr_exports__, 'default', { enumerable: true, configurable: true, get(){ return __vite_ssr_export_default__ } }); + /// const __vite_ssr_export_default__ = function () {} + /// const __vite_ssr_export_default__ = {} /// ``` fn transform_export_default_declaration( new_stmts: &mut ArenaVec<'a, Statement<'a>>, + hoist_exports: &mut Vec>, export: ArenaBox<'a, ExportDefaultDeclaration<'a>>, ctx: &mut TraverseCtx<'a>, ) { @@ -549,28 +557,26 @@ impl<'a> ModuleRunnerTransform<'a> { ExportDefaultDeclarationKind::FunctionDeclaration(mut func) => { if let Some(id) = &func.id { let ident = BoundIdentifier::from_binding_ident(id).create_read_expression(ctx); - new_stmts.extend([ - Statement::FunctionDeclaration(func), - Self::create_export(span, ident, DEFAULT, ctx), - ]); + new_stmts.push(Statement::FunctionDeclaration(func)); + hoist_exports.push(Self::create_export(span, ident, DEFAULT, ctx)); } else { func.r#type = FunctionType::FunctionExpression; let right = Expression::FunctionExpression(func); new_stmts.push(Self::create_export_default_assignment(span, right, ctx)); + hoist_exports.push(Self::create_export_default(span, ctx)); } return; } ExportDefaultDeclarationKind::ClassDeclaration(mut class) => { if let Some(id) = &class.id { let ident = BoundIdentifier::from_binding_ident(id).create_read_expression(ctx); - new_stmts.extend([ - Statement::ClassDeclaration(class), - Self::create_export(span, ident, DEFAULT, ctx), - ]); + new_stmts.push(Statement::ClassDeclaration(class)); + hoist_exports.push(Self::create_export(span, ident, DEFAULT, ctx)); } else { class.r#type = ClassType::ClassExpression; let right = Expression::ClassExpression(class); new_stmts.push(Self::create_export_default_assignment(span, right, ctx)); + hoist_exports.push(Self::create_export_default(span, ctx)); } return; } @@ -582,6 +588,7 @@ impl<'a> ModuleRunnerTransform<'a> { }; new_stmts.push(Self::create_export_default_assignment(span, expr, ctx)); + hoist_exports.push(Self::create_export_default(span, ctx)); } /// Transform import specifiers, and return an imported names object. @@ -780,20 +787,30 @@ impl<'a> ModuleRunnerTransform<'a> { ctx.ast.statement_expression(span, Self::create_define_property(arguments, ctx)) } - // __vite_ssr_exports__.default = right; + fn create_export_default(span: Span, ctx: &mut TraverseCtx<'a>) -> Statement<'a> { + Self::create_export( + span, + ctx.create_unbound_ident_expr(SPAN, SSR_EXPORT_DEFAULT_KEY, ReferenceFlags::Read), + DEFAULT, + ctx, + ) + } + + // const __vite_ssr_export_default__ = right; fn create_export_default_assignment( span: Span, right: Expression<'a>, ctx: &mut TraverseCtx<'a>, ) -> Statement<'a> { - let object = - ctx.create_unbound_ident_expr(SPAN, SSR_MODULE_EXPORTS_KEY, ReferenceFlags::Read); - let property = ctx.ast.identifier_name(SPAN, DEFAULT); - let member = ctx.ast.member_expression_static(SPAN, object, property, false); - let target = AssignmentTarget::from(member); - let operator = AssignmentOperator::Assign; - let assignment = ctx.ast.expression_assignment(SPAN, operator, target, right); - ctx.ast.statement_expression(span, assignment) + let binding = ctx.generate_binding_in_current_scope( + SSR_EXPORT_DEFAULT_KEY, + SymbolFlags::BlockScopedVariable, + ); + let pattern = binding.create_binding_pattern(ctx); + let kind = VariableDeclarationKind::Const; + let declarator = ctx.ast.variable_declarator(SPAN, kind, pattern, Some(right), false); + let declaration = ctx.ast.declaration_variable(span, kind, ctx.ast.vec1(declarator), false); + Statement::from(declaration) } } @@ -1187,7 +1204,19 @@ const __vite_ssr_import_0__ = await __vite_ssr_import__('vue', { importedNames: #[test] fn export_default() { - test_same("export default {}", "__vite_ssr_exports__.default = {};"); + test_same( + "export default {}", + " +Object.defineProperty(__vite_ssr_exports__, 'default', { + enumerable: true, + configurable: true, + get() { + return __vite_ssr_export_default__; + } +}); +const __vite_ssr_export_default__ = {}; +", + ); } #[test] @@ -1436,22 +1465,22 @@ class A extends __vite_ssr_import_0__.Foo {}", export class B extends Foo {} ", " - Object.defineProperty(__vite_ssr_exports__, 'B', { + Object.defineProperty(__vite_ssr_exports__, 'default', { enumerable: true, configurable: true, get() { - return B; + return A; } }); - const __vite_ssr_import_0__ = await __vite_ssr_import__('./dependency', { importedNames: ['Foo'] }); - class A extends __vite_ssr_import_0__.Foo {} - Object.defineProperty(__vite_ssr_exports__, 'default', { + Object.defineProperty(__vite_ssr_exports__, 'B', { enumerable: true, configurable: true, get() { - return A; + return B; } }); + const __vite_ssr_import_0__ = await __vite_ssr_import__('./dependency', { importedNames: ['Foo'] }); + class A extends __vite_ssr_import_0__.Foo {} class B extends __vite_ssr_import_0__.Foo {} ", ); @@ -1461,44 +1490,73 @@ class A extends __vite_ssr_import_0__.Foo {}", #[test] fn should_handle_default_export_variants() { // default anonymous functions - test_same("export default function() {}", "__vite_ssr_exports__.default = function() {};"); + test_same( + "export default function() {}", + " +Object.defineProperty(__vite_ssr_exports__, 'default', { + enumerable: true, + configurable: true, + get() { + return __vite_ssr_export_default__; + } +}); +const __vite_ssr_export_default__ = function() {}; +", + ); // default anonymous class - test_same("export default class {}", "__vite_ssr_exports__.default = class {};"); + test_same( + "export default class {}", + " +Object.defineProperty(__vite_ssr_exports__, 'default', { + enumerable: true, + configurable: true, + get() { + return __vite_ssr_export_default__; + } +}); +const __vite_ssr_export_default__ = class {}; +", + ); // default named functions test_same( - "export default function foo() {}\nfoo.prototype = Object.prototype;", - "function foo() {} + " +export default function foo() {} +foo.prototype = Object.prototype; +", + " Object.defineProperty(__vite_ssr_exports__, 'default', { - enumerable: true, - configurable: true, - get() { - return foo; - } +enumerable: true, +configurable: true, +get() { +return foo; +} }); -foo.prototype = Object.prototype;", +function foo() {} +foo.prototype = Object.prototype; +", ); // default named classes test_same( "export default class A {}\nexport class B extends A {}", " -Object.defineProperty(__vite_ssr_exports__, 'B', { +Object.defineProperty(__vite_ssr_exports__, 'default', { enumerable: true, configurable: true, get() { - return B; + return A; } }); -class A {} -Object.defineProperty(__vite_ssr_exports__, 'default', { +Object.defineProperty(__vite_ssr_exports__, 'B', { enumerable: true, configurable: true, get() { - return A; + return B; } }); +class A {} class B extends A {} ", ); @@ -1830,15 +1888,37 @@ function fn2() {} // return Math.random() // } test_same( - "export default (function getRandom() { - return Math.random(); - });", - "__vite_ssr_exports__.default = function getRandom() { + " +export default (function getRandom() { return Math.random(); -};", +}); +", + " +Object.defineProperty(__vite_ssr_exports__, 'default', { + enumerable: true, + configurable: true, + get() { + return __vite_ssr_export_default__; + } +}); +const __vite_ssr_export_default__ = function getRandom() { + return Math.random(); +}; +", ); - test_same("export default (class A {});", "__vite_ssr_exports__.default = class A {};"); + test_same( + "export default (class A {});", + " +Object.defineProperty(__vite_ssr_exports__, 'default', { + enumerable: true, + configurable: true, + get() { + return __vite_ssr_export_default__; + } +}); +const __vite_ssr_export_default__ = class A {};", + ); } // https://github.com/vitejs/vite/issues/8002 @@ -2085,6 +2165,13 @@ console.log(__vite_ssr_import_1__.foo + 2);", console.log(bar) ", " +Object.defineProperty(__vite_ssr_exports__, 'default', { + enumerable: true, + configurable: true, + get() { + return __vite_ssr_export_default__; + } +}); Object.defineProperty(__vite_ssr_exports__, 'bar', { enumerable: true, configurable: true, @@ -2094,7 +2181,7 @@ Object.defineProperty(__vite_ssr_exports__, 'bar', { }); const __vite_ssr_import_0__ = await __vite_ssr_import__('./foo', { importedNames: ['foo'] }); const __vite_ssr_import_1__ = await __vite_ssr_import__('./bar'); -__vite_ssr_exports__.default = (0, __vite_ssr_import_0__.foo)(); +const __vite_ssr_export_default__ = (0, __vite_ssr_import_0__.foo)(); console.log(bar); ", );