Repository: swc-project/swc (rust)

Issue:
Undefined references if nested namespaces is exported multiple times
### Describe the bug

If a nested namespace is exported multiple times, SWC fails to emit exports for the top-level namespace, leading to undefined reference errors.

### Input code

```typescript
export namespace a.b {
  export interface i {
    baz?: string;
  }
}
export namespace a.b {
  export enum e {
    FOO = "BAR",
  }
}
```


### Config

```json
{
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "tsx": true,
      "decorators": true
    },
    "target": "es2017",
    "loose": false,
    "minify": {
      "compress": false,
      "mangle": false
    }
  },
  "module": {
    "type": "es6"
  },
  "minify": false,
  "isModule": true
}
```


### Playground link

https://play.swc.rs/?version=1.2.136&code=H4sIAAAAAAAAA0utKMgvKlHIS8xNLS5ITE5VSNRLUqjm5VJQSIXIZOaVpBalgWQyIeIKCkmJVfZWCsUlRZl56dYgoVpeLiBKJWRUal5prkIqzBQ3f38FWwUlJ8cgJR2EIQCGAhLskAAAAA%3D%3D&config=H4sIAAAAAAAAA0WOTQrDIBCF7zLrLNouWvAOPcRgJsFiVGYmUBHvXjUp2T3ez8cr8BELpkBCFuKuJAfFLxjQnEgsu6QwgUqzlHeaYCYbGTWyHE5tKfJK2jYkj9v91fo%2BRiEwC3ppk80Ft%2BROt3FLTCJXhGH1%2F2ZtrC3OezfKODCYT6gX49w5eZ%2FF8eEH79iDjMkAAAA%3D

### Expected behavior

SWC generates an
```
export var a;
```
as it does for the case where both namespace declarations are merged.

See https://play.swc.rs/?version=1.2.136&code=H4sIAAAAAAAAA0utKMgvKlHIS8xNLS5ITE5VSNRLUqjm5VJQSIXIZOaVpBalgWQyIeIKCkmJVfZWCsUlRZl56dYgoVpeLn19KJlKyMDUvNJchVSYWW7%2B%2Fgq2CkpOjkFKOlCjgAgAQXQ8AZYAAAA%3D&config=H4sIAAAAAAAAA0WOTQrDIBCF7zLrLNouWvAOPcRgJsFiVGYmUBHvXjUp2T3ez8cr8BELpkBCFuKuJAfFLxjQnEgsu6QwgUqzlHeaYCYbGTWyHE5tKfJK2jYkj9v91fo%2BRiEwC3ppk80Ft%2BROt3FLTCJXhGH1%2F2ZtrC3OezfKODCYT6gX49w5eZ%2FF8eEH79iDjMkAAAA%3D.

### Actual behavior

SWC generates no `export var a;` statement, leading to an undefined reference error at runtime.

### Version

1.2.136

### Additional context

_No response_

Pull request:
fix(es/typescript): export empty namespace
<!-- Note: CI script will automatically rebase your PR so please do not rebase unless required -->

<!-- Thanks for submitting a pull request! Please provide enough information so that others can review your pull request. -->

**Description:**

<!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? -->

**BREAKING CHANGE:**

<!--
If this PR introduces a breaking change, it must contain a notice for it to be included in the CHANGELOG. Add description or remove entirely if not breaking.

You may need to update `MIGRATION.md` for the breaking changes.
-->

**Related issue (if exists):**
- fix #3454 

Patch:
diff --git a/crates/swc_ecma_transforms_typescript/src/strip.rs b/crates/swc_ecma_transforms_typescript/src/strip.rs
index 425f45eafc..e75427f8ea 100644
--- a/crates/swc_ecma_transforms_typescript/src/strip.rs
+++ b/crates/swc_ecma_transforms_typescript/src/strip.rs
@@ -1144,7 +1144,6 @@ where
         };
 
         let body = module.body?;
-        let var = self.create_uninit_var(module_name.span, module_name.to_id());
         let private_name = private_ident!(module_name.sym.clone());
         let body_stmts = match body {
             TsNamespaceBody::TsModuleBlock(block) => {
@@ -1155,6 +1154,8 @@ where
             }
         }?;
 
+        let var = self.create_uninit_var(module_name.span, module_name.to_id());
+
         self.get_namespace_var_decl_and_call_expr(
             var,
             body_stmts,


Test patch:
diff --git a/crates/swc/tests/tsc-references/es6modulekindWithES5Target12_es2015.1.normal.js b/crates/swc/tests/tsc-references/es6modulekindWithES5Target12_es2015.1.normal.js
index 22088586e3..b0a9ba41dd 100644
--- a/crates/swc/tests/tsc-references/es6modulekindWithES5Target12_es2015.1.normal.js
+++ b/crates/swc/tests/tsc-references/es6modulekindWithES5Target12_es2015.1.normal.js
@@ -18,8 +18,9 @@ export var E;
 (function(E) {
     var z = E.z = 1;
 })(E || (E = {}));
-(function(N) {
-    var x = N.x = 1;
+export var N;
+(function(N1) {
+    var x = N1.x = 1;
 })(N || (N = {}));
 export function F() {}
 (function(F1) {
diff --git a/crates/swc/tests/tsc-references/es6modulekindWithES5Target12_es2015.2.minified.js b/crates/swc/tests/tsc-references/es6modulekindWithES5Target12_es2015.2.minified.js
index 219290a3d4..d0b633bff9 100644
--- a/crates/swc/tests/tsc-references/es6modulekindWithES5Target12_es2015.2.minified.js
+++ b/crates/swc/tests/tsc-references/es6modulekindWithES5Target12_es2015.2.minified.js
@@ -1,4 +1,4 @@
-var E;
+var E, N;
 export class C {
 }
 (C || (C = {})).x = 1, (function(E) {
diff --git a/crates/swc/tests/tsc-references/es6modulekindWithES5Target12_es5.1.normal.js b/crates/swc/tests/tsc-references/es6modulekindWithES5Target12_es5.1.normal.js
index 94a5c6f553..2d7b01c811 100644
--- a/crates/swc/tests/tsc-references/es6modulekindWithES5Target12_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/es6modulekindWithES5Target12_es5.1.normal.js
@@ -25,8 +25,9 @@ export var E;
 (function(E) {
     var z = E.z = 1;
 })(E || (E = {}));
-(function(N) {
-    var x = N.x = 1;
+export var N;
+(function(N1) {
+    var x = N1.x = 1;
 })(N || (N = {}));
 export function F() {}
 (function(F1) {
diff --git a/crates/swc/tests/tsc-references/es6modulekindWithES5Target12_es5.2.minified.js b/crates/swc/tests/tsc-references/es6modulekindWithES5Target12_es5.2.minified.js
index 86be133ff5..5bbc0019bf 100644
--- a/crates/swc/tests/tsc-references/es6modulekindWithES5Target12_es5.2.minified.js
+++ b/crates/swc/tests/tsc-references/es6modulekindWithES5Target12_es5.2.minified.js
@@ -1,15 +1,15 @@
-var C, E, E1, N1, F, E2, C1 = function() {
+var C, E, E1, N, F, E2, N1, C1 = function() {
     "use strict";
     !function(instance, Constructor) {
         if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
     }(this, C1);
 };
-export { E1 as E, N1 as N, E2 as E, C1 as C };
+export { E1 as E, E2 as E, N1 as N, C1 as C };
 C = C1 || (C1 = {}), C.x = 1, (function(E2) {
     E2[E2.w = 1] = "w";
 })(E2 || (E2 = {})), (function(E2) {
     E2[E2.x = 2] = "x";
-})(E2 || (E2 = {})), E = E2 || (E2 = {}), E.y = 1, E1 = E2 || (E2 = {}), E1.z = 1, N1 = N || (N = {}), N1.x = 1;
+})(E2 || (E2 = {})), E = E2 || (E2 = {}), E.y = 1, E1 = E2 || (E2 = {}), E1.z = 1, N = N1 || (N1 = {}), N.x = 1;
 function F1() {}
 export { F1 as F };
 F = F1 || (F1 = {}), F.x = 1;
diff --git a/crates/swc/tests/tsc-references/esnextmodulekindWithES5Target12_es2015.1.normal.js b/crates/swc/tests/tsc-references/esnextmodulekindWithES5Target12_es2015.1.normal.js
index 4c144be653..7c7e24f063 100644
--- a/crates/swc/tests/tsc-references/esnextmodulekindWithES5Target12_es2015.1.normal.js
+++ b/crates/swc/tests/tsc-references/esnextmodulekindWithES5Target12_es2015.1.normal.js
@@ -18,8 +18,9 @@ export var E;
 (function(E) {
     var z = E.z = 1;
 })(E || (E = {}));
-(function(N) {
-    var x = N.x = 1;
+export var N;
+(function(N1) {
+    var x = N1.x = 1;
 })(N || (N = {}));
 export function F() {}
 (function(F1) {
diff --git a/crates/swc/tests/tsc-references/esnextmodulekindWithES5Target12_es2015.2.minified.js b/crates/swc/tests/tsc-references/esnextmodulekindWithES5Target12_es2015.2.minified.js
index 219290a3d4..d0b633bff9 100644
--- a/crates/swc/tests/tsc-references/esnextmodulekindWithES5Target12_es2015.2.minified.js
+++ b/crates/swc/tests/tsc-references/esnextmodulekindWithES5Target12_es2015.2.minified.js
@@ -1,4 +1,4 @@
-var E;
+var E, N;
 export class C {
 }
 (C || (C = {})).x = 1, (function(E) {
diff --git a/crates/swc/tests/tsc-references/esnextmodulekindWithES5Target12_es5.1.normal.js b/crates/swc/tests/tsc-references/esnextmodulekindWithES5Target12_es5.1.normal.js
index 8e8f1ec53e..dd59b4a435 100644
--- a/crates/swc/tests/tsc-references/esnextmodulekindWithES5Target12_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/esnextmodulekindWithES5Target12_es5.1.normal.js
@@ -25,8 +25,9 @@ export var E;
 (function(E) {
     var z = E.z = 1;
 })(E || (E = {}));
-(function(N) {
-    var x = N.x = 1;
+export var N;
+(function(N1) {
+    var x = N1.x = 1;
 })(N || (N = {}));
 export function F() {}
 (function(F1) {
diff --git a/crates/swc/tests/tsc-references/esnextmodulekindWithES5Target12_es5.2.minified.js b/crates/swc/tests/tsc-references/esnextmodulekindWithES5Target12_es5.2.minified.js
index 86be133ff5..5bbc0019bf 100644
--- a/crates/swc/tests/tsc-references/esnextmodulekindWithES5Target12_es5.2.minified.js
+++ b/crates/swc/tests/tsc-references/esnextmodulekindWithES5Target12_es5.2.minified.js
@@ -1,15 +1,15 @@
-var C, E, E1, N1, F, E2, C1 = function() {
+var C, E, E1, N, F, E2, N1, C1 = function() {
     "use strict";
     !function(instance, Constructor) {
         if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
     }(this, C1);
 };
-export { E1 as E, N1 as N, E2 as E, C1 as C };
+export { E1 as E, E2 as E, N1 as N, C1 as C };
 C = C1 || (C1 = {}), C.x = 1, (function(E2) {
     E2[E2.w = 1] = "w";
 })(E2 || (E2 = {})), (function(E2) {
     E2[E2.x = 2] = "x";
-})(E2 || (E2 = {})), E = E2 || (E2 = {}), E.y = 1, E1 = E2 || (E2 = {}), E1.z = 1, N1 = N || (N = {}), N1.x = 1;
+})(E2 || (E2 = {})), E = E2 || (E2 = {}), E.y = 1, E1 = E2 || (E2 = {}), E1.z = 1, N = N1 || (N1 = {}), N.x = 1;
 function F1() {}
 export { F1 as F };
 F = F1 || (F1 = {}), F.x = 1;
diff --git a/crates/swc/tests/tsc-references/nestedModules_es2015.1.normal.js b/crates/swc/tests/tsc-references/nestedModules_es2015.1.normal.js
index 0fb2d9d867..b835657364 100644
--- a/crates/swc/tests/tsc-references/nestedModules_es2015.1.normal.js
+++ b/crates/swc/tests/tsc-references/nestedModules_es2015.1.normal.js
@@ -1,18 +1,20 @@
-(function(A) {
+var A;
+(function(A1) {
     let B;
     (function(B) {
         var Point = {
             x: 0,
             y: 0
         }; // bug 832088: could not find module 'C'
-    })(B = A.B || (A.B = {}));
+    })(B = A1.B || (A1.B = {}));
 })(A || (A = {}));
-(function(M2) {
+var M2;
+(function(M21) {
     let X1;
     (function(X) {
         var Point;
         X.Point = Point;
-    })(X1 = M2.X || (M2.X = {}));
+    })(X1 = M21.X || (M21.X = {}));
 })(M2 || (M2 = {}));
 var m = M2.X;
 var point;
diff --git a/crates/swc/tests/tsc-references/nestedModules_es2015.2.minified.js b/crates/swc/tests/tsc-references/nestedModules_es2015.2.minified.js
index 680504f47f..52a3caf8ec 100644
--- a/crates/swc/tests/tsc-references/nestedModules_es2015.2.minified.js
+++ b/crates/swc/tests/tsc-references/nestedModules_es2015.2.minified.js
@@ -1,4 +1,5 @@
-!function(M2) {
+var M2;
+!function(M21) {
     var Point;
-    (M2.X || (M2.X = {})).Point = Point;
+    (M21.X || (M21.X = {})).Point = Point;
 }(M2 || (M2 = {})), M2.X.Point;
diff --git a/crates/swc/tests/tsc-references/nestedModules_es5.1.normal.js b/crates/swc/tests/tsc-references/nestedModules_es5.1.normal.js
index 21b00c2342..85716de4cf 100644
--- a/crates/swc/tests/tsc-references/nestedModules_es5.1.normal.js
+++ b/crates/swc/tests/tsc-references/nestedModules_es5.1.normal.js
@@ -1,18 +1,20 @@
-(function(A) {
+var A;
+(function(A1) {
     var B;
     (function(B) {
         var Point = {
             x: 0,
             y: 0
         }; // bug 832088: could not find module 'C'
-    })(B = A.B || (A.B = {}));
+    })(B = A1.B || (A1.B = {}));
 })(A || (A = {}));
-(function(M2) {
+var M2;
+(function(M21) {
     var X1;
     (function(X) {
         var Point;
         X.Point = Point;
-    })(X1 = M2.X || (M2.X = {}));
+    })(X1 = M21.X || (M21.X = {}));
 })(M2 || (M2 = {}));
 var m = M2.X;
 var point;
diff --git a/crates/swc/tests/tsc-references/nestedModules_es5.2.minified.js b/crates/swc/tests/tsc-references/nestedModules_es5.2.minified.js
index 680504f47f..52a3caf8ec 100644
--- a/crates/swc/tests/tsc-references/nestedModules_es5.2.minified.js
+++ b/crates/swc/tests/tsc-references/nestedModules_es5.2.minified.js
@@ -1,4 +1,5 @@
-!function(M2) {
+var M2;
+!function(M21) {
     var Point;
-    (M2.X || (M2.X = {})).Point = Point;
+    (M21.X || (M21.X = {})).Point = Point;
 }(M2 || (M2 = {})), M2.X.Point;
diff --git a/crates/swc_ecma_transforms_typescript/tests/fixture/issue-3454/1/input.ts b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-3454/1/input.ts
new file mode 100644
index 0000000000..999dbcd235
--- /dev/null
+++ b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-3454/1/input.ts
@@ -0,0 +1,11 @@
+export namespace a.b {
+    export interface i {
+        baz?: string;
+    }
+}
+
+export namespace a.b {
+    export enum e {
+        FOO = "BAR",
+    }
+}
diff --git a/crates/swc_ecma_transforms_typescript/tests/fixture/issue-3454/1/output.js b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-3454/1/output.js
new file mode 100644
index 0000000000..de8d527dfe
--- /dev/null
+++ b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-3454/1/output.js
@@ -0,0 +1,10 @@
+export var a;
+(function(a1) {
+    let b1;
+    (function(b) {
+        let e;
+        (function(e) {
+            e["FOO"] = "BAR";
+        })(e = b.e || (b.e = {}));
+    })(b1 = a1.b || (a1.b = {}));
+})(a || (a = {}));