Skip to content

Commit 447c191

Browse files
fix: apply final hashes deterministically with stable placeholders set (#5644)
Co-authored-by: Lukas Taegert-Atkinson <lukastaegert@users.noreply.github.com>
1 parent 184bc4e commit 447c191

File tree

2 files changed

+66
-3
lines changed

2 files changed

+66
-3
lines changed

src/utils/renderChunks.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,11 @@ export async function renderChunks(
5555
const getHash = hasherByType[outputOptions.hashCharacters];
5656
const chunkGraph = getChunkGraph(chunks);
5757
const {
58+
hashDependenciesByPlaceholder,
5859
initialHashesByPlaceholder,
5960
nonHashedChunksWithPlaceholders,
60-
renderedChunksByPlaceholder,
61-
hashDependenciesByPlaceholder
61+
placeholders,
62+
renderedChunksByPlaceholder
6263
} = await transformChunksAndGenerateContentHashes(
6364
renderedChunks,
6465
chunkGraph,
@@ -71,6 +72,7 @@ export async function renderChunks(
7172
renderedChunksByPlaceholder,
7273
hashDependenciesByPlaceholder,
7374
initialHashesByPlaceholder,
75+
placeholders,
7476
bundle,
7577
getHash
7678
);
@@ -283,6 +285,7 @@ async function transformChunksAndGenerateContentHashes(
283285
hashDependenciesByPlaceholder,
284286
initialHashesByPlaceholder,
285287
nonHashedChunksWithPlaceholders,
288+
placeholders,
286289
renderedChunksByPlaceholder
287290
};
288291
}
@@ -291,11 +294,13 @@ function generateFinalHashes(
291294
renderedChunksByPlaceholder: Map<string, RenderedChunkWithPlaceholders>,
292295
hashDependenciesByPlaceholder: Map<string, HashResult>,
293296
initialHashesByPlaceholder: Map<string, string>,
297+
placeholders: Set<string>,
294298
bundle: OutputBundleWithPlaceholders,
295299
getHash: GetHash
296300
) {
297301
const hashesByPlaceholder = new Map<string, string>(initialHashesByPlaceholder);
298-
for (const [placeholder, { fileName }] of renderedChunksByPlaceholder) {
302+
for (const placeholder of placeholders) {
303+
const { fileName } = renderedChunksByPlaceholder.get(placeholder)!;
299304
let contentToHash = '';
300305
const hashDependencyPlaceholders = new Set<string>([placeholder]);
301306
for (const dependencyPlaceholder of hashDependencyPlaceholders) {

test/misc/misc.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,64 @@ describe('misc', () => {
114114
});
115115
});
116116

117+
it('applies consistent hashes regardless of chunk transform order', async () => {
118+
const FILES = {
119+
main: `
120+
import('folder1/dupe').then(({dupe}) => console.log(dupe));
121+
import('folder2/dupe').then(({dupe}) => console.log(dupe));
122+
`,
123+
'folder1/dupe': `export const dupe = 'dupe content';`,
124+
'folder2/dupe': `export const dupe = 'dupe content';`
125+
};
126+
127+
async function buildBundle(delayedChunk) {
128+
const bundle = await rollup.rollup({
129+
input: 'main',
130+
plugins: [
131+
loader(FILES),
132+
{
133+
name: 'delay-chunk',
134+
async renderChunk(_, chunk) {
135+
if (chunk.facadeModuleId === delayedChunk) {
136+
await new Promise(resolve => setTimeout(resolve, 100));
137+
}
138+
return null;
139+
}
140+
}
141+
]
142+
});
143+
return bundle.generate({
144+
format: 'es',
145+
chunkFileNames: '[name]-[hash].js'
146+
});
147+
}
148+
149+
const { output: output1 } = await buildBundle('folder1/dupe');
150+
const { output: output2 } = await buildBundle('folder2/dupe');
151+
152+
assert.strictEqual(
153+
output1.length,
154+
output2.length,
155+
'Both outputs should have the same number of chunks'
156+
);
157+
158+
const sortedOutput1 = output1.sort((a, b) => a.fileName.localeCompare(b.fileName));
159+
const sortedOutput2 = output2.sort((a, b) => a.fileName.localeCompare(b.fileName));
160+
161+
for (let index = 0; index < sortedOutput1.length; index++) {
162+
assert.strictEqual(
163+
sortedOutput1[index].fileName,
164+
sortedOutput2[index].fileName,
165+
`Chunk ${index} should have the same filename in both outputs`
166+
);
167+
assert.strictEqual(
168+
sortedOutput1[index].code,
169+
sortedOutput2[index].code,
170+
`Chunk ${index} should have the same code in both outputs`
171+
);
172+
}
173+
});
174+
117175
it('ignores falsy plugins', () =>
118176
rollup.rollup({
119177
input: 'x',

0 commit comments

Comments
 (0)