Repository: swc-project/swc (rust)

Issue:
Assignment to identifier in namespace is incorrectly transpiled
### Describe the bug

When transpiling code with a TS namespace to module=ESM + target=es2017, the namespace is missing in the `AssignmentExpression`.

### Input code

```typescript
namespace API {
  export let url = '/'

  export function update(value: string) {
    url = value
  }
}

API.update('/new')
console.log(API.url)
```


### Config

```json
{
"sourceMaps": true,
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "tsx": true,
      "decorators": true
    },
    "target": "es2017",
    "loose": false,
    "externalHelpers": true,
    "keepClassNames": false
  },
  "module": {
    "type": "es6"
  },
  "minify": false
}
```


### Playground link

https://play.swc.rs/?version=1.3.89&code=H4sIAAAAAAAAA0WMOwrDMBAF%2Bz3F62Q1dh9wkTJdriCUjTFsdoU%2BSSD47jHC4PK9YUbDi0sKkXG93%2FAjgL%2FJcoVwRcuCGW5ydP7PprGupmjpESoP7yCNLyg1r7r4XsAhdrTvjTaiPT8eipuUP85TNC0mPIotQ8dZ%2FB%2BWWPhrkAAAAA%3D%3D&config=H4sIAAAAAAAAA1VPQQ6CQAy88wrSMwf1oIlXL170D80yGnRhN21JIIS%2FuyiI3joznZl2yEhDKw4XjkrH3KRFkeU5PdQlOKQxgciikC9OjPaNcZcYsj5CnVTRqFhU0%2B4n602VcEHYgiwtb2H86GQsd9gUB91ttoc5inwIikTf2OucRegM0rA%2Fw0eI%2FhXRE4gnz6pXrqGLM5ubqA5l67E%2BNh3%2Fad3TulQ11a3%2FescX%2FL2OKyQBAAA%3D

### SWC Info output

_No response_

### Expected behavior

[tsc](https://www.typescriptlang.org/play?#code/HYQwtgpgzgDiDGEAEBBACgSSQbwFBKQgA8YB7AJwBckAbCagV3JqQF4kByAeg930JIVqAMwbB4lAJalgSBjAAmIShAAUANxA0GEAFxIolcpOABzAJQ5+BJi3abtEfgF9cr3OgwA6eUpWruYAgAdw5zXHgZKFI6LxpSU1VPH2ZzIA) generates:
```js
"use strict";
var API;
(function (API) {
    API.url = '/';
    function update(value) {
        API.url = value;   // <-- note the "API."
    }
    API.update = update;
})(API || (API = {}));
API.update('/new');
console.log(API.url);
```

### Actual behavior

SWC generates:

```js
var API;
(function(API) {
    API.url = '/';
    function update(value) {
        url = value;   // <-- invalid
    }
    API.update = update;
})(API || (API = {}));
API.update('/new');
console.log(API.url);
```

### Version

1.3.89

### Additional context

_No response_

Pull request:
fix(es/typescript): Handle qualified access in assign pat
**Related issue:**

 - Closes #8011
 - https://github.com/swc-project/swc/discussions/5186

Patch:
diff --git a/crates/swc_ecma_transforms_typescript/src/transform.rs b/crates/swc_ecma_transforms_typescript/src/transform.rs
index ecb3b904a9..1f98738d7b 100644
--- a/crates/swc_ecma_transforms_typescript/src/transform.rs
+++ b/crates/swc_ecma_transforms_typescript/src/transform.rs
@@ -1190,6 +1190,17 @@ struct ExportRefRewrriter {
 impl VisitMut for ExportRefRewrriter {
     noop_visit_mut_type!();
 
+    fn visit_mut_pat(&mut self, n: &mut Pat) {
+        match n {
+            Pat::Ident(BindingIdent { id, .. }) => {
+                if self.export_id_list.contains(&id.to_id()) {
+                    *n = Pat::Expr(Box::new(self.namesapce_id.clone().make_member(id.take())));
+                }
+            }
+            _ => n.visit_mut_children_with(self),
+        }
+    }
+
     fn visit_mut_expr(&mut self, n: &mut Expr) {
         if let Expr::Ident(ref_ident) = n {
             if self.export_id_list.contains(&ref_ident.to_id()) {


Test patch:
diff --git a/crates/swc/tests/tsc-references/typeArgumentsWithStringLiteralTypes01.1.normal.js b/crates/swc/tests/tsc-references/typeArgumentsWithStringLiteralTypes01.1.normal.js
index 90064c092b..d066285b55 100644
--- a/crates/swc/tests/tsc-references/typeArgumentsWithStringLiteralTypes01.1.normal.js
+++ b/crates/swc/tests/tsc-references/typeArgumentsWithStringLiteralTypes01.1.normal.js
@@ -22,23 +22,23 @@ var n1;
     n1.d = fun2("Hello", "Hello");
     n1.e = fun3("Hello", "Hello", "World", "Foo");
     // Should be valid
-    a = takeReturnString(n1.a);
-    b = takeReturnString(n1.b);
-    c = takeReturnString(n1.c);
-    d = takeReturnString(n1.d);
-    e = takeReturnString(n1.e);
+    n1.a = takeReturnString(n1.a);
+    n1.b = takeReturnString(n1.b);
+    n1.c = takeReturnString(n1.c);
+    n1.d = takeReturnString(n1.d);
+    n1.e = takeReturnString(n1.e);
     // Passing these as arguments should cause an error.
-    a = takeReturnHello(n1.a);
-    b = takeReturnHello(n1.b);
-    c = takeReturnHello(n1.c);
-    d = takeReturnHello(n1.d);
-    e = takeReturnHello(n1.e);
+    n1.a = takeReturnHello(n1.a);
+    n1.b = takeReturnHello(n1.b);
+    n1.c = takeReturnHello(n1.c);
+    n1.d = takeReturnHello(n1.d);
+    n1.e = takeReturnHello(n1.e);
     // Passing these as arguments should cause an error.
-    a = takeReturnHelloWorld(n1.a);
-    b = takeReturnHelloWorld(n1.b);
-    c = takeReturnHelloWorld(n1.c);
-    d = takeReturnHelloWorld(n1.d);
-    e = takeReturnHelloWorld(n1.e);
+    n1.a = takeReturnHelloWorld(n1.a);
+    n1.b = takeReturnHelloWorld(n1.b);
+    n1.c = takeReturnHelloWorld(n1.c);
+    n1.d = takeReturnHelloWorld(n1.d);
+    n1.e = takeReturnHelloWorld(n1.e);
 })(n1 || (n1 = {}));
 var n2;
 (function(n2) {
@@ -50,23 +50,23 @@ var n2;
     n2.d = fun2("Hello", "World");
     n2.e = fun3("Hello", "World");
     // Assignment from the returned value should cause an error.
-    a = takeReturnString(n2.a);
-    b = takeReturnString(n2.b);
-    c = takeReturnString(n2.c);
-    d = takeReturnString(n2.d);
-    e = takeReturnString(n2.e);
+    n2.a = takeReturnString(n2.a);
+    n2.b = takeReturnString(n2.b);
+    n2.c = takeReturnString(n2.c);
+    n2.d = takeReturnString(n2.d);
+    n2.e = takeReturnString(n2.e);
     // Should be valid
-    a = takeReturnHello(n2.a);
-    b = takeReturnHello(n2.b);
-    c = takeReturnHello(n2.c);
-    d = takeReturnHello(n2.d);
-    e = takeReturnHello(n2.e);
+    n2.a = takeReturnHello(n2.a);
+    n2.b = takeReturnHello(n2.b);
+    n2.c = takeReturnHello(n2.c);
+    n2.d = takeReturnHello(n2.d);
+    n2.e = takeReturnHello(n2.e);
     // Assignment from the returned value should cause an error.
-    a = takeReturnHelloWorld(n2.a);
-    b = takeReturnHelloWorld(n2.b);
-    c = takeReturnHelloWorld(n2.c);
-    d = takeReturnHelloWorld(n2.d);
-    e = takeReturnHelloWorld(n2.e);
+    n2.a = takeReturnHelloWorld(n2.a);
+    n2.b = takeReturnHelloWorld(n2.b);
+    n2.c = takeReturnHelloWorld(n2.c);
+    n2.d = takeReturnHelloWorld(n2.d);
+    n2.e = takeReturnHelloWorld(n2.e);
 })(n2 || (n2 = {}));
 var n3;
 (function(n3) {
@@ -78,21 +78,21 @@ var n3;
     n3.d = fun2("World", "World");
     n3.e = fun3("Hello", "World");
     // Assignment from the returned value should cause an error.
-    a = takeReturnString(n3.a);
-    b = takeReturnString(n3.b);
-    c = takeReturnString(n3.c);
-    d = takeReturnString(n3.d);
-    e = takeReturnString(n3.e);
+    n3.a = takeReturnString(n3.a);
+    n3.b = takeReturnString(n3.b);
+    n3.c = takeReturnString(n3.c);
+    n3.d = takeReturnString(n3.d);
+    n3.e = takeReturnString(n3.e);
     // Passing these as arguments should cause an error.
-    a = takeReturnHello(n3.a);
-    b = takeReturnHello(n3.b);
-    c = takeReturnHello(n3.c);
-    d = takeReturnHello(n3.d);
-    e = takeReturnHello(n3.e);
+    n3.a = takeReturnHello(n3.a);
+    n3.b = takeReturnHello(n3.b);
+    n3.c = takeReturnHello(n3.c);
+    n3.d = takeReturnHello(n3.d);
+    n3.e = takeReturnHello(n3.e);
     // Both should be valid.
-    a = takeReturnHelloWorld(n3.a);
-    b = takeReturnHelloWorld(n3.b);
-    c = takeReturnHelloWorld(n3.c);
-    d = takeReturnHelloWorld(n3.d);
-    e = takeReturnHelloWorld(n3.e);
+    n3.a = takeReturnHelloWorld(n3.a);
+    n3.b = takeReturnHelloWorld(n3.b);
+    n3.c = takeReturnHelloWorld(n3.c);
+    n3.d = takeReturnHelloWorld(n3.d);
+    n3.e = takeReturnHelloWorld(n3.e);
 })(n3 || (n3 = {}));
diff --git a/crates/swc/tests/tsc-references/typeArgumentsWithStringLiteralTypes01.2.minified.js b/crates/swc/tests/tsc-references/typeArgumentsWithStringLiteralTypes01.2.minified.js
index 26fc1a9caa..3ad41365f7 100644
--- a/crates/swc/tests/tsc-references/typeArgumentsWithStringLiteralTypes01.2.minified.js
+++ b/crates/swc/tests/tsc-references/typeArgumentsWithStringLiteralTypes01.2.minified.js
@@ -11,12 +11,12 @@ function fun3() {
     return args[+randBool()];
 }
 (n11 = n1 || (n1 = {})).a = fun1("Hello", "World"), n11.b = fun1("Hello", "Hello"), n11.c = fun2("Hello", "World"), n11.d = fun2("Hello", "Hello"), n11.e = fun3("Hello", "Hello", "World", "Foo"), // Should be valid
-a = takeReturnString(n11.a), b = takeReturnString(n11.b), c = takeReturnString(n11.c), d = takeReturnString(n11.d), e = takeReturnString(n11.e), // Passing these as arguments should cause an error.
-a = takeReturnHello(n11.a), b = takeReturnHello(n11.b), c = takeReturnHello(n11.c), d = takeReturnHello(n11.d), e = takeReturnHello(n11.e), // Passing these as arguments should cause an error.
-a = takeReturnHelloWorld(n11.a), b = takeReturnHelloWorld(n11.b), c = takeReturnHelloWorld(n11.c), d = takeReturnHelloWorld(n11.d), e = takeReturnHelloWorld(n11.e), (n21 = n2 || (n2 = {})).a = fun1("Hello", "Hello"), n21.b = fun1("Hello", "World"), n21.c = fun2("Hello", "Hello"), n21.d = fun2("Hello", "World"), n21.e = fun3("Hello", "World"), // Assignment from the returned value should cause an error.
-a = takeReturnString(n21.a), b = takeReturnString(n21.b), c = takeReturnString(n21.c), d = takeReturnString(n21.d), e = takeReturnString(n21.e), // Should be valid
-a = takeReturnHello(n21.a), b = takeReturnHello(n21.b), c = takeReturnHello(n21.c), d = takeReturnHello(n21.d), e = takeReturnHello(n21.e), // Assignment from the returned value should cause an error.
-a = takeReturnHelloWorld(n21.a), b = takeReturnHelloWorld(n21.b), c = takeReturnHelloWorld(n21.c), d = takeReturnHelloWorld(n21.d), e = takeReturnHelloWorld(n21.e), (n31 = n3 || (n3 = {})).a = fun2("Hello", "World"), n31.b = fun2("World", "Hello"), n31.c = fun2("Hello", "Hello"), n31.d = fun2("World", "World"), n31.e = fun3("Hello", "World"), // Assignment from the returned value should cause an error.
-a = takeReturnString(n31.a), b = takeReturnString(n31.b), c = takeReturnString(n31.c), d = takeReturnString(n31.d), e = takeReturnString(n31.e), // Passing these as arguments should cause an error.
-a = takeReturnHello(n31.a), b = takeReturnHello(n31.b), c = takeReturnHello(n31.c), d = takeReturnHello(n31.d), e = takeReturnHello(n31.e), // Both should be valid.
-a = takeReturnHelloWorld(n31.a), b = takeReturnHelloWorld(n31.b), c = takeReturnHelloWorld(n31.c), d = takeReturnHelloWorld(n31.d), e = takeReturnHelloWorld(n31.e);
+n11.a = takeReturnString(n11.a), n11.b = takeReturnString(n11.b), n11.c = takeReturnString(n11.c), n11.d = takeReturnString(n11.d), n11.e = takeReturnString(n11.e), // Passing these as arguments should cause an error.
+n11.a = takeReturnHello(n11.a), n11.b = takeReturnHello(n11.b), n11.c = takeReturnHello(n11.c), n11.d = takeReturnHello(n11.d), n11.e = takeReturnHello(n11.e), // Passing these as arguments should cause an error.
+n11.a = takeReturnHelloWorld(n11.a), n11.b = takeReturnHelloWorld(n11.b), n11.c = takeReturnHelloWorld(n11.c), n11.d = takeReturnHelloWorld(n11.d), n11.e = takeReturnHelloWorld(n11.e), (n21 = n2 || (n2 = {})).a = fun1("Hello", "Hello"), n21.b = fun1("Hello", "World"), n21.c = fun2("Hello", "Hello"), n21.d = fun2("Hello", "World"), n21.e = fun3("Hello", "World"), // Assignment from the returned value should cause an error.
+n21.a = takeReturnString(n21.a), n21.b = takeReturnString(n21.b), n21.c = takeReturnString(n21.c), n21.d = takeReturnString(n21.d), n21.e = takeReturnString(n21.e), // Should be valid
+n21.a = takeReturnHello(n21.a), n21.b = takeReturnHello(n21.b), n21.c = takeReturnHello(n21.c), n21.d = takeReturnHello(n21.d), n21.e = takeReturnHello(n21.e), // Assignment from the returned value should cause an error.
+n21.a = takeReturnHelloWorld(n21.a), n21.b = takeReturnHelloWorld(n21.b), n21.c = takeReturnHelloWorld(n21.c), n21.d = takeReturnHelloWorld(n21.d), n21.e = takeReturnHelloWorld(n21.e), (n31 = n3 || (n3 = {})).a = fun2("Hello", "World"), n31.b = fun2("World", "Hello"), n31.c = fun2("Hello", "Hello"), n31.d = fun2("World", "World"), n31.e = fun3("Hello", "World"), // Assignment from the returned value should cause an error.
+n31.a = takeReturnString(n31.a), n31.b = takeReturnString(n31.b), n31.c = takeReturnString(n31.c), n31.d = takeReturnString(n31.d), n31.e = takeReturnString(n31.e), // Passing these as arguments should cause an error.
+n31.a = takeReturnHello(n31.a), n31.b = takeReturnHello(n31.b), n31.c = takeReturnHello(n31.c), n31.d = takeReturnHello(n31.d), n31.e = takeReturnHello(n31.e), // Both should be valid.
+n31.a = takeReturnHelloWorld(n31.a), n31.b = takeReturnHelloWorld(n31.b), n31.c = takeReturnHelloWorld(n31.c), n31.d = takeReturnHelloWorld(n31.d), n31.e = takeReturnHelloWorld(n31.e);
diff --git a/crates/swc_ecma_transforms_typescript/tests/fixture/issue-8011/input.ts b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-8011/input.ts
new file mode 100644
index 0000000000..dee6147c83
--- /dev/null
+++ b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-8011/input.ts
@@ -0,0 +1,10 @@
+namespace API {
+    export let url = "/";
+
+    export function update(value: string) {
+        url = value;
+    }
+}
+
+API.update("/new");
+console.log(API.url);
diff --git a/crates/swc_ecma_transforms_typescript/tests/fixture/issue-8011/output.js b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-8011/output.js
new file mode 100644
index 0000000000..de07cfa276
--- /dev/null
+++ b/crates/swc_ecma_transforms_typescript/tests/fixture/issue-8011/output.js
@@ -0,0 +1,10 @@
+var API;
+(function(API) {
+    API.url = "/";
+    function update(value) {
+        API.url = value;
+    }
+    API.update = update;
+})(API || (API = {}));
+API.update("/new");
+console.log(API.url);