Skip to content

Commit

Permalink
Respect rendered exports when generating chunk hashes (#2695)
Browse files Browse the repository at this point in the history
* Create hash test type and red test

* Consider actually exported names in hash, resolve #2692

* Add tests for other hashing situations and fix addon hashing

* Rework test to check hashes and contents of all files and throw if two
files with different content share the same hash; fix another issue for
exports that was uncovered by this.

* Remove unnecessary expectations
  • Loading branch information
lukastaegert committed Feb 17, 2019
1 parent 14a17af commit 4d082b0
Show file tree
Hide file tree
Showing 60 changed files with 252 additions and 47 deletions.
5 changes: 4 additions & 1 deletion src/Chunk.ts
Expand Up @@ -667,6 +667,7 @@ export default class Chunk {
if (!this.renderedSource) return '';
const hash = sha256();
hash.update(this.renderedSource.toString());
hash.update(Object.keys(this.exportNames).join(','));
return (this.renderedHash = hash.digest('hex'));
}

Expand Down Expand Up @@ -705,7 +706,9 @@ export default class Chunk {
private computeContentHashWithDependencies(addons: Addons, options: OutputOptions): string {
const hash = sha256();

hash.update(addons.hash);
hash.update(
[addons.intro, addons.outro, addons.banner, addons.footer].map(addon => addon || '').join(':')
);
hash.update(options.format);
this.visitDependencies(dep => {
if (dep instanceof ExternalModule) hash.update(':' + dep.renderPath);
Expand Down
5 changes: 1 addition & 4 deletions src/utils/addons.ts
Expand Up @@ -7,7 +7,6 @@ export interface Addons {
outro?: string;
banner?: string;
footer?: string;
hash: Uint8Array;
}

function evalIfFn(strOrFn: string | (() => string | Promise<string>)): string | Promise<string> {
Expand Down Expand Up @@ -38,9 +37,7 @@ export function createAddons(graph: Graph, options: OutputOptions): Promise<Addo
if (banner.length) banner += '\n';
if (footer.length) footer = '\n' + footer;

const hash = new Uint8Array(4);

return { intro, outro, banner, footer, hash };
return { intro, outro, banner, footer };
})
.catch(
(err): any => {
Expand Down
@@ -0,0 +1,5 @@
define(['./chunk-main2-0a527b43-amd.js'], function (main2) { 'use strict';

main2.log(main2.dep);

});

This file was deleted.

This file was deleted.

@@ -0,0 +1,7 @@
define(['./chunk-main2-0a527b43-amd.js'], function (main2) { 'use strict';



return main2.log;

});
@@ -0,0 +1,5 @@
'use strict';

var main2 = require('./chunk-main2-328c93f2-cjs.js');

main2.log(main2.dep);

This file was deleted.

This file was deleted.

@@ -0,0 +1,7 @@
'use strict';

var main2 = require('./chunk-main2-328c93f2-cjs.js');



module.exports = main2.log;

This file was deleted.

@@ -0,0 +1,3 @@
import { a as log, b as dep } from './chunk-main2-d7d48284-esm.js';

log(dep);

This file was deleted.

@@ -0,0 +1 @@
export { a as default } from './chunk-main2-d7d48284-esm.js';
@@ -1,4 +1,4 @@
System.register(['./chunk-main2-bd8b7ffb-system.js'], function (exports, module) {
System.register(['./chunk-main2-d134c6d9-system.js'], function (exports, module) {
'use strict';
var log, dep;
return {
Expand Down
@@ -1,4 +1,4 @@
System.register(['./chunk-main2-bd8b7ffb-system.js'], function (exports, module) {
System.register(['./chunk-main2-d134c6d9-system.js'], function (exports, module) {
'use strict';
return {
setters: [function (module) {
Expand Down
Expand Up @@ -17,13 +17,13 @@ module.exports = {
assert.equal(
code,
'\n' +
`${color}//→ entry1-a47394dc.js:${standard}\n` +
`${color}//→ entry1-d8c4343d.js:${standard}\n` +
"console.log('main1');\n" +
'\n' +
`${color}//→ Entry 2-d6c85f3f.js:${standard}\n` +
`${color}//→ Entry 2-99f48ca0.js:${standard}\n` +
"console.log('main2');\n" +
'\n' +
`${color}//→ main3-22d8845f.js:${standard}\n` +
`${color}//→ main3-a6240449.js:${standard}\n` +
"console.log('main3');\n"
);
}
Expand Down
6 changes: 3 additions & 3 deletions test/cli/samples/code-splitting-named-inputs/_config.js
Expand Up @@ -17,13 +17,13 @@ module.exports = {
assert.equal(
code,
'\n' +
`${color}//→ entry1-a47394dc.js:${standard}\n` +
`${color}//→ entry1-d8c4343d.js:${standard}\n` +
"console.log('main1');\n" +
'\n' +
`${color}//→ Entry 2-d6c85f3f.js:${standard}\n` +
`${color}//→ Entry 2-99f48ca0.js:${standard}\n` +
"console.log('main2');\n" +
'\n' +
`${color}//→ main3-22d8845f.js:${standard}\n` +
`${color}//→ main3-a6240449.js:${standard}\n` +
"console.log('main3');\n"
);
}
Expand Down
47 changes: 47 additions & 0 deletions test/file-hashes/index.js
@@ -0,0 +1,47 @@
const path = require('path');
const rollup = require('../../dist/rollup');
const { extend, runTestSuiteWithSamples } = require('../utils.js');
const assert = require('assert');

runTestSuiteWithSamples('file hashes', path.resolve(__dirname, 'samples'), (dir, config) => {
(config.skip ? describe.skip : config.solo ? describe.only : describe)(
path.basename(dir) + ': ' + config.description,
() => {
it(`generates correct hashes`, () => {
process.chdir(dir);
return Promise.all(
[config.options1, config.options2].map(options =>
rollup
.rollup(options)
.then(bundle =>
bundle.generate(
extend(
{ format: 'esm', chunkFileNames: '[hash]', entryFileNames: '[hash]' },
options.output
)
)
)
)
).then(([generated1, generated2]) => {
const fileContentsByHash = new Map();
addAndCheckFileContentsByHash(fileContentsByHash, generated1);
addAndCheckFileContentsByHash(fileContentsByHash, generated2);
});
});
}
);
});

function addAndCheckFileContentsByHash(fileContentsByHash, generated) {
for (const chunk of generated.output) {
const hash = chunk.fileName;
if (fileContentsByHash.has(hash)) {
assert.equal(
fileContentsByHash.get(hash),
chunk.code,
'Two chunks contained different code even though the hashes were the same.'
);
}
fileContentsByHash.set(hash, chunk.code);
}
}
15 changes: 15 additions & 0 deletions test/file-hashes/samples/banner/_config.js
@@ -0,0 +1,15 @@
module.exports = {
description: 'creates different hashes if the banner differs',
options1: {
input: 'main',
output: {
banner: 'console.log(1);'
}
},
options2: {
input: 'main',
output: {
banner: 'console.log(2);'
}
}
};
1 change: 1 addition & 0 deletions test/file-hashes/samples/banner/main.js
@@ -0,0 +1 @@
console.log('main');
9 changes: 9 additions & 0 deletions test/file-hashes/samples/content/_config.js
@@ -0,0 +1,9 @@
module.exports = {
description: 'creates different hashes if the content differs',
options1: {
input: { main: 'main1', dep: 'dep1' }
},
options2: {
input: { main: 'main2', dep: 'dep2' }
}
};
1 change: 1 addition & 0 deletions test/file-hashes/samples/content/dep1.js
@@ -0,0 +1 @@
console.log('dep');
1 change: 1 addition & 0 deletions test/file-hashes/samples/content/dep2.js
@@ -0,0 +1 @@
console.log('dep');
3 changes: 3 additions & 0 deletions test/file-hashes/samples/content/main1.js
@@ -0,0 +1,3 @@
import './dep1';

console.log('main1');
3 changes: 3 additions & 0 deletions test/file-hashes/samples/content/main2.js
@@ -0,0 +1,3 @@
import './dep2';

console.log('main2');
9 changes: 9 additions & 0 deletions test/file-hashes/samples/dependency-content/_config.js
@@ -0,0 +1,9 @@
module.exports = {
description: 'creates different hashes if the content of dependencies differs',
options1: {
input: { main: 'main1', dep: 'dep1' }
},
options2: {
input: { main: 'main2', dep: 'dep2' }
}
};
1 change: 1 addition & 0 deletions test/file-hashes/samples/dependency-content/dep1.js
@@ -0,0 +1 @@
console.log('dep1');
1 change: 1 addition & 0 deletions test/file-hashes/samples/dependency-content/dep2.js
@@ -0,0 +1 @@
console.log('dep2');
3 changes: 3 additions & 0 deletions test/file-hashes/samples/dependency-content/main1.js
@@ -0,0 +1,3 @@
import './dep1';

console.log('main');
3 changes: 3 additions & 0 deletions test/file-hashes/samples/dependency-content/main2.js
@@ -0,0 +1,3 @@
import './dep2';

console.log('main');
9 changes: 9 additions & 0 deletions test/file-hashes/samples/exports/_config.js
@@ -0,0 +1,9 @@
module.exports = {
description: 'creates different hashes if the generated exports differ',
options1: {
input: ['main1', 'other']
},
options2: {
input: ['main2', 'other']
}
};
2 changes: 2 additions & 0 deletions test/file-hashes/samples/exports/dep.js
@@ -0,0 +1,2 @@
export const mightNotBeImported = 42;
console.log(mightNotBeImported);
3 changes: 3 additions & 0 deletions test/file-hashes/samples/exports/main1.js
@@ -0,0 +1,3 @@
import {mightNotBeImported} from './dep';

console.log('main1', mightNotBeImported);
3 changes: 3 additions & 0 deletions test/file-hashes/samples/exports/main2.js
@@ -0,0 +1,3 @@
import './dep';

console.log('main2');
3 changes: 3 additions & 0 deletions test/file-hashes/samples/exports/other.js
@@ -0,0 +1,3 @@
import './dep';

console.log('other');
11 changes: 11 additions & 0 deletions test/file-hashes/samples/external-dependency-names/_config.js
@@ -0,0 +1,11 @@
module.exports = {
description: 'creates different hashes if the names of external dependencies differ',
options1: {
input: { mainA: 'main1a', mainB: 'main1b' },
external: ['external', 'external1']
},
options2: {
input: { mainA: 'main2a', mainB: 'main2b' },
external: ['external', 'external2']
}
};
3 changes: 3 additions & 0 deletions test/file-hashes/samples/external-dependency-names/main1a.js
@@ -0,0 +1,3 @@
import 'external';

console.log('main');
3 changes: 3 additions & 0 deletions test/file-hashes/samples/external-dependency-names/main1b.js
@@ -0,0 +1,3 @@
import 'external1';

console.log('main');
3 changes: 3 additions & 0 deletions test/file-hashes/samples/external-dependency-names/main2a.js
@@ -0,0 +1,3 @@
import 'external';

console.log('main');
3 changes: 3 additions & 0 deletions test/file-hashes/samples/external-dependency-names/main2b.js
@@ -0,0 +1,3 @@
import 'external2';

console.log('main');
15 changes: 15 additions & 0 deletions test/file-hashes/samples/footer/_config.js
@@ -0,0 +1,15 @@
module.exports = {
description: 'creates different hashes if the footer differs',
options1: {
input: 'main',
output: {
footer: 'console.log(1);'
}
},
options2: {
input: 'main',
output: {
footer: 'console.log(2);'
}
}
};
1 change: 1 addition & 0 deletions test/file-hashes/samples/footer/main.js
@@ -0,0 +1 @@
console.log('main');
15 changes: 15 additions & 0 deletions test/file-hashes/samples/format/_config.js
@@ -0,0 +1,15 @@
module.exports = {
description: 'creates different hashes if the format differs',
options1: {
input: 'main',
output: {
format: 'esm'
}
},
options2: {
input: 'main',
output: {
format: 'cjs'
}
}
};
1 change: 1 addition & 0 deletions test/file-hashes/samples/format/main.js
@@ -0,0 +1 @@
export const x = 1;
15 changes: 15 additions & 0 deletions test/file-hashes/samples/intro/_config.js
@@ -0,0 +1,15 @@
module.exports = {
description: 'creates different hashes if the intro differs',
options1: {
input: 'main',
output: {
intro: 'console.log(1);'
}
},
options2: {
input: 'main',
output: {
intro: 'console.log(2);'
}
}
};
1 change: 1 addition & 0 deletions test/file-hashes/samples/intro/main.js
@@ -0,0 +1 @@
console.log('main');
15 changes: 15 additions & 0 deletions test/file-hashes/samples/outro/_config.js
@@ -0,0 +1,15 @@
module.exports = {
description: 'creates different hashes if the outro differs',
options1: {
input: 'main',
output: {
outro: 'console.log(1);'
}
},
options2: {
input: 'main',
output: {
outro: 'console.log(2);'
}
}
};
1 change: 1 addition & 0 deletions test/file-hashes/samples/outro/main.js
@@ -0,0 +1 @@
console.log('main');

0 comments on commit 4d082b0

Please sign in to comment.