Repository: swc-project/swc (rust)

Issue:
Add `use strict` while preserving directives 
### Describe the bug

Follow-up of https://github.com/swc-project/swc/pull/7528

https://github.com/swc-project/swc/pull/7528#discussion_r1231746027

Reported by @magic-akari

### Input code

_No response_

### Config

_No response_

### Playground link

_No response_

### Expected behavior

`"use strict"` should be added even if there's a custom directive

### Actual behavior

_No response_

### Version

Current main (unreleased)

### Additional context

_No response_

Pull request description:
fix(es/module): Add `"use strict"` while preserving directives
**Description:**


**Related issue:**

 - Closes #7536.

Patch:
diff --git a/crates/swc_ecma_transforms_module/src/amd.rs b/crates/swc_ecma_transforms_module/src/amd.rs
index f8507ea6d1..9b4149ee9d 100644
--- a/crates/swc_ecma_transforms_module/src/amd.rs
+++ b/crates/swc_ecma_transforms_module/src/amd.rs
@@ -145,7 +145,7 @@ where
 
         // "use strict";
         if self.config.strict_mode {
-            stmts.push(clone_first_use_directive(n).unwrap_or_else(use_strict));
+            stmts.push(clone_first_use_directive(n, true).unwrap_or_else(use_strict));
         }
 
         let ModuleDeclStrip {
diff --git a/crates/swc_ecma_transforms_module/src/common_js.rs b/crates/swc_ecma_transforms_module/src/common_js.rs
index b8263c389c..4654f3654f 100644
--- a/crates/swc_ecma_transforms_module/src/common_js.rs
+++ b/crates/swc_ecma_transforms_module/src/common_js.rs
@@ -111,12 +111,14 @@ where
         let mut strip = ModuleDeclStrip::new(self.const_var_kind);
         n.visit_mut_with(&mut strip);
 
-        let mut stmts: Vec<ModuleItem> = Vec::with_capacity(n.len() + 4);
+        let mut stmts: Vec<ModuleItem> = Vec::with_capacity(n.len() + 6);
+
+        stmts.extend(clone_first_use_directive(n, false).map(From::from));
 
         // "use strict";
         if self.config.strict_mode {
             stmts.push(
-                clone_first_use_directive(n)
+                clone_first_use_directive(n, true)
                     .unwrap_or_else(use_strict)
                     .into(),
             );
diff --git a/crates/swc_ecma_transforms_module/src/umd.rs b/crates/swc_ecma_transforms_module/src/umd.rs
index 1b1cfc0818..8bd9f1b213 100644
--- a/crates/swc_ecma_transforms_module/src/umd.rs
+++ b/crates/swc_ecma_transforms_module/src/umd.rs
@@ -122,7 +122,7 @@ where
 
         // "use strict";
         if self.config.config.strict_mode {
-            stmts.push(clone_first_use_directive(module_items).unwrap_or_else(use_strict));
+            stmts.push(clone_first_use_directive(module_items, true).unwrap_or_else(use_strict));
         }
 
         let ModuleDeclStrip {
diff --git a/crates/swc_ecma_transforms_module/src/util.rs b/crates/swc_ecma_transforms_module/src/util.rs
index 2014fe5635..ee607d1b95 100644
--- a/crates/swc_ecma_transforms_module/src/util.rs
+++ b/crates/swc_ecma_transforms_module/src/util.rs
@@ -6,7 +6,7 @@ use swc_common::{Span, Spanned, DUMMY_SP};
 use swc_ecma_ast::*;
 use swc_ecma_utils::{
     is_valid_prop_ident, member_expr, private_ident, quote_ident, quote_str, ExprFactory,
-    FunctionFactory,
+    FunctionFactory, IsDirective,
 };
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -190,18 +190,24 @@ pub(super) fn define_es_module(exports: Ident) -> Stmt {
     .into_stmt()
 }
 
-pub(super) fn clone_first_use_directive(stmts: &[ModuleItem]) -> Option<Stmt> {
+pub(super) fn clone_first_use_directive(
+    stmts: &[ModuleItem],
+    want_use_strict: bool,
+) -> Option<Stmt> {
     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.starts_with("use ") => {
+        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,
-        },
+        }
         _ => None,
     })
 }


Test patch:
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
index 633c3dc67f..39305984ff 100644
--- a/crates/swc/tests/fixture/issues-7xxx/7315/1/output/index.js
+++ b/crates/swc/tests/fixture/issues-7xxx/7315/1/output/index.js
@@ -1,5 +1,6 @@
 // input.ts
 "use client";
+"use strict";
 Object.defineProperty(exports, "__esModule", {
     value: true
 });
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 e025e079a6..61a79f599b 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,7 +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.cjs b/crates/swc_ecma_transforms_module/tests/fixture/common/issue-7315/output.cjs
index 633c3dc67f..39305984ff 100644
--- 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
@@ -1,5 +1,6 @@
 // input.ts
 "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 93d5611e8f..85a8c5b27a 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,7 +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
     });