Skip to content

Commit 7c96476

Browse files
committed
Fixing a bug where split filenames might change too often
1 parent a426b8c commit 7c96476

File tree

2 files changed

+80
-26
lines changed

2 files changed

+80
-26
lines changed

lib/config-generator.js

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -412,22 +412,12 @@ class ConfigGenerator {
412412
chunks: this.webpackConfig.shouldSplitEntryChunks ? 'all' : 'async'
413413
};
414414

415-
// hash the split filenames in production to protect exposing entry names
415+
// This causes the split filenames (& internal names) to be,
416+
// for example, 0.js. This is needed so that the filename
417+
// doesn't change suddenly when another entry needs that same
418+
// shared code (e.g. vendor~entry1~entry2.js).
416419
if (this.webpackConfig.shouldSplitEntryChunks && this.webpackConfig.isProduction()) {
417-
// taken from SplitChunksPlugin
418-
const hashFilename = name => {
419-
return crypto
420-
.createHash('md4')
421-
.update(name)
422-
.digest('hex')
423-
.slice(0, 8);
424-
};
425-
426-
splitChunks.name = function(module, chunks, cacheGroup) {
427-
const names = chunks.map(c => c.name);
428-
429-
return cacheGroup + '~' + hashFilename(names.join('~'));
430-
};
420+
splitChunks.name = false;
431421
}
432422

433423
if (this.webpackConfig.sharedCommonsEntryName) {

test/functional.js

Lines changed: 75 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,7 @@ function createWebpackConfig(outputDirName = '', command, argv = {}) {
3131
}
3232

3333
function convertToManifestPath(assetSrc, webpackConfig) {
34-
const manifestData = JSON.parse(
35-
fs.readFileSync(path.join(webpackConfig.outputPath, 'manifest.json'), 'utf8')
36-
);
34+
const manifestData = JSON.parse(readOutputFileContents('manifest.json', webpackConfig));
3735

3836
if (typeof manifestData[assetSrc] === 'undefined') {
3937
throw new Error(`Path ${assetSrc} not found in manifest!`);
@@ -42,6 +40,26 @@ function convertToManifestPath(assetSrc, webpackConfig) {
4240
return manifestData[assetSrc];
4341
}
4442

43+
function readOutputFileContents(filename, config) {
44+
const fullPath = path.join(config.outputPath, filename);
45+
46+
if (!fs.existsSync(fullPath)) {
47+
throw new Error(`Output file "${filename}" does not exist.`);
48+
}
49+
50+
return fs.readFileSync(fullPath, 'utf8');
51+
}
52+
53+
function getEntrypointData(config, entryName) {
54+
const entrypointsData = JSON.parse(readOutputFileContents('entrypoints.json', config));
55+
56+
if (typeof entrypointsData.entrypoints[entryName] === 'undefined') {
57+
throw new Error(`The entry ${entryName} was not found!`);
58+
}
59+
60+
return entrypointsData.entrypoints[entryName];
61+
}
62+
4563
describe('Functional tests using webpack', function() {
4664
// being functional tests, these can take quite long
4765
this.timeout(8000);
@@ -1839,24 +1857,70 @@ module.exports = {
18391857
return config;
18401858
};
18411859

1842-
const getMain3Contents = function(config) {
1843-
const fullPath = path.join(config.outputPath, 'main3.js');
1860+
const configA = createSimilarConfig(false);
1861+
const configB = createSimilarConfig(true);
1862+
1863+
testSetup.runWebpack(configA, () => {
1864+
testSetup.runWebpack(configB, () => {
1865+
const main3Contents = readOutputFileContents('main3.js', configA);
1866+
const finalMain3Contents = readOutputFileContents('main3.js', configB);
18441867

1845-
if (!fs.existsSync(fullPath)) {
1846-
throw new Error('Output file "main3.js" does not exist.');
1868+
if (finalMain3Contents !== main3Contents) {
1869+
throw new Error(`Contents after first compile do not match after second compile: \n\n ${main3Contents} \n\n versus \n\n ${finalMain3Contents} \n`);
1870+
}
1871+
1872+
done();
1873+
});
1874+
});
1875+
});
1876+
1877+
it('Do not change contents or filenames when more modules require the same split contents', (done) => {
1878+
const createSimilarConfig = function(includeExtraEntry) {
1879+
const config = createWebpackConfig('web/build', 'production');
1880+
config.addEntry('main1', ['./js/code_splitting', 'preact']);
1881+
config.addEntry('main3', ['./js/no_require', 'preact']);
1882+
if (includeExtraEntry) {
1883+
config.addEntry('main4', ['./js/eslint', 'preact']);
1884+
}
1885+
config.setPublicPath('/build');
1886+
config.splitEntryChunks();
1887+
1888+
return config;
1889+
};
1890+
1891+
const getSplitVendorJsPath = function(config) {
1892+
const entrypointData = getEntrypointData(config, 'main3');
1893+
1894+
const splitFiles = entrypointData.js.filter(filename => {
1895+
return filename !== '/build/runtime.js' && filename !== '/build/main3.js';
1896+
});
1897+
1898+
// sanity check
1899+
if (splitFiles.length !== 1) {
1900+
throw new Error(`Unexpected number (${splitFiles.length}) of split files for main3 entry`);
18471901
}
18481902

1849-
return fs.readFileSync(fullPath, 'utf8');
1903+
return splitFiles[0];
18501904
};
18511905

18521906
const configA = createSimilarConfig(false);
18531907
const configB = createSimilarConfig(true);
18541908

18551909
testSetup.runWebpack(configA, () => {
1856-
const main3Contents = getMain3Contents(configA);
1857-
18581910
testSetup.runWebpack(configB, () => {
1859-
const finalMain3Contents = getMain3Contents(configB);
1911+
const vendorPath = getSplitVendorJsPath(configA);
1912+
const finalVendorPath = getSplitVendorJsPath(configB);
1913+
1914+
// make sure that the filename of the split vendor file didn't change,
1915+
// even though an additional entry is now sharing its contents
1916+
if (finalVendorPath !== vendorPath) {
1917+
throw new Error(`Vendor filename changed! Before ${vendorPath} and after ${finalVendorPath}.`);
1918+
}
1919+
1920+
// make sure that, internally, the split chunk name did not change,
1921+
// which would cause the contents of main3 to suddenly change
1922+
const main3Contents = readOutputFileContents('main3.js', configA);
1923+
const finalMain3Contents = readOutputFileContents('main3.js', configB);
18601924

18611925
if (finalMain3Contents !== main3Contents) {
18621926
throw new Error(`Contents after first compile do not match after second compile: \n\n ${main3Contents} \n\n versus \n\n ${finalMain3Contents} \n`);

0 commit comments

Comments
 (0)