Skip to content

Commit 57796cf

Browse files
clydinmgechev
authored andcommitted
fix(@angular-devkit/build-angular): use translation file in bundle hash calculations
This change ensures that any changes to translation files is represented in the output file names when output hashing is enabled. This prevents the situation where a translation file only change to an application would result in built files with no change in output name.
1 parent 441c606 commit 57796cf

File tree

2 files changed

+98
-0
lines changed

2 files changed

+98
-0
lines changed

packages/angular_devkit/build_angular/src/utils/webpack-browser-config.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,37 @@ export async function generateI18nBrowserWebpackConfigFromContext(
153153
config.resolve.alias = {};
154154
}
155155
config.resolve.alias['@angular/localize/init'] = require.resolve('./empty.js');
156+
157+
// Update file hashes to include translation file content
158+
const i18nHash = Object.values(i18n.locales).reduce(
159+
(data, locale) => data + (locale.integrity || ''),
160+
'',
161+
);
162+
if (!config.plugins) {
163+
config.plugins = [];
164+
}
165+
config.plugins.push({
166+
apply(compiler) {
167+
compiler.hooks.compilation.tap('build-angular', compilation => {
168+
// Webpack typings do not contain template hashForChunk hook
169+
// tslint:disable-next-line: no-any
170+
(compilation.mainTemplate.hooks as any).hashForChunk.tap(
171+
'build-angular',
172+
(hash: { update(data: string): void }) => {
173+
hash.update('$localize' + i18nHash);
174+
},
175+
);
176+
// Webpack typings do not contain hooks property
177+
// tslint:disable-next-line: no-any
178+
(compilation.chunkTemplate as any).hooks.hashForChunk.tap(
179+
'build-angular',
180+
(hash: { update(data: string): void }) => {
181+
hash.update('$localize' + i18nHash);
182+
},
183+
);
184+
});
185+
},
186+
});
156187
}
157188

158189
return { ...result, i18n };
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
import * as fs from 'fs';
9+
import { appendToFile } from '../../utils/fs';
10+
import { ng } from '../../utils/process';
11+
import { langTranslations, setupI18nConfig } from './legacy';
12+
13+
const OUTPUT_RE = /^(?<name>(?:main|vendor|\d+)\-(?:es2015|es5))\.(?<hash>[a-z0-9]+)\.js$/i;
14+
15+
export default async function() {
16+
// Setup i18n tests and config.
17+
await setupI18nConfig(true);
18+
19+
// Build each locale and record output file hashes
20+
const hashes = new Map<string, string>();
21+
await ng('build', '--output-hashing=all');
22+
for (const { lang, outputPath } of langTranslations) {
23+
for (const entry of fs.readdirSync(outputPath)) {
24+
const match = entry.match(OUTPUT_RE);
25+
if (!match) {
26+
continue;
27+
}
28+
29+
hashes.set(`${lang}/${match.groups.name}`, match.groups.hash);
30+
}
31+
}
32+
33+
// Ensure hashes for output files were recorded
34+
if (hashes.size === 0) {
35+
throw new Error('No output entries found.');
36+
}
37+
38+
// Alter content of a used translation file
39+
await appendToFile('src/locale/messages.fr.xlf', '\n');
40+
41+
// Build each locale and ensure hashes are different
42+
await ng('build', '--output-hashing=all');
43+
for (const { lang, outputPath } of langTranslations) {
44+
for (const entry of fs.readdirSync(outputPath)) {
45+
const match = entry.match(OUTPUT_RE);
46+
if (!match) {
47+
continue;
48+
}
49+
50+
const id = `${lang}/${match.groups.name}`;
51+
const hash = hashes.get(id);
52+
if (!hash) {
53+
throw new Error('Unexpected output entry: ' + id);
54+
}
55+
if (hash === match.groups.hash) {
56+
throw new Error('Hash value did not change for entry: ' + id);
57+
}
58+
59+
hashes.delete(id);
60+
}
61+
}
62+
63+
// Check for missing entries in second build
64+
if (hashes.size > 0) {
65+
throw new Error('Missing output entries: ' + JSON.stringify(Array.from(hashes.values())));
66+
}
67+
}

0 commit comments

Comments
 (0)