Repository: parcel-bundler/parcel (js)

Issue:
Running parcel build without  --no-scope-hoist generates an error about a missing export
# 🐛 bug report

In https://github.com/youknowriad/asblocks/pull/32 I've tried moving our build setup from Webpack to parcel 2 beta 1. While everything works properly on dev, it seems the production build with scope hoisting enabled fails because of the @wordpress/element package.

Here's the full error I have

<img width="875" alt="Capture d’écran 2020-06-18 à 8 16 48 PM" src="https://user-images.githubusercontent.com/272444/85062991-5d37a400-b1a1-11ea-9eee-7d9919093ae0.png">

After digging into the source code of that package, I've found that the mentioned file there `node_modules/@wordpress/element/build-module/index.js` does `export * from ./react.js` and on that `react.js` file, there's indeed a `createElement` function being exported.

Can't tell for sure whether it's a bug on Parcel's tree shaking behavior or if it's the package doing it wrong.

## 🌍 Your Environment
| Software         | Version(s) |
| ---------------- | ---------- |
| Parcel           |  2 beta 1
| Node             | v12.14.1
| npm/Yarn         | 6.14.5
| Operating System | MacOS


Pull request:
Fix named imports of CommonJS assets with namespace exports via namespace reexports
# ↪️ Pull Request

Quite a mouthful....

Closes #4758


Patch:
diff --git a/packages/core/core/src/BundleGraph.js b/packages/core/core/src/BundleGraph.js
index 2a39a98c5..6348d6662 100644
--- a/packages/core/core/src/BundleGraph.js
+++ b/packages/core/core/src/BundleGraph.js
@@ -803,13 +803,10 @@ export default class BundleGraph {
         if (!resolved) continue;
         let result = this.resolveSymbol(resolved, symbol, boundary);
 
-        // Either result.symbol is a string (found) or null with a wildcard (found)
-        if (
-          result.symbol != undefined ||
-          (result.symbol === null && result.exportSymbol === '*')
-        ) {
+        // We found the symbol
+        if (result.symbol != undefined) {
           if (assetOutside) {
-            // We found the symbol, but `asset` is outside, return `asset` and the original symbol
+            // ..., but `asset` is outside, return `asset` and the original symbol
             bailout = true;
             break;
           }
@@ -821,7 +818,7 @@ export default class BundleGraph {
             loc: resolved.symbols?.get(symbol)?.loc,
           };
         }
-        if (!result.asset.symbols) {
+        if (!result.asset.symbols || result.symbol === null) {
           // We didn't find it in this dependency, but it might still be there: bailout.
           // Continue searching though, with the assumption that there are no conficting reexports
           // and there might be a another (re)export (where we might statically find the symbol).
@@ -845,7 +842,8 @@ export default class BundleGraph {
       return {
         asset,
         exportSymbol: symbol,
-        symbol: identifier ?? (bailout ? null : undefined),
+        symbol:
+          identifier ?? (bailout || asset.symbols?.has('*') ? null : undefined),
         loc: asset.symbols?.get(symbol)?.loc,
       };
     }
diff --git a/packages/shared/scope-hoisting/src/link.js b/packages/shared/scope-hoisting/src/link.js
index 321d343fb..07948e174 100644
--- a/packages/shared/scope-hoisting/src/link.js
+++ b/packages/shared/scope-hoisting/src/link.js
@@ -231,6 +231,7 @@ export function link({
     }
 
     // Look for an exports object if we bailed out.
+    // TODO remove the first part of the condition once bundleGraph.resolveSymbol().identifier === null covers this
     if ((node === undefined && mod.meta.isCommonJS) || node === null) {
       node = findSymbol(path, assertString(mod.meta.exportsIdentifier));
       if (!node) {


Test patch:
diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-commonjs-wildcard/a.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-commonjs-wildcard/a.js
new file mode 100644
index 000000000..22d581844
--- /dev/null
+++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-commonjs-wildcard/a.js
@@ -0,0 +1,3 @@
+import { createElement } from "./b.js";
+
+output = createElement();
diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-commonjs-wildcard/b.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-commonjs-wildcard/b.js
new file mode 100644
index 000000000..a7ce11c1c
--- /dev/null
+++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-commonjs-wildcard/b.js
@@ -0,0 +1,1 @@
+export * from './c.js';
diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-commonjs-wildcard/c.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-commonjs-wildcard/c.js
new file mode 100644
index 000000000..5b85182aa
--- /dev/null
+++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-commonjs-wildcard/c.js
@@ -0,0 +1,3 @@
+import { createElement } from './d.js';
+
+export { createElement };
diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-commonjs-wildcard/d.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-commonjs-wildcard/d.js
new file mode 100644
index 000000000..dbd76e900
--- /dev/null
+++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/re-export-commonjs-wildcard/d.js
@@ -0,0 +1,5 @@
+module.exports = {
+	createElement: function () {
+		return 'foo';
+	},
+};
diff --git a/packages/core/integration-tests/test/scope-hoisting.js b/packages/core/integration-tests/test/scope-hoisting.js
index e741dd0e4..a0f81fee8 100644
--- a/packages/core/integration-tests/test/scope-hoisting.js
+++ b/packages/core/integration-tests/test/scope-hoisting.js
@@ -995,6 +995,18 @@ describe('scope hoisting', function() {
       assert.deepEqual(output, 'foo');
     });
 
+    it('support chained namespace reexports of CommonJS', async function() {
+      let b = await bundle(
+        path.join(
+          __dirname,
+          '/integration/scope-hoisting/es6/re-export-commonjs-wildcard/a.js',
+        ),
+      );
+
+      let output = await run(b);
+      assert.deepEqual(output, 'foo');
+    });
+
     it('should support named imports on wrapped modules', async function() {
       let b = await bundle(
         path.join(