Repository: swc-project/swc (rust)

Issue:
Compiler is incorrectly considering local variables as exported module names `_exportNames`
### Describe the bug

Local variables in a module are being considered as "named exports" and preventing real re-exports from being exposed.

Looking at the change history, I tracked down that this issue was introduced in #4089.

Using the playground we can see the behavior was introduced after [1.2.158](https://swc-play.vercel.app/?version=1.2.158&code=H4sIAAAAAAAAA33NuwrCQBCF4dp5itPFCxZqJ1go2Ka1XpKJG9jMyOysCuK7G7QQG0%2F7f3D4flFzzNGZDqiCqEe25aBtSVwR8ad3RRrvVXCLwfnKNh3h8d3qMPAW2a2X8wwPmjQq2ZGjltTW6gf%2Bun0%2BcUrYYbXeEMYZezH5ixf4uaInvQCYYQRktAAAAA%3D%3D&config=H4sIAAAAAAAAA0WOSwrDMAxE76J1tt3kDj2EcZXg4B8aBWqM7145pGQ3zOdJnQ54WjtVJ2CZCi2r%2B9JK2irDS6hKCynMUjl5mHays1qD8bIolgKmdXMRvFAKOWxtgnxJVRh4Ipf3%2BG8OA6XyOafRr1sGtEkq%2BQCNB3SPA953%2B%2FriB8tcjwO5AAAA), on [1.2.159](https://swc-play.vercel.app/?version=1.2.159&code=H4sIAAAAAAAAA33NuwrCQBCF4dp5itPFCxZqJ1go2Ka1XpKJG9jMyOysCuK7G7QQG0%2F7f3D4flFzzNGZDqiCqEe25aBtSVwR8ad3RRrvVXCLwfnKNh3h8d3qMPAW2a2X8wwPmjQq2ZGjltTW6gf%2Bun0%2BcUrYYbXeEMYZezH5ixf4uaInvQCYYQRktAAAAA%3D%3D&config=H4sIAAAAAAAAA0WOSwrDMAxE76J1tt3kDj2EcZXg4B8aBWqM7145pGQ3zOdJnQ54WjtVJ2CZCi2r%2B9JK2irDS6hKCynMUjl5mHays1qD8bIolgKmdXMRvFAKOWxtgnxJVRh4Ipf3%2BG8OA6XyOafRr1sGtEkq%2BQCNB3SPA953%2B%2FriB8tcjwO5AAAA).

### Input code

```typescript
export * from 'another-module'

export function whatever(notExportName: string) {
  const shouldNotBeExportNameAsWell = 123
  return shouldNotBeExportNameAsWell + notExportName
}
```


### Config

```json
{
  "module": {
    "type": "commonjs",
    "noInterop": true
  },
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "tsx": true
    }
  }
}
```


### Playground link

https://swc-play.vercel.app/?version=1.2.159&code=H4sIAAAAAAAAA33NuwrCQBCF4dp5itPFCxZqJ1go2Ka1XpKJG9jMyOysCuK7G7QQG0%2F7f3D4flFzzNGZDqiCqEe25aBtSVwR8ad3RRrvVXCLwfnKNh3h8d3qMPAW2a2X8wwPmjQq2ZGjltTW6gf%2Bun0%2BcUrYYbXeEMYZezH5ixf4uaInvQCYYQRktAAAAA%3D%3D&config=H4sIAAAAAAAAA0WOSwrDMAxE76J1tt3kDj2EcZXg4B8aBWqM7145pGQ3zOdJnQ54WjtVJ2CZCi2r%2B9JK2irDS6hKCynMUjl5mHays1qD8bIolgKmdXMRvFAKOWxtgnxJVRh4Ipf3%2BG8OA6XyOafRr1sGtEkq%2BQCNB3SPA953%2B%2FriB8tcjwO5AAAA

### Expected behavior

The variables `notExportName` and `shouldNotBeExportNameAsWell` should not be considered named exports.

### Actual behavior

In my input code if I were to have a named export `notExportName` from `export * from 'another-module'`, it would not be properly exported because the local variable `notExportName` is being considered a named export.

### Version

1.2.186

### Additional context

I tried working on a fix, but I'm a complete noob on Rust, so all I managed was creating a unit test for it at https://github.com/pirelenito/swc/tree/fix-export-names

Pull request:
fix(es/modules/cjs): FIx detection of exported names
**Description:**

**Related issue (if exists):**

 - Closes #4700

Patch:
diff --git a/crates/swc_ecma_transforms_module/src/common_js.rs b/crates/swc_ecma_transforms_module/src/common_js.rs
index 4b3d3b5891..e42c8f1a67 100644
--- a/crates/swc_ecma_transforms_module/src/common_js.rs
+++ b/crates/swc_ecma_transforms_module/src/common_js.rs
@@ -207,8 +207,20 @@ impl Fold for CommonJs {
                 ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { decl, .. })) => {
                     let mut found: Vec<Ident> = vec![];
 
-                    let mut v = DestructuringFinder { found: &mut found };
-                    decl.visit_with(&mut v);
+                    match decl {
+                        Decl::Class(d) => {
+                            found.push(d.ident.clone());
+                        }
+                        Decl::Fn(d) => {
+                            found.push(d.ident.clone());
+                        }
+                        Decl::Var(v) => {
+                            found.extend(find_pat_ids(&v.decls));
+                        }
+                        _ => {
+                            unreachable!()
+                        }
+                    }
 
                     for ident in found {
                         exports.push(ident.sym.clone());


Test patch:
diff --git a/crates/swc/tests/fixture/issues-4xxx/4700/1/.swcrc b/crates/swc/tests/fixture/issues-4xxx/4700/1/.swcrc
new file mode 100644
index 0000000000..5d62d989a9
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-4xxx/4700/1/.swcrc
@@ -0,0 +1,12 @@
+{
+    "module": {
+        "type": "commonjs",
+        "noInterop": true
+    },
+    "jsc": {
+        "parser": {
+            "syntax": "typescript",
+            "tsx": true
+        }
+    }
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-4xxx/4700/1/input.ts b/crates/swc/tests/fixture/issues-4xxx/4700/1/input.ts
new file mode 100644
index 0000000000..44dbce9121
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-4xxx/4700/1/input.ts
@@ -0,0 +1,6 @@
+export * from "another-module";
+
+export function whatever(notExportName: string) {
+    const shouldNotBeExportNameAsWell = 123;
+    return shouldNotBeExportNameAsWell + notExportName;
+}
diff --git a/crates/swc_ecma_transforms_module/tests/fixture/commonjs/issue-4700/1/input.js b/crates/swc_ecma_transforms_module/tests/fixture/commonjs/issue-4700/1/input.js
new file mode 100644
index 0000000000..335ccc7cf4
--- /dev/null
+++ b/crates/swc_ecma_transforms_module/tests/fixture/commonjs/issue-4700/1/input.js
@@ -0,0 +1,6 @@
+export * from "another-module";
+
+export function whatever(notExportName) {
+    const shouldNotBeExportNameAsWell = 123;
+    return shouldNotBeExportNameAsWell + notExportName;
+}
diff --git a/crates/swc_ecma_transforms_module/tests/fixture/commonjs/issue-4700/1/output.js b/crates/swc_ecma_transforms_module/tests/fixture/commonjs/issue-4700/1/output.js
new file mode 100644
index 0000000000..e13b8269d3
--- /dev/null
+++ b/crates/swc_ecma_transforms_module/tests/fixture/commonjs/issue-4700/1/output.js
@@ -0,0 +1,24 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", {
+    value: true
+});
+var _exportNames = {
+    whatever: true
+};
+exports.whatever = whatever;
+var _anotherModule = require("another-module");
+Object.keys(_anotherModule).forEach(function(key) {
+    if (key === "default" || key === "__esModule") return;
+    if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
+    if (key in exports && exports[key] === _anotherModule[key]) return;
+    Object.defineProperty(exports, key, {
+        enumerable: true,
+        get: function() {
+            return _anotherModule[key];
+        }
+    });
+});
+function whatever(notExportName) {
+    const shouldNotBeExportNameAsWell = 123;
+    return shouldNotBeExportNameAsWell + notExportName;
+}