Repository: swc-project/swc (rust)

Issue:
export {default as name} can create duplicate symbols
<!--
If you are using swc at work, please considering adding your company to https://swc.rs/users/
If then, your issue will be fixed more quickly.
-->

**Describe the bug**

I frequently re-export the `default` export from another file as a named export.  When doing so with `babel`, it assigns the new named export inside the `_exportNames` array.  With `swc` this doesn't happen.  This causes apps that import my library to get an error:  `TypeError: Cannot redefine property:`

**Input code**

```
export {default as f} from "./f";
export * from "./f";

export {default as g} from "./g";
export * from "./g";
```

**Config**

```json
{
    "module": {
      "type": "commonjs"
    },
    "jsc": {
      "parser": {
        "syntax": "ecmascript",
        "jsx": true
      },
      "transform": {
          "react": {
              "runtime": "automatic"
          }
      },
      "loose": true
    }
  }
```

**Expected behavior**
With babel, I will find:

```
var _exportNames = {
  f: true,
  g: true
};
```

With `swc`, it produces:

```
var _exportNames = {
};
```

Where this is important is later on:

```
Object.keys(_f).forEach(function(key) {
    if (key === "default" || key === "__esModule") return;
    if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; // <- This doesn't return because _exportNames is wrong
    if (key in exports && exports[key] === _f[key]) return; 
    Object.defineProperty(exports, key, {
        enumerable: true,
        get: function() {
            return _f[key];
        }
    });
});
```

**Version**
```
npx swc --version

@swc/cli: 0.1.51
@swc/core: 1.2.103
```

**Additional context**
I'd be happy to help track this down, if it's found to be a valid bug. 


Pull request:
fix(es/transforms/cjs): Allow mixing named exports and export stars.
This is my attempt at fixing #2548. 


---

 - Closes #2548

---

swc_ecma_transforms_module:
 - `common_js`: Fix _exportNames when exporting aliases and using `export *`s . (#2548)

Patch:
diff --git a/ecmascript/transforms/module/src/common_js.rs b/ecmascript/transforms/module/src/common_js.rs
index 3e4476c4cd..ef61117d98 100644
--- a/ecmascript/transforms/module/src/common_js.rs
+++ b/ecmascript/transforms/module/src/common_js.rs
@@ -155,6 +155,19 @@ where
                             ref specifiers,
                             ..
                         })) => {
+                            // handle: export {sym as alias1, alias2, ...} from "x"
+                            for ExportNamedSpecifier { orig, exported, .. } in
+                                specifiers.into_iter().filter_map(|e| match e {
+                                    ExportSpecifier::Named(e) => Some(e),
+                                    _ => None,
+                                })
+                            {
+                                if let Some(exported) = &exported {
+                                    exports.push(exported.sym.clone());
+                                } else {
+                                    exports.push(orig.sym.clone());
+                                }
+                            }
                             scope.import_to_export(&src, !specifiers.is_empty());
                         }
                         _ => {}


Test patch:
diff --git a/ecmascript/transforms/module/tests/common_js.rs b/ecmascript/transforms/module/tests/common_js.rs
index f3eab5b231..0fe24849c7 100644
--- a/ecmascript/transforms/module/tests/common_js.rs
+++ b/ecmascript/transforms/module/tests/common_js.rs
@@ -4166,10 +4166,14 @@ Object.defineProperty(exports, "Scope", {
       return _interfaces.Scope;
   }
 });
+var _exportNames = {
+    Scope: true
+};
 var _http = require("./http");
 var _interfaces = require("./interfaces");
 Object.keys(_http).forEach(function(key) {
     if (key === "default" || key === "__esModule") return;
+    if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
     if (key in exports && exports[key] === _http[key]) return;
     Object.defineProperty(exports, key, {
         enumerable: true,
@@ -4206,6 +4210,7 @@ export * from './pipes';
         }
     });
     var _exportNames = {
+        id: true
     };
     require("reflect-metadata");
     var _http = require("./http");
diff --git a/tests/fixture/issue-2548/case1/input/.swcrc b/tests/fixture/issue-2548/case1/input/.swcrc
new file mode 100644
index 0000000000..3f013e7290
--- /dev/null
+++ b/tests/fixture/issue-2548/case1/input/.swcrc
@@ -0,0 +1,5 @@
+{
+    "module": {
+        "type": "commonjs"
+    }
+}
\ No newline at end of file
diff --git a/tests/fixture/issue-2548/case1/input/index.js b/tests/fixture/issue-2548/case1/input/index.js
new file mode 100644
index 0000000000..e5bddae419
--- /dev/null
+++ b/tests/fixture/issue-2548/case1/input/index.js
@@ -0,0 +1,1 @@
+export { default as X } from "./Z";
diff --git a/tests/fixture/issue-2548/case1/output/index.js b/tests/fixture/issue-2548/case1/output/index.js
new file mode 100644
index 0000000000..c2e99752f5
--- /dev/null
+++ b/tests/fixture/issue-2548/case1/output/index.js
@@ -0,0 +1,16 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", {
+    value: true
+});
+Object.defineProperty(exports, "X", {
+    enumerable: true,
+    get: function() {
+        return _z.default;
+    }
+});
+var _z = _interopRequireDefault(require("./Z"));
+function _interopRequireDefault(obj) {
+    return obj && obj.__esModule ? obj : {
+        default: obj
+    };
+}
diff --git a/tests/fixture/issue-2548/case2/input/.swcrc b/tests/fixture/issue-2548/case2/input/.swcrc
new file mode 100644
index 0000000000..3f013e7290
--- /dev/null
+++ b/tests/fixture/issue-2548/case2/input/.swcrc
@@ -0,0 +1,5 @@
+{
+    "module": {
+        "type": "commonjs"
+    }
+}
\ No newline at end of file
diff --git a/tests/fixture/issue-2548/case2/input/index.js b/tests/fixture/issue-2548/case2/input/index.js
new file mode 100644
index 0000000000..18b9eb11b1
--- /dev/null
+++ b/tests/fixture/issue-2548/case2/input/index.js
@@ -0,0 +1,2 @@
+export { default as X } from "./Z";
+export * from "./Z";
diff --git a/tests/fixture/issue-2548/case2/output/index.js b/tests/fixture/issue-2548/case2/output/index.js
new file mode 100644
index 0000000000..744f3f90fe
--- /dev/null
+++ b/tests/fixture/issue-2548/case2/output/index.js
@@ -0,0 +1,48 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", {
+    value: true
+});
+Object.defineProperty(exports, "X", {
+    enumerable: true,
+    get: function() {
+        return _z.default;
+    }
+});
+var _exportNames = {
+    X: true
+};
+var _z = _interopRequireWildcard(require("./Z"));
+function _interopRequireWildcard(obj) {
+    if (obj && obj.__esModule) {
+        return obj;
+    } else {
+        var newObj = {
+        };
+        if (obj != null) {
+            for(var key in obj){
+                if (Object.prototype.hasOwnProperty.call(obj, key)) {
+                    var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {
+                    };
+                    if (desc.get || desc.set) {
+                        Object.defineProperty(newObj, key, desc);
+                    } else {
+                        newObj[key] = obj[key];
+                    }
+                }
+            }
+        }
+        newObj.default = obj;
+        return newObj;
+    }
+}
+Object.keys(_z).forEach(function(key) {
+    if (key === "default" || key === "__esModule") return;
+    if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
+    if (key in exports && exports[key] === _z[key]) return;
+    Object.defineProperty(exports, key, {
+        enumerable: true,
+        get: function() {
+            return _z[key];
+        }
+    });
+});
diff --git a/tests/fixture/issue-2548/case3/input/.swcrc b/tests/fixture/issue-2548/case3/input/.swcrc
new file mode 100644
index 0000000000..3f013e7290
--- /dev/null
+++ b/tests/fixture/issue-2548/case3/input/.swcrc
@@ -0,0 +1,5 @@
+{
+    "module": {
+        "type": "commonjs"
+    }
+}
\ No newline at end of file
diff --git a/tests/fixture/issue-2548/case3/input/index.js b/tests/fixture/issue-2548/case3/input/index.js
new file mode 100644
index 0000000000..4987d8909c
--- /dev/null
+++ b/tests/fixture/issue-2548/case3/input/index.js
@@ -0,0 +1,2 @@
+export { default as X, Y, X2 } from "./Z";
+export * from "./Z";
diff --git a/tests/fixture/issue-2548/case3/output/index.js b/tests/fixture/issue-2548/case3/output/index.js
new file mode 100644
index 0000000000..f7aba41fae
--- /dev/null
+++ b/tests/fixture/issue-2548/case3/output/index.js
@@ -0,0 +1,62 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", {
+    value: true
+});
+Object.defineProperty(exports, "X", {
+    enumerable: true,
+    get: function() {
+        return _z.default;
+    }
+});
+Object.defineProperty(exports, "Y", {
+    enumerable: true,
+    get: function() {
+        return _z.Y;
+    }
+});
+Object.defineProperty(exports, "X2", {
+    enumerable: true,
+    get: function() {
+        return _z.X2;
+    }
+});
+var _exportNames = {
+    X: true,
+    Y: true,
+    X2: true
+};
+var _z = _interopRequireWildcard(require("./Z"));
+function _interopRequireWildcard(obj) {
+    if (obj && obj.__esModule) {
+        return obj;
+    } else {
+        var newObj = {
+        };
+        if (obj != null) {
+            for(var key in obj){
+                if (Object.prototype.hasOwnProperty.call(obj, key)) {
+                    var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {
+                    };
+                    if (desc.get || desc.set) {
+                        Object.defineProperty(newObj, key, desc);
+                    } else {
+                        newObj[key] = obj[key];
+                    }
+                }
+            }
+        }
+        newObj.default = obj;
+        return newObj;
+    }
+}
+Object.keys(_z).forEach(function(key) {
+    if (key === "default" || key === "__esModule") return;
+    if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
+    if (key in exports && exports[key] === _z[key]) return;
+    Object.defineProperty(exports, key, {
+        enumerable: true,
+        get: function() {
+            return _z[key];
+        }
+    });
+});