Skip to content

Commit b09dc06

Browse files
Charles LydingZhicheng Wang
Charles Lyding
authored and
Zhicheng Wang
committed
fix(@angular/cli): stabilize webpack module identifiers
This provides increased viability for long-term caching by limiting the changes to the content of output files due to webpack module identifier changes. Close angular#4733
1 parent 1dc3adb commit b09dc06

File tree

3 files changed

+76
-32
lines changed

3 files changed

+76
-32
lines changed

packages/@angular/cli/models/webpack-configs/production.ts

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export const getProdConfig = function (wco: WebpackConfigOptions) {
6969
new webpack.DefinePlugin({
7070
'process.env.NODE_ENV': JSON.stringify('production')
7171
}),
72+
new (<any>webpack).HashedModuleIdsPlugin(),
7273
new webpack.optimize.UglifyJsPlugin(<any>{
7374
mangle: { screw_ie8: true },
7475
compress: { screw_ie8: true, warnings: buildOptions.verbose },

packages/@angular/cli/tasks/eject.ts

+3
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ class JsonWebpackSerializer {
175175
case webpack.NoEmitOnErrorsPlugin:
176176
this._addImport('webpack', 'NoEmitOnErrorsPlugin');
177177
break;
178+
case (<any>webpack).HashedModuleIdsPlugin:
179+
this._addImport('webpack', 'HashedModuleIdsPlugin');
180+
break;
178181
case webpack.optimize.UglifyJsPlugin:
179182
this._addImport('webpack.optimize', 'UglifyJsPlugin');
180183
break;

tests/e2e/tests/build/chunk-hash.ts

+72-32
Original file line numberDiff line numberDiff line change
@@ -5,52 +5,92 @@ import {ng} from '../../utils/process';
55
import {writeFile} from '../../utils/fs';
66
import {addImportToModule} from '../../utils/ast';
77

8+
const OUTPUT_RE = /(main|polyfills|vendor|inline|styles|\d+)\.[a-z0-9]+\.(chunk|bundle)\.(js|css)$/;
9+
10+
function generateFileHashMap(): Map<string, string> {
11+
const hashes = new Map<string, string>();
12+
13+
fs.readdirSync('./dist')
14+
.forEach(name => {
15+
if (!name.match(OUTPUT_RE)) {
16+
return;
17+
}
18+
19+
const [module, hash] = name.split('.');
20+
hashes.set(module, hash);
21+
});
22+
23+
return hashes;
24+
}
25+
26+
function validateHashes(
27+
oldHashes: Map<string, string>,
28+
newHashes: Map<string, string>,
29+
shouldChange: Array<string>): void {
30+
31+
console.log(' Validating hashes...');
32+
console.log(` Old hashes: ${JSON.stringify([...oldHashes])}`);
33+
console.log(` New hashes: ${JSON.stringify([...newHashes])}`);
34+
35+
oldHashes.forEach((hash, module) => {
36+
if (hash == newHashes.get(module)) {
37+
if (shouldChange.includes(module)) {
38+
throw new Error(`Module "${module}" did not change hash (${hash})...`);
39+
}
40+
} else if (!shouldChange.includes(module)) {
41+
throw new Error(`Module "${module}" changed hash (${hash})...`);
42+
}
43+
});
44+
}
845

946
export default function() {
10-
const oldHashes: {[module: string]: string} = {};
11-
const newHashes: {[module: string]: string} = {};
47+
let oldHashes: Map<string, string>;
48+
let newHashes: Map<string, string>;
1249
// First, collect the hashes.
1350
return Promise.resolve()
1451
.then(() => ng('generate', 'module', 'lazy', '--routing'))
1552
.then(() => addImportToModule('src/app/app.module.ts', oneLine`
1653
RouterModule.forRoot([{ path: "lazy", loadChildren: "./lazy/lazy.module#LazyModule" }])
1754
`, '@angular/router'))
55+
.then(() => addImportToModule(
56+
'src/app/app.module.ts', 'ReactiveFormsModule', '@angular/forms'))
1857
.then(() => ng('build', '--prod'))
1958
.then(() => {
20-
fs.readdirSync('./dist')
21-
.forEach(name => {
22-
if (!name.match(/(main|inline|styles|\d+)\.[a-z0-9]+\.bundle\.(js|css)/)) {
23-
return;
24-
}
25-
26-
const [module, hash] = name.split('.');
27-
oldHashes[module] = hash;
28-
});
59+
oldHashes = generateFileHashMap();
2960
})
30-
.then(() => writeFile('src/app/app.component.css', 'h1 { margin: 5px; }'))
31-
.then(() => writeFile('src/styles.css', 'body { background: red; }'))
3261
.then(() => ng('build', '--prod'))
3362
.then(() => {
34-
fs.readdirSync('./dist')
35-
.forEach(name => {
36-
if (!name.match(/(main|inline|styles|\d+)\.[a-z0-9]+\.bundle\.(js|css)/)) {
37-
return;
38-
}
39-
40-
const [module, hash] = name.split('.');
41-
newHashes[module] = hash;
42-
});
63+
newHashes = generateFileHashMap();
4364
})
4465
.then(() => {
45-
console.log(' Validating hashes...');
46-
console.log(` Old hashes: ${JSON.stringify(oldHashes)}`);
47-
console.log(` New hashes: ${JSON.stringify(newHashes)}`);
48-
49-
Object.keys(oldHashes)
50-
.forEach(module => {
51-
if (oldHashes[module] == newHashes[module]) {
52-
throw new Error(`Module "${module}" did not change hash (${oldHashes[module]})...`);
53-
}
54-
});
66+
validateHashes(oldHashes, newHashes, []);
67+
oldHashes = newHashes;
68+
})
69+
.then(() => writeFile('src/styles.css', 'body { background: blue; }'))
70+
.then(() => ng('build', '--prod'))
71+
.then(() => {
72+
newHashes = generateFileHashMap();
73+
})
74+
.then(() => {
75+
validateHashes(oldHashes, newHashes, ['styles']);
76+
oldHashes = newHashes;
77+
})
78+
.then(() => writeFile('src/app/app.component.css', 'h1 { margin: 10px; }'))
79+
.then(() => ng('build', '--prod'))
80+
.then(() => {
81+
newHashes = generateFileHashMap();
82+
})
83+
.then(() => {
84+
validateHashes(oldHashes, newHashes, ['inline', 'main']);
85+
oldHashes = newHashes;
86+
})
87+
.then(() => addImportToModule(
88+
'src/app/lazy/lazy.module.ts', 'ReactiveFormsModule', '@angular/forms'))
89+
.then(() => ng('build', '--prod'))
90+
.then(() => {
91+
newHashes = generateFileHashMap();
92+
})
93+
.then(() => {
94+
validateHashes(oldHashes, newHashes, ['inline', '0']);
5595
});
5696
}

0 commit comments

Comments
 (0)