Repository: parcel-bundler/parcel
Pull request #4900 (2020-07-16 21:04:40)

Concat order with skipped assets
# ↪️ Pull Request

Fixes the first scope hoisting issue in the React Spectrum docs

<br>

Previously, we skipped excluded assets when determining the concat order.

```js
// foo.js
import {a} from "a.js";
import {b} from "b.js";
import {c} from "c.js";
import {value} from "sideeffect-free-reexport.js"

// sideeffect-free-reexport.js
export {value} from "value.js"
```
here `sideeffect-free-reexport.js` was being skipped and when the dependencies of `foo.js` were being spliced into `foo.js`, the child (rather grandchild) `value` wasn't actually listed in `statementIndices` because `foo.js` only has a require call for `sideeffect-free-reexport.js`. 
This mean that the output order was:
```
// start foo.js
// value.js (because of fallback to i=0 if not in `statementIndices`)
// a.js
// b.js
// c.js
// end foo.js
```

In this case, `c.js` imported a file that was also used by `value`, resulting in:
```
// start foo.js
// value.js - uses x
// a.js
// b.js
// c.js - contains x because of DFS
// end foo.js
```
(the relative order of `value.js`/`sideeffect-free-reexport.js` and `c.js` is swapped compared to the input).

height[bot]: > Link [Height](https://parcel.height.app) tasks by mentioning a task ID in the pull request title or description, commit messages, or comments.
>
> 💡Tip: You can also use "Close T-X" to automatically close a task when the pull request is merged.
<!-- Sent by Height -->
parcel-benchmark: ## Benchmark Results
<details><summary>Kitchen Sink ✅</summary><p>

#### Timings

| Description | Time | Difference |
| --- | --- | --- |
| Cold | 7.16s | -26.00ms |
| Cached | 2.46s | -64.00ms |

#### Cold Bundles

| Bundle | Size | Difference | Time | Difference |
| --- | --- | --- | --- | --- |
| dist/legacy/parcel.d5807e82.webp | 102.94kb | +0.00b | 295.00ms | -58.00ms 🚀 |
| dist/modern/parcel.d5807e82.webp | 102.94kb | +0.00b | 294.00ms | -66.00ms 🚀 |
| dist/legacy/index.42516b4a.js | 1.10kb | +0.00b | 1.92s | +783.00ms ⚠️ |
| dist/modern/index.8a1ced31.js | 1.10kb | +0.00b | 1.92s | +1.08s ⚠️ |
| dist/legacy/index.html | 701.00b | +0.00b | 787.00ms | -238.00ms 🚀 |
| dist/modern/index.html | 701.00b | +0.00b | 784.00ms | -237.00ms 🚀 |
| dist/legacy/index.0e158b6b.css | 77.00b | +0.00b | 1.92s | +162.00ms ⚠️ |
| dist/modern/index.e480862d.css | 77.00b | +0.00b | 1.57s | -194.00ms 🚀 |

#### Cached Bundles

*No bundle changes detected.*

</p></details>

<details><summary>React HackerNews ✅</summary><p>

#### Timings

| Description | Time | Difference |
| --- | --- | --- |
| Cold | 32.53s | +485.00ms |
| Cached | 2.66s | -10.00ms |

#### Cold Bundles

| Bundle | Size | Difference | Time | Difference |
| --- | --- | --- | --- | --- |
| dist/PermalinkedComment.c85d137d.js | 4.22kb | +0.00b | 6.29s | -6.29s 🚀 |
| dist/UserProfile.30e15baf.js | 1.62kb | +0.00b | 6.29s | -6.29s 🚀 |
| dist/NotFound.5aa6b041.js | 387.00b | +0.00b | 6.29s | -6.29s 🚀 |

#### Cached Bundles

| Bundle | Size | Difference | Time | Difference |
| --- | --- | --- | --- | --- |
| dist/index.js | 478.42kb | +0.00b | 351.00ms | -22.00ms 🚀 |
| dist/PermalinkedComment.c85d137d.js | 4.22kb | +0.00b | 348.00ms | -22.00ms 🚀 |
| dist/UserProfile.30e15baf.js | 1.62kb | +0.00b | 348.00ms | -22.00ms 🚀 |
| dist/NotFound.5aa6b041.js | 387.00b | +0.00b | 348.00ms | -22.00ms 🚀 |
| dist/logo.24c8bf9e.png | 274.00b | +0.00b | 284.00ms | +38.00ms ⚠️ |

</p></details>

<details><summary>AtlasKit Editor ✅</summary><p>

#### Timings

| Description | Time | Difference |
| --- | --- | --- |
| Cold | 3.15m | -9.06s |
| Cached | 5.50s | -214.00ms |

#### Cold Bundles

| Bundle | Size | Difference | Time | Difference |
| --- | --- | --- | --- | --- |
| dist/index.3417749f.js | 2.73mb | -54.00b 🚀 | 1.76m | -5.07s |
| dist/pdfRenderer.cd742f5e.js | 1.11mb | +0.00b | 59.01s | -5.08s 🚀 |
| dist/editorView.ae59b49f.js | 585.98kb | +0.00b | 1.51m | -4.88s 🚀 |
| dist/popup.4a66e15e.js | 170.62kb | +0.00b | 1.02m | +21.32s ⚠️ |
| dist/EmojiPickerComponent.bab709db.js | 97.89kb | +0.00b | 13.60s | -696.00ms 🚀 |
| dist/card.ce67dc35.js | 55.09kb | +0.00b | 16.18s | -987.00ms 🚀 |
| dist/card.1007a95f.js | 52.86kb | +0.00b | 1.76m | +38.90s ⚠️ |
| dist/DatePicker.cbe44a9b.js | 21.17kb | -1.00b 🚀 | 11.90s | -416.00ms |
| dist/EmojiPickerComponent.898d8be6.js | 18.23kb | +0.00b | 11.90s | -2.19s 🚀 |
| dist/dropzone.c8c03b6a.js | 14.36kb | +0.00b | 1.04m | -3.50s 🚀 |
| dist/ui.cee430a3.js | 13.97kb | +0.00b | 16.09s | -831.00ms 🚀 |
| dist/smartMediaEditor.7ee0079c.js | 13.95kb | +0.00b | 1.07m | -46.94s 🚀 |
| dist/workerHasher.65b8a1b7.js | 11.78kb | +0.00b | 15.17s | -811.00ms 🚀 |
| dist/EmojiTypeAheadComponent.9af6e5fc.js | 8.67kb | +0.00b | 13.94s | -1.05s 🚀 |
| dist/EmojiPickerComponent.624bf3f4.js | 3.58kb | +0.00b | 11.90s | -2.19s 🚀 |
| dist/dropzone.2f1a1e17.js | 3.23kb | +0.00b | 59.02s | +21.82s ⚠️ |
| dist/Modal.2835744c.js | 3.08kb | +0.00b | 6.40s | +711.00ms ⚠️ |
| dist/EmojiUploadComponent.88a444df.js | 2.18kb | +0.00b | 13.94s | -737.00ms 🚀 |
| dist/card.cadccd96.js | 2.03kb | +0.00b | 35.72s | +15.34s ⚠️ |
| dist/ResourcedEmojiComponent.d9a49606.js | 2.00kb | +0.00b | 6.15s | +454.00ms ⚠️ |
| dist/16.86a8c7d4.js | 1.66kb | +0.00b | 8.02s | -464.00ms 🚀 |
| dist/feedback.8b7c7928.js | 1.64kb | +0.00b | 8.41s | -53.31s 🚀 |
| dist/workerHasher.9bb425b1.js | 1.54kb | +0.00b | 13.94s | -1.05s 🚀 |
| dist/list-number.4632b459.js | 1.46kb | +0.00b | 59.40s | +51.10s ⚠️ |
| dist/16.c1ed07f8.js | 1.33kb | +0.00b | 8.02s | -464.00ms 🚀 |
| dist/link.d12cc130.js | 1.30kb | +0.00b | 59.40s | +51.10s ⚠️ |
| dist/heading6.4c4017db.js | 1.30kb | +0.00b | 8.41s | -53.31s 🚀 |
| dist/heading5.dde15cec.js | 1.17kb | +0.00b | 8.41s | -53.31s 🚀 |
| dist/media-card-analytics-error-boundary.6a25b970.js | 1.15kb | +0.00b | 59.02s | +21.82s ⚠️ |
| dist/16.da43aa07.js | 1.13kb | +0.00b | 8.02s | -464.00ms 🚀 |
| dist/16.dd07505e.js | 1.10kb | +0.00b | 6.13s | +434.00ms ⚠️ |
| dist/component.40194190.js | 1.07kb | +0.00b | 6.13s | +434.00ms ⚠️ |
| dist/heading4.5316f6f2.js | 1.06kb | +0.00b | 8.41s | -53.31s 🚀 |
| dist/list.057cb664.js | 978.00b | +0.00b | 59.40s | +51.10s ⚠️ |
| dist/panel.9dbc048e.js | 897.00b | +0.00b | 59.41s | +50.97s ⚠️ |
| dist/media-picker-analytics-error-boundary.553055f6.js | 837.00b | +0.00b | 1.03m | +21.69s ⚠️ |
| dist/media-card-analytics-error-boundary.964206cc.js | 833.00b | +0.00b | 16.09s | -838.00ms 🚀 |
| dist/EmojiPickerComponent.a968dafe.js | 535.00b | +0.00b | 13.94s | -736.00ms 🚀 |
| dist/simpleHasher.f8e69464.js | 528.00b | +0.00b | 16.09s | -837.00ms 🚀 |
| dist/index.html | 119.00b | +0.00b | 1.10s | -62.00ms 🚀 |

#### Cached Bundles

| Bundle | Size | Difference | Time | Difference |
| --- | --- | --- | --- | --- |
| dist/index.3417749f.js | 2.73mb | -47.00b 🚀 | 581.00ms | -148.00ms 🚀 |
| dist/pdfRenderer.cd742f5e.js | 1.11mb | +0.00b | 578.00ms | -147.00ms 🚀 |
| dist/editorView.ae59b49f.js | 585.98kb | +0.00b | 577.00ms | -147.00ms 🚀 |
| dist/popup.4a66e15e.js | 170.62kb | +0.00b | 577.00ms | -148.00ms 🚀 |
| dist/EmojiPickerComponent.bab709db.js | 97.89kb | +0.00b | 578.00ms | -148.00ms 🚀 |
| dist/Toolbar.33a83ae3.js | 92.34kb | +0.00b | 577.00ms | -147.00ms 🚀 |
| dist/media-viewer.1d9322e7.js | 69.09kb | +0.00b | 578.00ms | -147.00ms 🚀 |
| dist/card.ce67dc35.js | 55.09kb | +0.00b | 578.00ms | -148.00ms 🚀 |
| dist/card.1007a95f.js | 52.86kb | +0.00b | 577.00ms | -147.00ms 🚀 |
| dist/Modal.e3bd77d0.js | 41.40kb | +0.00b | 580.00ms | -148.00ms 🚀 |
| dist/component.59edff1f.js | 30.78kb | +0.00b | 580.00ms | -148.00ms 🚀 |
| dist/component.37867f50.js | 22.51kb | +0.00b | 580.00ms | -147.00ms 🚀 |
| dist/DatePicker.cbe44a9b.js | 21.17kb | -1.00b 🚀 | 578.00ms | -148.00ms 🚀 |
| dist/EmojiPickerComponent.898d8be6.js | 18.23kb | +0.00b | 578.00ms | -148.00ms 🚀 |
| dist/js.48be70a4.js | 16.40kb | +0.00b | 580.00ms | -148.00ms 🚀 |
| dist/dropzone.c8c03b6a.js | 14.36kb | +0.00b | 577.00ms | -148.00ms 🚀 |
| dist/ui.cee430a3.js | 13.97kb | +0.00b | 578.00ms | -148.00ms 🚀 |
| dist/smartMediaEditor.7ee0079c.js | 13.95kb | +0.00b | 577.00ms | -147.00ms 🚀 |
| dist/workerHasher.65b8a1b7.js | 11.78kb | +0.00b | 578.00ms | -148.00ms 🚀 |
| dist/EmojiTypeAheadComponent.9af6e5fc.js | 8.67kb | +0.00b | 578.00ms | -148.00ms 🚀 |
| dist/component.11476a92.js | 6.05kb | +0.00b | 580.00ms | -147.00ms 🚀 |
| dist/card.4ac5c1b2.js | 5.61kb | +0.00b | 578.00ms | -147.00ms 🚀 |
| dist/media-viewer.bc661bce.js | 3.92kb | +0.00b | 578.00ms | -147.00ms 🚀 |
| dist/EmojiPickerComponent.624bf3f4.js | 3.58kb | +0.00b | 578.00ms | -148.00ms 🚀 |
| dist/index.393afe59.css | 3.46kb | +0.00b | 577.00ms | -146.00ms 🚀 |
| dist/png-chunks-extract.8d15a914.js | 3.41kb | +0.00b | 580.00ms | -148.00ms 🚀 |
| dist/dropzone.2f1a1e17.js | 3.23kb | +0.00b | 577.00ms | -148.00ms 🚀 |
| dist/Modal.2835744c.js | 3.08kb | +0.00b | 580.00ms | -148.00ms 🚀 |
| dist/clipboard.0244488f.js | 2.75kb | +0.00b | 577.00ms | -148.00ms 🚀 |
| dist/16.d1b8e6f7.js | 2.38kb | +0.00b | 580.00ms | -148.00ms 🚀 |
| dist/EmojiUploadComponent.88a444df.js | 2.18kb | +0.00b | 578.00ms | -148.00ms 🚀 |
| dist/card.cadccd96.js | 2.03kb | +0.00b | 578.00ms | -147.00ms 🚀 |
| dist/ResourcedEmojiComponent.d9a49606.js | 2.00kb | +0.00b | 581.00ms | -147.00ms 🚀 |
| dist/date.d49e64f8.js | 1.73kb | +0.00b | 579.00ms | -147.00ms 🚀 |
| dist/16.f40c1329.js | 1.73kb | +0.00b | 580.00ms | -147.00ms 🚀 |
| dist/images.80270b1f.js | 1.67kb | +0.00b | 579.00ms | -148.00ms 🚀 |
| dist/16.86a8c7d4.js | 1.66kb | +0.00b | 580.00ms | -147.00ms 🚀 |
| dist/feedback.8b7c7928.js | 1.64kb | +0.00b | 578.00ms | -148.00ms 🚀 |
| dist/browser.16c438d4.js | 1.61kb | +0.00b | 577.00ms | -148.00ms 🚀 |
| dist/workerHasher.9bb425b1.js | 1.54kb | +0.00b | 578.00ms | -148.00ms 🚀 |
| dist/workerHasher.f66f00c7.js | 1.54kb | +0.00b | 577.00ms | -147.00ms 🚀 |
| dist/list-number.4632b459.js | 1.46kb | +0.00b | 579.00ms | -147.00ms 🚀 |
| dist/status.cccf04be.js | 1.46kb | +0.00b | 579.00ms | -147.00ms 🚀 |
| dist/16.cff410ff.js | 1.38kb | +0.00b | 580.00ms | -147.00ms 🚀 |
| dist/code.af9ca2b3.js | 1.38kb | +0.00b | 579.00ms | -147.00ms 🚀 |
| dist/16.c1ed07f8.js | 1.33kb | +0.00b | 580.00ms | -147.00ms 🚀 |
| dist/16.6603f2f3.js | 1.33kb | +0.00b | 580.00ms | -147.00ms 🚀 |
| dist/16.a4b392c2.js | 1.32kb | +0.00b | 580.00ms | -148.00ms 🚀 |
| dist/16.249c644a.js | 1.31kb | +0.00b | 579.00ms | -148.00ms 🚀 |
| dist/link.d12cc130.js | 1.30kb | +0.00b | 579.00ms | -147.00ms 🚀 |
| dist/heading6.4c4017db.js | 1.30kb | +0.00b | 578.00ms | -148.00ms 🚀 |
| dist/heading3.93606b9c.js | 1.29kb | +0.00b | 578.00ms | -148.00ms 🚀 |
| dist/16.ab5c0088.js | 1.27kb | +0.00b | 579.00ms | -148.00ms 🚀 |
| dist/emoji.e603e70e.js | 1.23kb | +0.00b | 580.00ms | -147.00ms 🚀 |
| dist/16.afee1494.js | 1.23kb | +0.00b | 579.00ms | -148.00ms 🚀 |
| dist/16.e60b4d77.js | 1.21kb | +0.00b | 581.00ms | -147.00ms 🚀 |
| dist/16.7c7a40c9.js | 1.19kb | +0.00b | 579.00ms | -148.00ms 🚀 |
| dist/16.06d5a67b.js | 1.18kb | +0.00b | 579.00ms | -148.00ms 🚀 |
| dist/heading5.dde15cec.js | 1.17kb | +0.00b | 578.00ms | -148.00ms 🚀 |
| dist/expand.6889aead.js | 1.16kb | +0.00b | 578.00ms | -148.00ms 🚀 |
| dist/media-card-analytics-error-boundary.6a25b970.js | 1.15kb | +0.00b | 577.00ms | -148.00ms 🚀 |
| dist/16.2096b9f3.js | 1.14kb | +0.00b | 580.00ms | -148.00ms 🚀 |
| dist/16.f040d62d.js | 1.14kb | +0.00b | 580.00ms | -147.00ms 🚀 |
| dist/16.ecf8a702.js | 1.13kb | +0.00b | 580.00ms | -147.00ms 🚀 |
| dist/16.1b342686.js | 1.13kb | +0.00b | 580.00ms | -147.00ms 🚀 |
| dist/16.2e7f52c6.js | 1.13kb | +0.00b | 580.00ms | -147.00ms 🚀 |
| dist/16.da43aa07.js | 1.13kb | +0.00b | 580.00ms | -147.00ms 🚀 |
| dist/heading2.e219309a.js | 1.11kb | +0.00b | 578.00ms | -147.00ms 🚀 |
| dist/16.dd07505e.js | 1.10kb | +0.00b | 581.00ms | -147.00ms 🚀 |
| dist/component.40194190.js | 1.07kb | +0.00b | 581.00ms | -147.00ms 🚀 |
| dist/mention.b1828447.js | 1.07kb | +0.00b | 579.00ms | -147.00ms 🚀 |
| dist/heading4.5316f6f2.js | 1.06kb | +0.00b | 578.00ms | -148.00ms 🚀 |
| dist/16.317b9164.js | 1.05kb | +0.00b | 579.00ms | -148.00ms 🚀 |
| dist/layout.ac6734f9.js | 1.05kb | +0.00b | 578.00ms | -148.00ms 🚀 |
| dist/divider.d2be55a1.js | 1.03kb | +0.00b | 579.00ms | -147.00ms 🚀 |
| dist/quote.f4e09143.js | 1.02kb | +0.00b | 579.00ms | -147.00ms 🚀 |
| dist/action.cb287eb0.js | 1.00kb | +0.00b | 579.00ms | -148.00ms 🚀 |
| dist/decision.91f3af14.js | 1010.00b | +0.00b | 579.00ms | -147.00ms 🚀 |
| dist/panel-warning.68b04096.js | 1008.00b | +0.00b | 579.00ms | -147.00ms 🚀 |
| dist/list.057cb664.js | 978.00b | +0.00b | 579.00ms | -147.00ms 🚀 |
| dist/heading1.fa66b375.js | 976.00b | +0.00b | 579.00ms | -147.00ms 🚀 |
| dist/panel-error.c4b05f6f.js | 906.00b | +0.00b | 579.00ms | -147.00ms 🚀 |
| dist/panel.9dbc048e.js | 897.00b | +0.00b | 579.00ms | -147.00ms 🚀 |
| dist/table.b35545be.js | 889.00b | +0.00b | 579.00ms | -147.00ms 🚀 |
| dist/panel-success.7eb0809a.js | 845.00b | +0.00b | 579.00ms | -147.00ms 🚀 |
| dist/panel-note.ec8386b7.js | 841.00b | +0.00b | 579.00ms | -147.00ms 🚀 |
| dist/media-picker-analytics-error-boundary.553055f6.js | 837.00b | +0.00b | 577.00ms | -148.00ms 🚀 |
| dist/media-card-analytics-error-boundary.964206cc.js | 833.00b | +0.00b | 578.00ms | -147.00ms 🚀 |
| dist/media-card-analytics-error-boundary.57503222.js | 833.00b | +0.00b | 577.00ms | -147.00ms 🚀 |
| dist/media-viewer-analytics-error-boundary.bbe40a02.js | 757.00b | +0.00b | 578.00ms | -147.00ms 🚀 |
| dist/EmojiPickerComponent.a968dafe.js | 535.00b | +0.00b | 578.00ms | -148.00ms 🚀 |
| dist/simpleHasher.f8e69464.js | 528.00b | +0.00b | 578.00ms | -148.00ms 🚀 |
| dist/simpleHasher.32d9352e.js | 528.00b | +0.00b | 577.00ms | -147.00ms 🚀 |
| dist/index.html | 119.00b | +0.00b | 583.00ms | -150.00ms 🚀 |

</p></details>

<details><summary>Three.js x4 🚨</summary><p>

#### Timings

| Description | Time | Difference |
| --- | --- | --- |
| Cold | **FAILED** | -0.00ms |
| Cached | **FAILED** | -0.00ms |

#### Cold Bundles

*No bundles found, this is probably a failed build...*
#### Cached Bundles

*No bundles found, this is probably a failed build...*
</p></details>

**[Click here to view a detailed benchmark overview.](https://benchmark.parceljs.org/benchmark/271243037108077064)**

Patch:
diff --git a/packages/core/utils/src/collection.js b/packages/core/utils/src/collection.js
index 814eeb438..dab07a4f0 100644
--- a/packages/core/utils/src/collection.js
+++ b/packages/core/utils/src/collection.js
@@ -4,14 +4,22 @@ export function unique<T>(array: Array<T>): Array<T> {
   return [...new Set(array)];
 }
 
+export function flat<T>(array: $ReadOnlyArray<$ReadOnlyArray<T>>): Array<T> {
+  let out = [];
+  for (let v of array) {
+    out.push(...v);
+  }
+  return out;
+}
+
 export function flatMap<T, U>(
   array: Array<T>,
   projectFn: (T, number, Array<T>) => Array<U>,
 ): Array<U> {
   let out = [];
 
-  for (let arr of array.map(projectFn)) {
-    out.push(...arr);
+  for (let i = 0; i < array.length; i++) {
+    out.push(...projectFn(array[i], i, array));
   }
   return out;
 }
diff --git a/packages/shared/scope-hoisting/src/concat.js b/packages/shared/scope-hoisting/src/concat.js
index f357bc336..ace7a43cc 100644
--- a/packages/shared/scope-hoisting/src/concat.js
+++ b/packages/shared/scope-hoisting/src/concat.js
@@ -32,7 +32,7 @@ import {
   isVariableDeclaration,
 } from '@babel/types';
 import {simple as walkSimple, traverse} from '@parcel/babylon-walk';
-import {PromiseQueue, relativeUrl} from '@parcel/utils';
+import {PromiseQueue, relativeUrl, flat} from '@parcel/utils';
 import invariant from 'assert';
 import fs from 'fs';
 import nullthrows from 'nullthrows';
@@ -103,9 +103,9 @@ export async function concat({
   // `asset.getDependencies()` must be the same!
   bundle.traverseAssets<TraversalContext>({
     enter(asset, context) {
-      if (shouldSkipAsset(bundleGraph, asset, usedExports)) {
-        return context;
-      }
+      // Do not skip over excluded assets entirely, since their dependencies need
+      // to be in the correct order and they themselves need to be inserted correctly
+      // into the parent again.
 
       return {
         parent: context && context.children,
@@ -113,7 +113,7 @@ export async function concat({
       };
     },
     exit(asset, context) {
-      if (!context || shouldSkipAsset(bundleGraph, asset, usedExports)) {
+      if (!context) {
         return;
       }
 
@@ -138,11 +138,22 @@ export async function concat({
         }
       }
 
-      for (let [assetId, ast] of [...context.children].reverse()) {
-        let index = statementIndices.has(assetId)
-          ? nullthrows(statementIndices.get(assetId))
-          : 0;
-        statements.splice(index, 0, ...ast);
+      if (shouldSkipAsset(bundleGraph, asset, usedExports)) {
+        // The order of imports of excluded assets has to be retained
+        statements = flat(
+          [...context.children]
+            .sort(
+              ([aId], [bId]) =>
+                nullthrows(statementIndices.get(aId)) -
+                nullthrows(statementIndices.get(bId)),
+            )
+            .map(([, ast]) => ast),
+        );
+      } else {
+        for (let [assetId, ast] of [...context.children].reverse()) {
+          let index = statementIndices.get(assetId) ?? 0;
+          statements.splice(index, 0, ...ast);
+        }
       }
 
       // If this module is referenced by another JS bundle, or is an entry module in a child bundle,


Test patch:
diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/foo/a.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/foo/a.js
new file mode 100644
index 000000000..9233cce2f
--- /dev/null
+++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/foo/a.js
@@ -0,0 +1,1 @@
+export const a = "a";
diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/foo/b.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/foo/b.js
new file mode 100644
index 000000000..59d168993
--- /dev/null
+++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/foo/b.js
@@ -0,0 +1,1 @@
+export const b = "b";
diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/foo/c.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/foo/c.js
new file mode 100644
index 000000000..bb481d518
--- /dev/null
+++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/foo/c.js
@@ -0,0 +1,3 @@
+import { x } from "../x.js";
+
+export const c = x;
diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/foo/index.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/foo/index.js
new file mode 100644
index 000000000..afea3c446
--- /dev/null
+++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/foo/index.js
@@ -0,0 +1,6 @@
+import { a } from "./a.js";
+import { b } from "./b.js";
+import { c } from "./c.js";
+import { w } from "../w/index.js";
+
+export const foo = [a, b, c, w];
diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/index.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/index.js
new file mode 100644
index 000000000..9d83a3815
--- /dev/null
+++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/index.js
@@ -0,0 +1,3 @@
+import { foo } from "./foo/index.js";
+
+output = foo;
diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/package.json b/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/package.json
new file mode 100644
index 000000000..87fed9cb2
--- /dev/null
+++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/package.json
@@ -0,0 +1,3 @@
+{
+  "browserslist": "Chrome 80"
+}
diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/w/index.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/w/index.js
new file mode 100644
index 000000000..48e2ec8f8
--- /dev/null
+++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/w/index.js
@@ -0,0 +1,3 @@
+export { w } from "./w.js";
+
+sideEffect("unused!");
diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/w/package.json b/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/w/package.json
new file mode 100644
index 000000000..bb21a1175
--- /dev/null
+++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/w/package.json
@@ -0,0 +1,3 @@
+{
+	"sideEffects": false
+}
\ No newline at end of file
diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/w/w.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/w/w.js
new file mode 100644
index 000000000..7475a4067
--- /dev/null
+++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/w/w.js
@@ -0,0 +1,3 @@
+import { x } from "../x.js";
+
+export class w extends x {}
diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/x.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/x.js
new file mode 100644
index 000000000..02610b05a
--- /dev/null
+++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/x.js
@@ -0,0 +1,1 @@
+export class x {}
diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/yarn.lock b/packages/core/integration-tests/test/integration/scope-hoisting/es6/side-effects-false-order/yarn.lock
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/core/integration-tests/test/scope-hoisting.js b/packages/core/integration-tests/test/scope-hoisting.js
index 035244a46..40a59ad17 100644
--- a/packages/core/integration-tests/test/scope-hoisting.js
+++ b/packages/core/integration-tests/test/scope-hoisting.js
@@ -773,6 +773,33 @@ describe('scope hoisting', function() {
       assert.deepEqual(output, 4);
     });
 
+    it('concatenates in the correct order when re-exporting assets were excluded', async function() {
+      let b = await bundle(
+        path.join(
+          __dirname,
+          '/integration/scope-hoisting/es6/side-effects-false-order/index.js',
+        ),
+      );
+
+      let contents = await outputFS.readFile(
+        b.getBundles()[0].filePath,
+        'utf8',
+      );
+      assert(/\s+class\s+/.test(contents));
+
+      let called = false;
+      let output = await run(b, {
+        sideEffect: () => {
+          called = true;
+        },
+      });
+
+      assert(!called, 'side effect called');
+      assert.strictEqual(output[0], 'a');
+      assert.strictEqual(output[1], 'b');
+      assert(new output[3]() instanceof output[2]);
+    });
+
     it('supports wildcards with sideEffects: false', async function() {
       let b = await bundle(
         path.join(