Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Symbol Propagation] Non-deterministic bundle hashes #8212

Merged
merged 36 commits into from Jul 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
ddf86c7
wip test case
gorakong May 12, 2022
c6c7f68
Merge branch 'v2' of https://github.com/parcel-bundler/parcel into gk…
gorakong Jun 6, 2022
1621bf8
Merge branch 'v2' of https://github.com/parcel-bundler/parcel into gk…
gorakong Jun 6, 2022
22201bb
working repro
gorakong Jun 11, 2022
7e95bc0
test case
gorakong Jun 14, 2022
581daf5
Merge branch 'v2' of https://github.com/parcel-bundler/parcel into gk…
gorakong Jun 14, 2022
0cdd233
Merge branch 'gkong/nondeterministic-shared-bundle-hashes' of https:/…
gorakong Jun 14, 2022
71f3095
cleanup
gorakong Jun 14, 2022
3ec5650
more cleanup
gorakong Jun 14, 2022
6e5c7f2
Merge branch 'v2' into gkong/nondeterministic-shared-bundle-hashes
gorakong Jun 16, 2022
8fcd26c
Merge branch 'v2' of https://github.com/parcel-bundler/parcel into gk…
gorakong Jun 16, 2022
0341dc5
Merge branch 'gkong/nondeterministic-shared-bundle-hashes' of https:/…
gorakong Jun 16, 2022
d97c389
wip eliminate non-determinism post loading asset
gorakong Jun 16, 2022
0a6eb57
Merge branch 'v2' into gkong/nondeterministic-shared-bundle-hashes
gorakong Jun 17, 2022
ceae061
Merge branch 'v2' of https://github.com/parcel-bundler/parcel into gk…
gorakong Jun 21, 2022
292d3be
Merge branch 'gkong/nondeterministic-shared-bundle-hashes' of https:/…
gorakong Jun 21, 2022
d755c1e
custom transformer
gorakong Jun 21, 2022
b00adb1
Merge branch 'v2' of https://github.com/parcel-bundler/parcel into gk…
gorakong Jul 5, 2022
c45a72d
proposed fix
gorakong Jul 8, 2022
8d63b7a
Merge commit 'c45a72d4b' into gkong/nondeterministic-shared-bundle-ha…
gorakong Jul 8, 2022
ade94ec
test case
gorakong Jun 14, 2022
a38b60f
remove comments
gorakong Jul 8, 2022
4aef4e4
move sorting outside of callback
gorakong Jul 11, 2022
3111eab
Merge branch 'v2' into gkong/nondeterministic-shared-bundle-hashes
gorakong Jul 12, 2022
1a11b35
stop tracking changes to usedSymbolsDown
gorakong Jul 12, 2022
74117ce
Merge https://github.com/parcel-bundler/parcel into gkong/nondetermin…
gorakong Jul 12, 2022
5f0d937
Merge branch 'gkong/nondeterministic-shared-bundle-hashes' of https:/…
gorakong Jul 12, 2022
c72b900
end workerfarm
gorakong Jul 13, 2022
69c2ad1
use workerfarm from test-utils
gorakong Jul 14, 2022
486b0d9
remove workerFarmZeroConcurrent and create new temporary outputFS and…
gorakong Jul 20, 2022
a730135
Merge branch 'v2' into gkong/nondeterministic-shared-bundle-hashes
gorakong Jul 21, 2022
a84a1c7
Merge branch 'v2' of https://github.com/parcel-bundler/parcel into gk…
gorakong Jul 21, 2022
70ddf42
cleanup after react-refresh test
gorakong Jul 22, 2022
9e01d85
Merge branch 'gkong/nondeterministic-shared-bundle-hashes' of https:/…
gorakong Jul 22, 2022
3df2a78
oops
gorakong Jul 22, 2022
4a6e55f
Merge branch 'v2' into gkong/nondeterministic-shared-bundle-hashes
mischnic Jul 26, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions packages/core/core/src/requests/AssetGraphRequest.js
Expand Up @@ -260,6 +260,10 @@ export class AssetGraphBuilder {
}

propagateSymbols() {
// Keep track of dependencies that have changes to their used symbols,
// so we can sort them after propagation.
let changedDeps = new Set<DependencyNode>();

// Propagate the requested symbols down from the root to the leaves
this.propagateSymbolsDown((assetNode, incomingDeps, outgoingDeps) => {
if (!assetNode.value.symbols) return;
Expand Down Expand Up @@ -568,6 +572,7 @@ export class AssetGraphBuilder {
}

if (!equalSet(incomingDepUsedSymbolsUpOld, incomingDep.usedSymbolsUp)) {
changedDeps.add(incomingDep);
incomingDep.usedSymbolsUpDirtyUp = true;
}

Expand Down Expand Up @@ -595,6 +600,12 @@ export class AssetGraphBuilder {
}
return errors;
});
// Sort usedSymbolsUp so they are a consistent order across builds.
// This ensures a consistent ordering of these symbols when packaging.
// See https://github.com/parcel-bundler/parcel/pull/8212
for (let dep of changedDeps) {
dep.usedSymbolsUp = new Set([...dep.usedSymbolsUp].sort());
}
}

propagateSymbolsDown(
Expand Down
@@ -0,0 +1,6 @@
<!DOCTYPE html>
<html>
<body>
<script type="module" src="./index.js"></script>
</body>
</html>
@@ -0,0 +1,7 @@
import {foo,bar} from './library';

function other() {
return foo+bar;
}

export {other};
@@ -0,0 +1,7 @@
import {baz, bag} from '../utils';

function bar() {
return baz+bag;
}

export {bar};
@@ -0,0 +1,7 @@
import {bag} from '../utils';

function foo() {
return bag;
}

export {foo};
@@ -0,0 +1,2 @@
export {foo} from './foo';
export {bar} from './bar';
@@ -0,0 +1,3 @@
{
"sideEffects": false
}
@@ -0,0 +1,7 @@
export function bag() {
return 'bag';
};

export function baz() {
return bag() + 'baz';
}
@@ -0,0 +1,2 @@
export * from './bag';
export * from './empty';
@@ -0,0 +1,3 @@
{
"sideEffects": false
}
12 changes: 10 additions & 2 deletions packages/core/integration-tests/test/react-refresh.js
Expand Up @@ -37,10 +37,14 @@ if (MessageChannel) {

let b,
root,
randoms = {};
randoms,
subscription,
window = {};

beforeEach(async () => {
({b, root, randoms} = await setup(path.join(testDir, 'index.html')));
({b, root, randoms, subscription, window} = await setup(
path.join(testDir, 'index.html'),
));
});

it('retains state in functional components', async function () {
Expand All @@ -62,6 +66,10 @@ if (MessageChannel) {
assert.equal(randoms.fooNum, fooNum);
assert.equal(fooText, 'OtherFunctional');
});

afterEach(async () => {
await cleanup({subscription, window});
});
});

describe('synchronous', () => {
Expand Down
86 changes: 86 additions & 0 deletions packages/core/integration-tests/test/scope-hoisting.js
Expand Up @@ -2,6 +2,7 @@ import assert from 'assert';
import path from 'path';
import nullthrows from 'nullthrows';
import {normalizePath} from '@parcel/utils';
import {createWorkerFarm} from '@parcel/core';
import {md} from '@parcel/diagnostic';
import {
assertBundles,
Expand Down Expand Up @@ -5532,4 +5533,89 @@ describe('scope hoisting', function () {
let output = await run(b);
assert.strictEqual(output, 'foo');
});

it('produce the same bundle hash regardless of transformation order', async function () {
let testDir = path.join(
__dirname,
'integration/scope-hoisting/es6/non-deterministic-bundle-hashes',
);

const waitHandler = (fileToDelay, fileToWaitFor) => {
const waitMap = new Map();

function wait(filePath) {
if (waitMap.has(filePath)) {
return Promise.resolve();
}
return new Promise(resolve => {
waitMap.set(filePath, resolve);
});
}
// a set of filepaths that have been read
function seen(filePath) {
// check map of things we're waiting for to resolved promises
let promisesToResolve = waitMap.get(filePath);
if (promisesToResolve) {
// if we find any, we call it
promisesToResolve();
}
waitMap.set(filePath, null);
}

return {
get(target, prop) {
let original = Reflect.get(...arguments);
if (prop === 'readFile') {
return async function (...args) {
if (args[0].includes(fileToDelay)) {
await wait(fileToWaitFor);
}
let result = await original.apply(this, args);
seen(path.basename(args[0]));
return result;
};
}
return original;
},
};
};

let workerFarm = createWorkerFarm({
maxConcurrentWorkers: 0,
});

let slowFooFS = new Proxy(overlayFS, waitHandler('foo.js', 'bar.js'));

try {
let b = await bundle(path.join(testDir, 'index.html'), {
inputFS: slowFooFS,
outputFS: slowFooFS,
shouldDisableCache: true,
workerFarm,
});

let bundleHashDelayFoo = b
.getBundles()
.find(b => b.filePath.endsWith('.js') && b.filePath.includes('index'))
.filePath.split('.')[1];

let slowBarFS = new Proxy(overlayFS, waitHandler('bar.js', 'foo.js'));

let b2 = await bundle(path.join(testDir, 'index.html'), {
inputFS: slowBarFS,
outputFS: slowBarFS,
shouldDisableCache: true,
workerFarm,
});

let bundleHashDelayBar = b2
.getBundles()
.find(b => b.filePath.endsWith('.js') && b.filePath.includes('index'))
.filePath.split('.')[1];

assert.strictEqual(bundleHashDelayFoo, bundleHashDelayBar);
} finally {
await workerFarm.end();
}
});
});