diff --git a/crates/swc_ecma_transforms_module/src/amd.rs b/crates/swc_ecma_transforms_module/src/amd.rs index 7fdcd93f0f1..c4f3d136f61 100644 --- a/crates/swc_ecma_transforms_module/src/amd.rs +++ b/crates/swc_ecma_transforms_module/src/amd.rs @@ -20,8 +20,8 @@ use crate::{ module_ref_rewriter::{ImportMap, ModuleRefRewriter}, path::{ImportResolver, Resolver}, util::{ - clone_first_use_directive, define_es_module, emit_export_stmts, local_name_for_src, - use_strict, ImportInterop, + define_es_module, emit_export_stmts, local_name_for_src, use_strict, ImportInterop, + VecStmtLike, }, }; @@ -143,9 +143,19 @@ where let mut stmts: Vec = Vec::with_capacity(n.body.len() + 4); + // Collect directives + stmts.extend( + &mut n + .body + .iter_mut() + .take_while(|i| i.directive_continue()) + .map(|i| i.take()) + .map(ModuleItem::expect_stmt), + ); + // "use strict"; - if self.config.strict_mode { - stmts.push(clone_first_use_directive(&n.body, true).unwrap_or_else(use_strict)); + if self.config.strict_mode && !stmts.has_use_strict() { + stmts.push(use_strict()); } let ModuleDeclStrip { @@ -170,7 +180,7 @@ where ); stmts.extend(n.body.take().into_iter().filter_map(|item| match item { - ModuleItem::Stmt(stmt) if !stmt.is_directive() => Some(stmt), + ModuleItem::Stmt(stmt) if !stmt.is_empty() => Some(stmt), _ => None, })); diff --git a/crates/swc_ecma_transforms_module/src/common_js.rs b/crates/swc_ecma_transforms_module/src/common_js.rs index 1338c9eac8a..b827a6a82b5 100644 --- a/crates/swc_ecma_transforms_module/src/common_js.rs +++ b/crates/swc_ecma_transforms_module/src/common_js.rs @@ -16,8 +16,8 @@ use crate::{ module_ref_rewriter::{ImportMap, ModuleRefRewriter}, path::{ImportResolver, Resolver}, util::{ - clone_first_use_directive, define_es_module, emit_export_stmts, local_name_for_src, - prop_name, use_strict, ImportInterop, ObjPropKeyIdent, + define_es_module, emit_export_stmts, local_name_for_src, prop_name, use_strict, + ImportInterop, ObjPropKeyIdent, VecStmtLike, }, }; @@ -113,15 +113,18 @@ where let mut stmts: Vec = Vec::with_capacity(n.body.len() + 6); - stmts.extend(clone_first_use_directive(&n.body, false).map(From::from)); + // Collect directives + stmts.extend( + &mut n + .body + .iter_mut() + .take_while(|i| i.directive_continue()) + .map(|i| i.take()), + ); // "use strict"; - if self.config.strict_mode { - stmts.push( - clone_first_use_directive(&n.body, true) - .unwrap_or_else(use_strict) - .into(), - ); + if self.config.strict_mode && !stmts.has_use_strict() { + stmts.push(use_strict().into()); } let ModuleDeclStrip { @@ -156,7 +159,7 @@ where ); stmts.extend(n.body.take().into_iter().filter(|item| match item { - ModuleItem::Stmt(stmt) => !stmt.is_directive(), + ModuleItem::Stmt(stmt) => !stmt.is_empty(), _ => false, })); diff --git a/crates/swc_ecma_transforms_module/src/umd.rs b/crates/swc_ecma_transforms_module/src/umd.rs index 3749acddaa1..59d09bdb5ae 100644 --- a/crates/swc_ecma_transforms_module/src/umd.rs +++ b/crates/swc_ecma_transforms_module/src/umd.rs @@ -17,8 +17,8 @@ use crate::{ module_ref_rewriter::{ImportMap, ModuleRefRewriter}, path::{ImportResolver, Resolver}, util::{ - clone_first_use_directive, define_es_module, emit_export_stmts, local_name_for_src, - use_strict, ImportInterop, + define_es_module, emit_export_stmts, local_name_for_src, use_strict, ImportInterop, + VecStmtLike, }, }; @@ -116,9 +116,18 @@ where let mut stmts: Vec = Vec::with_capacity(module_items.len() + 4); + // Collect directives + stmts.extend( + module_items + .iter_mut() + .take_while(|i| i.directive_continue()) + .map(|i| i.take()) + .map(ModuleItem::expect_stmt), + ); + // "use strict"; - if self.config.config.strict_mode { - stmts.push(clone_first_use_directive(module_items, true).unwrap_or_else(use_strict)); + if self.config.config.strict_mode && !stmts.has_use_strict() { + stmts.push(use_strict()); } let ModuleDeclStrip { @@ -143,7 +152,7 @@ where ); stmts.extend(module_items.take().into_iter().filter_map(|i| match i { - ModuleItem::Stmt(stmt) if !stmt.is_directive() => Some(stmt), + ModuleItem::Stmt(stmt) if !stmt.is_empty() => Some(stmt), _ => None, })); diff --git a/crates/swc_ecma_transforms_module/src/util.rs b/crates/swc_ecma_transforms_module/src/util.rs index 823f9a2ef00..7ffbdda9f73 100644 --- a/crates/swc_ecma_transforms_module/src/util.rs +++ b/crates/swc_ecma_transforms_module/src/util.rs @@ -194,26 +194,33 @@ pub(super) fn define_es_module(exports: Ident) -> Stmt { .into_stmt() } -pub(super) fn clone_first_use_directive( - stmts: &[ModuleItem], - want_use_strict: bool, -) -> Option { - if stmts.is_empty() { - return None; - } +pub(super) trait VecStmtLike { + type StmtLike: IsDirective; - stmts.iter().find_map(|item| match item { - ModuleItem::Stmt(stmt) => { - if (want_use_strict && stmt.is_use_strict()) - || (!want_use_strict && !stmt.is_use_strict() && stmt.is_directive()) - { - Some(stmt.clone()) - } else { - None - } - } - _ => None, - }) + fn as_ref(&self) -> &[Self::StmtLike]; + + fn has_use_strict(&self) -> bool { + self.as_ref() + .iter() + .take_while(|s| s.directive_continue()) + .any(IsDirective::is_use_strict) + } +} + +impl VecStmtLike for [ModuleItem] { + type StmtLike = ModuleItem; + + fn as_ref(&self) -> &[Self::StmtLike] { + self + } +} + +impl VecStmtLike for [Stmt] { + type StmtLike = Stmt; + + fn as_ref(&self) -> &[Self::StmtLike] { + self + } } pub(super) fn use_strict() -> Stmt { diff --git a/crates/swc_ecma_transforms_module/tests/fixture/common/issue-7315/output.amd.js b/crates/swc_ecma_transforms_module/tests/fixture/common/issue-7315/output.amd.js index 61a79f599bf..b826cb60eee 100644 --- a/crates/swc_ecma_transforms_module/tests/fixture/common/issue-7315/output.amd.js +++ b/crates/swc_ecma_transforms_module/tests/fixture/common/issue-7315/output.amd.js @@ -3,6 +3,7 @@ define([ "require", "exports" ], function(require, exports) { + "use client"; "use strict"; Object.defineProperty(exports, "__esModule", { value: true diff --git a/crates/swc_ecma_transforms_module/tests/fixture/common/issue-7315/output.umd.js b/crates/swc_ecma_transforms_module/tests/fixture/common/issue-7315/output.umd.js index 85a8c5b27a7..69d0b73bbe3 100644 --- a/crates/swc_ecma_transforms_module/tests/fixture/common/issue-7315/output.umd.js +++ b/crates/swc_ecma_transforms_module/tests/fixture/common/issue-7315/output.umd.js @@ -6,6 +6,7 @@ ], factory); else if (global = typeof globalThis !== "undefined" ? globalThis : global || self) factory(global.input = {}); })(this, function(exports) { + "use client"; "use strict"; Object.defineProperty(exports, "__esModule", { value: true diff --git a/crates/swc_ecma_transforms_module/tests/fixture/common/issue-8047/input.js b/crates/swc_ecma_transforms_module/tests/fixture/common/issue-8047/input.js new file mode 100644 index 00000000000..b3af62419df --- /dev/null +++ b/crates/swc_ecma_transforms_module/tests/fixture/common/issue-8047/input.js @@ -0,0 +1,12 @@ +"use client"; + +"foo"; + +"use bar"; + +"use strict"; +// All above are directives + +function foo() { } + +"use hello"; // This is not directive diff --git a/crates/swc_ecma_transforms_module/tests/fixture/common/issue-8047/output.amd.js b/crates/swc_ecma_transforms_module/tests/fixture/common/issue-8047/output.amd.js new file mode 100644 index 00000000000..073e3b1f274 --- /dev/null +++ b/crates/swc_ecma_transforms_module/tests/fixture/common/issue-8047/output.amd.js @@ -0,0 +1,11 @@ +define([ + "require" +], function(require) { + "use client"; + "foo"; + "use bar"; + "use strict"; + // All above are directives + function foo() {} + "use hello"; // This is not directive +}); diff --git a/crates/swc_ecma_transforms_module/tests/fixture/common/issue-8047/output.cjs b/crates/swc_ecma_transforms_module/tests/fixture/common/issue-8047/output.cjs new file mode 100644 index 00000000000..7f88df56ede --- /dev/null +++ b/crates/swc_ecma_transforms_module/tests/fixture/common/issue-8047/output.cjs @@ -0,0 +1,7 @@ +"use client"; +"foo"; +"use bar"; +"use strict"; +// All above are directives +function foo() {} +"use hello"; // This is not directive diff --git a/crates/swc_ecma_transforms_module/tests/fixture/common/issue-8047/output.umd.js b/crates/swc_ecma_transforms_module/tests/fixture/common/issue-8047/output.umd.js new file mode 100644 index 00000000000..40bc5cc7ed6 --- /dev/null +++ b/crates/swc_ecma_transforms_module/tests/fixture/common/issue-8047/output.umd.js @@ -0,0 +1,13 @@ +(function(global, factory) { + if (typeof module === "object" && typeof module.exports === "object") factory(); + else if (typeof define === "function" && define.amd) define([], factory); + else if (global = typeof globalThis !== "undefined" ? globalThis : global || self) factory(); +})(this, function() { + "use client"; + "foo"; + "use bar"; + "use strict"; + // All above are directives + function foo() {} + "use hello"; // This is not directive +}); diff --git a/crates/swc_ecma_utils/src/lib.rs b/crates/swc_ecma_utils/src/lib.rs index 3986f284af9..82f564c9832 100644 --- a/crates/swc_ecma_utils/src/lib.rs +++ b/crates/swc_ecma_utils/src/lib.rs @@ -2262,6 +2262,7 @@ pub fn prepend_stmts( pub trait IsDirective { fn as_ref(&self) -> Option<&Stmt>; + #[deprecated(note = "use directive_continue instead")] fn is_directive(&self) -> bool { match self.as_ref() { Some(Stmt::Expr(expr)) => match &*expr.expr { @@ -2273,6 +2274,15 @@ pub trait IsDirective { _ => false, } } + fn directive_continue(&self) -> bool { + match self.as_ref() { + Some(Stmt::Expr(expr)) => match &*expr.expr { + Expr::Lit(Lit::Str(..)) => true, + _ => false, + }, + _ => false, + } + } fn is_use_strict(&self) -> bool { match self.as_ref() { Some(Stmt::Expr(expr)) => match *expr.expr { @@ -2292,6 +2302,18 @@ impl IsDirective for Stmt { } } +impl IsDirective for ModuleItem { + fn as_ref(&self) -> Option<&Stmt> { + self.as_stmt() + } +} + +impl IsDirective for &ModuleItem { + fn as_ref(&self) -> Option<&Stmt> { + self.as_stmt() + } +} + pub trait IdentExt { fn prefix(&self, prefix: &str) -> Ident;