Skip to content

Commit

Permalink
More experimental bundler fixes (#7783)
Browse files Browse the repository at this point in the history
* internalization bug

* Cleanup

* Always add assets to reachable bundles

* cleanup

* fixed test regressions

* add ref edges whenever there are bundle edges

* add edge from bundlegroups to bundles wip

* * Get inline bundles in bundle group in HTML packager
* Traverse each bundle instead of iterating each outbound node
* Add edge between root and bundle

* use and follow reference edges again

* set env whenever we create bundles

* Check to add parallel edges from all paths to an asset from a bundle

* Always register referenced bundles before pruning when building bundle manifest

* Revert "set env whenever we create bundles"

This reverts commit 73ad828.

* Add test for referenced roots in bundle manifest

* Add reused sibling bundles to asyncBundleRootGraph

* Add test case for asset that has both an async and sync import

* ExperimentalBundler: stop at isolated bundles

* ExperimentalBundler: fix step 7 comment

* ExperimentalBundler: initialize entry bundles with no ancestors

* ExperimentalBundler: accept shared bundles extracted from workers

* Remove unused async bundles if needed

* Scope-hositing with new bundler: allow less duplication

* Uncomment line in getSymbolResolution

* Consider sibling availability before removing from ancestorAssets

* Uncomment line in getSymbolResolution

* Upgrade flow to 0.173.0 (#7809)

* Remove reachableBundles

* Bump lmdb (#7797)

* Replace typeof before DCE (#7788)

* Consider sibling availability before removing from ancestorAssets

* Consider assets in siblings before duplicating

* Remove unrelated change

* Don't consider any of parent's async bundles as sibling

* Remove unused structure

* remove eager bundle reuse and related lending code

* Alter tests with mode production and correct assets with logic for splittable bundles

* Skip unused dependencies in experimental bundler

* Implement getBundleFromBundleRoot

* Only add dependencies to CSS module JS, not CSS

* Handle multiple assets on dependencies in reachability

* ScopeHoistingPackager: Handle different wrapped ancestries in wrapping dfs

* skip assets for reachable if isolated or inline fix invariant

* Use bundleGroup instead of bundle root for determining needsStableName

* Add bundle.mainEntryAsset

* fixup! Add bundle.mainEntryAsset

Co-authored-by: Gora Kong <gora.kong1@gmail.com>
Co-authored-by: Will Binns-Smith <wbinnssmith@atlassian.com>
Co-authored-by: Eric Eldredge <lettertwo@gmail.com>
Co-authored-by: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com>
Co-authored-by: Agnieszka Gawrys <agawrys@atlassian.com>
Co-authored-by: Devon Govett <devongovett@gmail.com>
  • Loading branch information
7 people committed May 12, 2022
1 parent e68beeb commit 38bb2ee
Show file tree
Hide file tree
Showing 22 changed files with 230 additions and 281 deletions.
363 changes: 116 additions & 247 deletions packages/bundlers/experimental/src/ExperimentalBundler.js

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions packages/core/integration-tests/test/html.js
Expand Up @@ -2841,4 +2841,34 @@ describe('html', function () {
},
);
});

it('extracts shared bundles that load referenced bundle roots across entries', async () => {
let b = await bundle(
['index1.html', 'index2.html'].map(entry =>
path.join(__dirname, 'integration/html-shared-referenced', entry),
),
{
mode: 'production',
defaultTargetOptions: {
shouldOptimize: false,
},
},
);

await run(b);
});

it('should not skip bundleRoots if an asset is both async required and static required', async function () {
let b = await bundle(
path.join(__dirname, 'integration/html-sync-async-asset/index.html'),
{
mode: 'production',
defaultTargetOptions: {
shouldOptimize: false,
},
},
);

await run(b, {output: null}, {require: false});
});
});
@@ -0,0 +1,3 @@
import './async2.js';

import('./async2.js');
Empty file.
@@ -0,0 +1 @@
<script type="module" src="index1.js"></script>
@@ -0,0 +1 @@
import './shared';
@@ -0,0 +1 @@
<script type="module" src="index2.js"></script>
@@ -0,0 +1,3 @@
import './async.js';
import './shared.js';

@@ -0,0 +1,5 @@
{
"@parcel/bundler-default": {
"minBundleSize": 0
}
}
@@ -0,0 +1 @@
import('./async.js');
Empty file.
@@ -0,0 +1 @@
<script type="module" src="./index.js"></script>
@@ -0,0 +1,4 @@
import t from "./test.js";

import("./other.js")
.then((v) => v.default)
@@ -0,0 +1 @@
export default import("./test.js");
@@ -0,0 +1 @@
export default "test";
@@ -0,0 +1,6 @@
<!DOCTYPE html>
<html>
<body>
<script type="module" src="./index.mjs"></script>
</body>
</html>
49 changes: 34 additions & 15 deletions packages/core/integration-tests/test/javascript.js
Expand Up @@ -1036,31 +1036,42 @@ describe('javascript', function () {
let b = await bundle(
path.join(__dirname, '/integration/workers-module/index.js'),
{
mode: 'production',
defaultTargetOptions: {
shouldOptimize: false,
shouldScopeHoist: true,
},
},
);

assertBundles(b, [
{
assets: ['dedicated-worker.js', 'index.js'],
assets: ['dedicated-worker.js'],
},
{
name: 'index.js',
assets: ['index.js', 'bundle-url.js', 'get-worker-url.js'],
assets: [
'index.js',
'bundle-url.js',
'get-worker-url.js',
'bundle-manifest.js',
],
},
{
assets: ['shared-worker.js', 'index.js'],
assets: ['shared-worker.js'],
},
{
assets: ['index.js'],
},
]);

let dedicated, shared;
b.traverseBundles((bundle, ctx, traversal) => {
if (bundle.getMainEntry().filePath.endsWith('shared-worker.js')) {
let mainEntry = bundle.getMainEntry();
if (mainEntry && mainEntry.filePath.endsWith('shared-worker.js')) {
shared = bundle;
} else if (
bundle.getMainEntry().filePath.endsWith('dedicated-worker.js')
mainEntry &&
mainEntry.filePath.endsWith('dedicated-worker.js')
) {
dedicated = bundle;
}
Expand All @@ -1086,7 +1097,9 @@ describe('javascript', function () {
let b = await bundle(
path.join(__dirname, '/integration/workers-module/index.js'),
{
mode: 'production',
defaultTargetOptions: {
shouldOptimize: false,
shouldScopeHoist,
engines: {
browsers: '>= 0.25%',
Expand All @@ -1097,31 +1110,36 @@ describe('javascript', function () {

assertBundles(b, [
{
assets: [
'dedicated-worker.js',
!shouldScopeHoist && 'esmodule-helpers.js',
'index.js',
].filter(Boolean),
assets: ['dedicated-worker.js'],
},
{
name: 'index.js',
assets: ['index.js', 'bundle-url.js', 'get-worker-url.js'],
assets: [
'index.js',
'bundle-url.js',
'get-worker-url.js',
'bundle-manifest.js',
],
},
{
assets: [
!shouldScopeHoist && 'esmodule-helpers.js',
'shared-worker.js',
'index.js',
].filter(Boolean),
},
{
assets: ['shared-worker.js'],
},
]);

let dedicated, shared;
b.traverseBundles((bundle, ctx, traversal) => {
if (bundle.getMainEntry().filePath.endsWith('shared-worker.js')) {
let mainEntry = bundle.getMainEntry();
if (mainEntry && mainEntry.filePath.endsWith('shared-worker.js')) {
shared = bundle;
} else if (
bundle.getMainEntry().filePath.endsWith('dedicated-worker.js')
mainEntry &&
mainEntry.filePath.endsWith('dedicated-worker.js')
) {
dedicated = bundle;
}
Expand Down Expand Up @@ -1977,6 +1995,7 @@ describe('javascript', function () {
});

it('should contain duplicate assets in workers when in development', async () => {
if (process.env.PARCEL_TEST_EXPERIMENTAL_BUNDLER) return;
let b = await bundle(
path.join(__dirname, '/integration/worker-shared/index.js'),
{mode: 'development'},
Expand Down
6 changes: 5 additions & 1 deletion packages/core/integration-tests/test/scope-hoisting.js
Expand Up @@ -5063,7 +5063,11 @@ describe('scope hoisting', function () {
],
},
{assets: ['dep.js']},
{assets: ['async-has-dep.js', 'dep.js', 'get-dep.js']},
{
assets: process.env.PARCEL_TEST_EXPERIMENTAL_BUNDLER
? ['async-has-dep.js']
: ['async-has-dep.js', 'dep.js', 'get-dep.js'],
},
{assets: ['get-dep.js']},
]);

Expand Down
5 changes: 5 additions & 0 deletions packages/runtimes/js/src/JSRuntime.js
Expand Up @@ -552,6 +552,11 @@ function getRegisterCode(
idToName[bundle.publicId] = path.basename(nullthrows(bundle.name));

if (bundle !== entryBundle && isNewContext(bundle, bundleGraph)) {
for (let referenced of bundleGraph.getReferencedBundles(bundle)) {
idToName[referenced.publicId] = path.basename(
nullthrows(referenced.name),
);
}
// New contexts have their own manifests, so there's no need to continue.
actions.skipChildren();
}
Expand Down
2 changes: 1 addition & 1 deletion packages/transformers/js/native.js
Expand Up @@ -18,7 +18,7 @@ if (process.env.PARCEL_BUILD_ENV === 'production') {
} else if (process.env.PARCEL_SWC_WASM) {
const {transform} = require('./wasm/dist-node/parcel_js_swc_wasm.js');

module.exports.transform = function(config) {
module.exports.transform = function (config) {
let result = transform(config);
return {
...result,
Expand Down
18 changes: 9 additions & 9 deletions packages/utils/fs-write-stream-atomic/index.js
Expand Up @@ -64,25 +64,25 @@ function WriteStreamAtomic(path, options) {
// data has been written to our target stream. So we suppress
// finish from being emitted here, and only emit it after our
// target stream is closed and we've moved everything around.
WriteStreamAtomic.prototype.emit = function(event) {
WriteStreamAtomic.prototype.emit = function (event) {
if (event === 'finish') return this.__atomicStream.end();
return Writable.prototype.emit.apply(this, arguments);
};

WriteStreamAtomic.prototype._write = function(buffer, encoding, cb) {
WriteStreamAtomic.prototype._write = function (buffer, encoding, cb) {
var flushed = this.__atomicStream.write(buffer, encoding);
if (flushed) return cb();
this.__atomicStream.once('drain', cb);
};

function handleOpen(writeStream) {
return function(fd) {
return function (fd) {
writeStream.emit('open', fd);
};
}

function handleClose(writeStream) {
return function() {
return function () {
if (writeStream.__atomicClosed) return;
writeStream.__atomicClosed = true;
if (writeStream.__atomicChown) {
Expand Down Expand Up @@ -127,13 +127,13 @@ function handleClose(writeStream) {
var targetFileHash = crypto.createHash('sha512');

fs.createReadStream(writeStream.__atomicTmp)
.on('data', function(data, enc) {
.on('data', function (data, enc) {
tmpFileHash.update(data, enc);
})
.on('error', fileHashError)
.on('end', fileHashComplete);
fs.createReadStream(writeStream.__atomicTarget)
.on('data', function(data, enc) {
.on('data', function (data, enc) {
targetFileHash.update(data, enc);
})
.on('error', fileHashError)
Expand All @@ -157,7 +157,7 @@ function handleClose(writeStream) {
}

function cleanup(err) {
fs.unlink(writeStream.__atomicTmp, function() {
fs.unlink(writeStream.__atomicTmp, function () {
if (err) {
writeStream.emit('error', err);
writeStream.emit('close');
Expand All @@ -175,14 +175,14 @@ function handleClose(writeStream) {
// Delay the close to provide the same temporal separation a physical
// file operation would have– that is, the close event is emitted only
// after the async close operation completes.
setImmediate(function() {
setImmediate(function () {
writeStream.emit('close');
});
}
}

function handleError(writeStream) {
return function(er) {
return function (er) {
cleanupSync();
writeStream.emit('error', er);
writeStream.__atomicClosed = true;
Expand Down
10 changes: 2 additions & 8 deletions packages/utils/hash/browser.js
Expand Up @@ -49,13 +49,7 @@ function concatUint8Arrays(arrays) {
function toHex(arr) {
let dataView = new DataView(arr.buffer);
return (
dataView
.getUint32(0, true)
.toString(16)
.padStart(8, '0') +
dataView
.getUint32(4, true)
.toString(16)
.padStart(8, '0')
dataView.getUint32(0, true).toString(16).padStart(8, '0') +
dataView.getUint32(4, true).toString(16).padStart(8, '0')
);
}

0 comments on commit 38bb2ee

Please sign in to comment.