From 3e9dd88e37fcb99293a3683ea7a62214950f7860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 16 Jun 2023 11:49:11 +0900 Subject: [PATCH] feat(es/module): Preserve custom `use` directives (#7528) **Related issue:** - Closes #7315. --- .../fixture/issues-7xxx/7315/1/input/.swcrc | 5 +++++ .../fixture/issues-7xxx/7315/1/input/index.js | 6 +++++ .../issues-7xxx/7315/1/output/index.js | 14 ++++++++++++ crates/swc_ecma_transforms_module/src/amd.rs | 6 ++--- .../src/common_js.rs | 12 ++++++---- crates/swc_ecma_transforms_module/src/umd.rs | 6 ++--- crates/swc_ecma_transforms_module/src/util.rs | 4 ++-- .../tests/fixture/common/issue-7315/input.js | 6 +++++ .../fixture/common/issue-7315/output.amd.js | 19 ++++++++++++++++ .../fixture/common/issue-7315/output.cjs | 14 ++++++++++++ .../fixture/common/issue-7315/output.umd.js | 22 +++++++++++++++++++ crates/swc_ecma_utils/src/lib.rs | 11 ++++++++++ 12 files changed, 113 insertions(+), 12 deletions(-) create mode 100644 crates/swc/tests/fixture/issues-7xxx/7315/1/input/.swcrc create mode 100644 crates/swc/tests/fixture/issues-7xxx/7315/1/input/index.js create mode 100644 crates/swc/tests/fixture/issues-7xxx/7315/1/output/index.js create mode 100644 crates/swc_ecma_transforms_module/tests/fixture/common/issue-7315/input.js create mode 100644 crates/swc_ecma_transforms_module/tests/fixture/common/issue-7315/output.amd.js create mode 100644 crates/swc_ecma_transforms_module/tests/fixture/common/issue-7315/output.cjs create mode 100644 crates/swc_ecma_transforms_module/tests/fixture/common/issue-7315/output.umd.js diff --git a/crates/swc/tests/fixture/issues-7xxx/7315/1/input/.swcrc b/crates/swc/tests/fixture/issues-7xxx/7315/1/input/.swcrc new file mode 100644 index 000000000000..3f013e729084 --- /dev/null +++ b/crates/swc/tests/fixture/issues-7xxx/7315/1/input/.swcrc @@ -0,0 +1,5 @@ +{ + "module": { + "type": "commonjs" + } +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-7xxx/7315/1/input/index.js b/crates/swc/tests/fixture/issues-7xxx/7315/1/input/index.js new file mode 100644 index 000000000000..81c49848f438 --- /dev/null +++ b/crates/swc/tests/fixture/issues-7xxx/7315/1/input/index.js @@ -0,0 +1,6 @@ +// input.ts +"use client" + +export function ReactClientComponent() { + return "Hello world" +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-7xxx/7315/1/output/index.js b/crates/swc/tests/fixture/issues-7xxx/7315/1/output/index.js new file mode 100644 index 000000000000..633c3dc67f2b --- /dev/null +++ b/crates/swc/tests/fixture/issues-7xxx/7315/1/output/index.js @@ -0,0 +1,14 @@ +// input.ts +"use client"; +Object.defineProperty(exports, "__esModule", { + value: true +}); +Object.defineProperty(exports, "ReactClientComponent", { + enumerable: true, + get: function() { + return ReactClientComponent; + } +}); +function ReactClientComponent() { + return "Hello world"; +} diff --git a/crates/swc_ecma_transforms_module/src/amd.rs b/crates/swc_ecma_transforms_module/src/amd.rs index 7a9144ee8168..f8507ea6d10d 100644 --- a/crates/swc_ecma_transforms_module/src/amd.rs +++ b/crates/swc_ecma_transforms_module/src/amd.rs @@ -20,7 +20,7 @@ use crate::{ module_ref_rewriter::{ImportMap, ModuleRefRewriter}, path::{ImportResolver, Resolver}, util::{ - clone_first_use_strict, define_es_module, emit_export_stmts, local_name_for_src, + clone_first_use_directive, define_es_module, emit_export_stmts, local_name_for_src, use_strict, ImportInterop, }, }; @@ -145,7 +145,7 @@ where // "use strict"; if self.config.strict_mode { - stmts.push(clone_first_use_strict(n).unwrap_or_else(use_strict)); + stmts.push(clone_first_use_directive(n).unwrap_or_else(use_strict)); } let ModuleDeclStrip { @@ -170,7 +170,7 @@ where ); stmts.extend(n.take().into_iter().filter_map(|item| match item { - ModuleItem::Stmt(stmt) if !stmt.is_use_strict() => Some(stmt), + ModuleItem::Stmt(stmt) if !stmt.is_directive() => 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 3ebc5a402b15..b8263c389c58 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_strict, define_es_module, emit_export_stmts, local_name_for_src, prop_name, - use_strict, ImportInterop, ObjPropKeyIdent, + clone_first_use_directive, define_es_module, emit_export_stmts, local_name_for_src, + prop_name, use_strict, ImportInterop, ObjPropKeyIdent, }, }; @@ -115,7 +115,11 @@ where // "use strict"; if self.config.strict_mode { - stmts.push(clone_first_use_strict(n).unwrap_or_else(use_strict).into()); + stmts.push( + clone_first_use_directive(n) + .unwrap_or_else(use_strict) + .into(), + ); } let ModuleDeclStrip { @@ -150,7 +154,7 @@ where ); stmts.extend(n.take().into_iter().filter(|item| match item { - ModuleItem::Stmt(stmt) => !stmt.is_use_strict(), + ModuleItem::Stmt(stmt) => !stmt.is_directive(), _ => false, })); diff --git a/crates/swc_ecma_transforms_module/src/umd.rs b/crates/swc_ecma_transforms_module/src/umd.rs index c9ae9c684579..1b1cfc0818e3 100644 --- a/crates/swc_ecma_transforms_module/src/umd.rs +++ b/crates/swc_ecma_transforms_module/src/umd.rs @@ -18,7 +18,7 @@ use crate::{ module_ref_rewriter::{ImportMap, ModuleRefRewriter}, path::{ImportResolver, Resolver}, util::{ - clone_first_use_strict, define_es_module, emit_export_stmts, local_name_for_src, + clone_first_use_directive, define_es_module, emit_export_stmts, local_name_for_src, use_strict, ImportInterop, }, }; @@ -122,7 +122,7 @@ where // "use strict"; if self.config.config.strict_mode { - stmts.push(clone_first_use_strict(module_items).unwrap_or_else(use_strict)); + stmts.push(clone_first_use_directive(module_items).unwrap_or_else(use_strict)); } let ModuleDeclStrip { @@ -147,7 +147,7 @@ where ); stmts.extend(module_items.take().into_iter().filter_map(|i| match i { - ModuleItem::Stmt(stmt) if !stmt.is_use_strict() => Some(stmt), + ModuleItem::Stmt(stmt) if !stmt.is_directive() => Some(stmt), _ => None, })); diff --git a/crates/swc_ecma_transforms_module/src/util.rs b/crates/swc_ecma_transforms_module/src/util.rs index 4c485f7bc300..2014fe563551 100644 --- a/crates/swc_ecma_transforms_module/src/util.rs +++ b/crates/swc_ecma_transforms_module/src/util.rs @@ -190,14 +190,14 @@ pub(super) fn define_es_module(exports: Ident) -> Stmt { .into_stmt() } -pub(super) fn clone_first_use_strict(stmts: &[ModuleItem]) -> Option { +pub(super) fn clone_first_use_directive(stmts: &[ModuleItem]) -> Option { if stmts.is_empty() { return None; } stmts.iter().find_map(|item| match item { ModuleItem::Stmt(stmt @ Stmt::Expr(ExprStmt { expr, .. })) => match **expr { - Expr::Lit(Lit::Str(Str { ref value, .. })) if value == "use strict" => { + Expr::Lit(Lit::Str(Str { ref value, .. })) if value.starts_with("use ") => { Some(stmt.clone()) } _ => None, diff --git a/crates/swc_ecma_transforms_module/tests/fixture/common/issue-7315/input.js b/crates/swc_ecma_transforms_module/tests/fixture/common/issue-7315/input.js new file mode 100644 index 000000000000..c09b798959ea --- /dev/null +++ b/crates/swc_ecma_transforms_module/tests/fixture/common/issue-7315/input.js @@ -0,0 +1,6 @@ +// input.ts +"use client" + +export function ReactClientComponent() { + return "Hello world" +} 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 new file mode 100644 index 000000000000..e025e079a6e8 --- /dev/null +++ b/crates/swc_ecma_transforms_module/tests/fixture/common/issue-7315/output.amd.js @@ -0,0 +1,19 @@ +// input.ts +define([ + "require", + "exports" +], function(require, exports) { + "use client"; + Object.defineProperty(exports, "__esModule", { + value: true + }); + Object.defineProperty(exports, "ReactClientComponent", { + enumerable: true, + get: function() { + return ReactClientComponent; + } + }); + function ReactClientComponent() { + return "Hello world"; + } +}); diff --git a/crates/swc_ecma_transforms_module/tests/fixture/common/issue-7315/output.cjs b/crates/swc_ecma_transforms_module/tests/fixture/common/issue-7315/output.cjs new file mode 100644 index 000000000000..633c3dc67f2b --- /dev/null +++ b/crates/swc_ecma_transforms_module/tests/fixture/common/issue-7315/output.cjs @@ -0,0 +1,14 @@ +// input.ts +"use client"; +Object.defineProperty(exports, "__esModule", { + value: true +}); +Object.defineProperty(exports, "ReactClientComponent", { + enumerable: true, + get: function() { + return ReactClientComponent; + } +}); +function ReactClientComponent() { + return "Hello world"; +} 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 new file mode 100644 index 000000000000..93d5611e8f9e --- /dev/null +++ b/crates/swc_ecma_transforms_module/tests/fixture/common/issue-7315/output.umd.js @@ -0,0 +1,22 @@ +// input.ts +(function(global, factory) { + if (typeof module === "object" && typeof module.exports === "object") factory(exports); + else if (typeof define === "function" && define.amd) define([ + "exports" + ], factory); + else if (global = typeof globalThis !== "undefined" ? globalThis : global || self) factory(global.input = {}); +})(this, function(exports) { + "use client"; + Object.defineProperty(exports, "__esModule", { + value: true + }); + Object.defineProperty(exports, "ReactClientComponent", { + enumerable: true, + get: function() { + return ReactClientComponent; + } + }); + function ReactClientComponent() { + return "Hello world"; + } +}); diff --git a/crates/swc_ecma_utils/src/lib.rs b/crates/swc_ecma_utils/src/lib.rs index 9febb3d6f3f2..b72bcb3339b8 100644 --- a/crates/swc_ecma_utils/src/lib.rs +++ b/crates/swc_ecma_utils/src/lib.rs @@ -2267,6 +2267,17 @@ pub fn prepend_stmts( pub trait IsDirective { fn as_ref(&self) -> Option<&Stmt>; + fn is_directive(&self) -> bool { + match self.as_ref() { + Some(Stmt::Expr(expr)) => match &*expr.expr { + Expr::Lit(Lit::Str(Str { + raw: Some(value), .. + })) => value.starts_with("\"use ") || value.starts_with("'use "), + _ => false, + }, + _ => false, + } + } fn is_use_strict(&self) -> bool { match self.as_ref() { Some(Stmt::Expr(expr)) => match *expr.expr {