Skip to content

Commit a5f26e5

Browse files
committed
feat(@angular-devkit/build-angular): upgrade to Webpack 5 throughout the build system
With this change Webpack 5 is now used by the Angular tooling to build applications. Webpack 4 usage and support has been removed. No project level configuration changes are required to take advantage of the upgraded Webpack version when using the official Angular builders. Custom builders based on this package that use the experimental programmatic APIs may need to be updated to become compatible with Webpack 5. BREAKING CHANGE: Webpack 5 lazy loaded file name changes Webpack 5 generates similar but differently named files for lazy loaded JavaScript files in development configurations (when the `namedChunks` option is enabled). For the majority of users this change should have no effect on the application and/or build process. Production builds should also not be affected as the `namedChunks` option is disabled by default in production configurations. However, if a project's post-build process makes assumptions as to the file names then adjustments may need to be made to account for the new naming paradigm. Such post-build processes could include custom file transformations after the build, integration into service-side frameworks, or deployment procedures. Example development file name change: `lazy-lazy-module.js` --> `src_app_lazy_lazy_module_ts.js` BREAKING CHANGE: Webpack 5 web worker support Webpack 5 now includes web worker support. However, the structure of the URL within the `Worker` constructor must be in a specific format that differs from the current requirement. Web worker usage should be updated as shown below (where `./app.worker` should be replaced with the actual worker name): Before: `new Worker('./app.worker', ...)` After: `new Worker(new URL('./app.worker', import.meta.url), ...)`
1 parent 5b7aaf9 commit a5f26e5

34 files changed

+252
-1189
lines changed

package.json

+3-4
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@
136136
"common-tags": "^1.8.0",
137137
"conventional-changelog": "^3.0.0",
138138
"conventional-commits-parser": "^3.0.0",
139-
"copy-webpack-plugin": "6.3.2",
139+
"copy-webpack-plugin": "8.1.1",
140140
"core-js": "3.10.0",
141141
"critters": "0.0.10",
142142
"css-loader": "5.0.2",
@@ -199,7 +199,7 @@
199199
"rimraf": "3.0.2",
200200
"rxjs": "6.6.7",
201201
"sass": "1.32.8",
202-
"sass-loader": "10.1.1",
202+
"sass-loader": "11.0.1",
203203
"sauce-connect-proxy": "https://saucelabs.com/downloads/sc-4.6.4-linux.tar.gz",
204204
"semver": "7.3.5",
205205
"source-map": "0.7.3",
@@ -226,13 +226,12 @@
226226
"typescript": "4.2.3",
227227
"verdaccio": "4.12.0",
228228
"verdaccio-auth-memory": "^10.0.0",
229-
"webpack": "4.44.2",
229+
"webpack": "5.21.2",
230230
"webpack-dev-middleware": "4.1.0",
231231
"webpack-dev-server": "3.11.2",
232232
"webpack-merge": "5.7.3",
233233
"webpack-sources": "2.2.0",
234234
"webpack-subresource-integrity": "1.5.2",
235-
"worker-plugin": "5.0.0",
236235
"zone.js": "^0.11.3"
237236
}
238237
}

packages/angular_devkit/build_angular/BUILD.bazel

-1
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,6 @@ ts_library(
197197
"@npm//webpack-merge",
198198
"@npm//webpack-sources",
199199
"@npm//webpack-subresource-integrity",
200-
"@npm//worker-plugin",
201200
],
202201
)
203202

packages/angular_devkit/build_angular/package.json

+4-5
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"cacache": "15.0.6",
2828
"caniuse-lite": "^1.0.30001032",
2929
"circular-dependency-plugin": "5.2.2",
30-
"copy-webpack-plugin": "6.3.2",
30+
"copy-webpack-plugin": "8.1.1",
3131
"core-js": "3.10.0",
3232
"critters": "0.0.10",
3333
"css-loader": "5.0.2",
@@ -58,7 +58,7 @@
5858
"rimraf": "3.0.2",
5959
"rxjs": "6.6.7",
6060
"sass": "1.32.8",
61-
"sass-loader": "10.1.1",
61+
"sass-loader": "11.0.1",
6262
"semver": "7.3.5",
6363
"source-map": "0.7.3",
6464
"source-map-loader": "1.1.3",
@@ -70,13 +70,12 @@
7070
"terser-webpack-plugin": "4.2.3",
7171
"text-table": "0.2.0",
7272
"tree-kill": "1.2.2",
73-
"webpack": "4.44.2",
73+
"webpack": "5.21.2",
7474
"webpack-dev-middleware": "4.1.0",
7575
"webpack-dev-server": "3.11.2",
7676
"webpack-merge": "5.7.3",
7777
"webpack-sources": "2.2.0",
78-
"webpack-subresource-integrity": "1.5.2",
79-
"worker-plugin": "5.0.0"
78+
"webpack-subresource-integrity": "1.5.2"
8079
},
8180
"peerDependencies": {
8281
"@angular/compiler-cli": "^12.0.0-next",

packages/angular_devkit/build_angular/src/browser/index.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ import {
6767
BundleStats,
6868
ChunkType,
6969
JsonChunkStats,
70+
JsonCompilationStats,
7071
generateBundleStats,
7172
statsErrorsToString,
7273
statsHasErrors,
@@ -250,11 +251,11 @@ export function buildWebpackBrowser(
250251
spinner.enabled = options.progress !== false;
251252

252253
const {
253-
webpackStats: webpackRawStats,
254254
success,
255255
emittedFiles = [],
256256
outputPath: webpackOutputPath,
257257
} = buildEvent;
258+
const webpackRawStats = buildEvent.webpackStats as JsonCompilationStats;
258259
if (!webpackRawStats) {
259260
throw new Error('Webpack stats build result is required.');
260261
}
@@ -608,10 +609,10 @@ export function buildWebpackBrowser(
608609
for (const { severity, message } of budgetFailures) {
609610
switch (severity) {
610611
case ThresholdSeverity.Warning:
611-
webpackStats.warnings.push(message);
612+
webpackStats.warnings?.push({message});
612613
break;
613614
case ThresholdSeverity.Error:
614-
webpackStats.errors.push(message);
615+
webpackStats.errors?.push({message});
615616
break;
616617
default:
617618
assertNever(severity);

packages/angular_devkit/build_angular/src/browser/specs/deploy-url_spec.ts

+7
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ describe('Browser Builder deploy url', () => {
2525
const overrides = { deployUrl: 'deployUrl/' };
2626
const overrides2 = { deployUrl: 'http://example.com/some/path/' };
2727

28+
// Add lazy loaded chunk to provide a usage of the deploy URL
29+
// Webpack 5+ will not include the deploy URL in the code unless needed
30+
host.appendToFile('src/main.ts', '\nimport("./lazy");');
31+
host.writeMultipleFiles({
32+
'src/lazy.ts': 'export const foo = "bar";',
33+
});
34+
2835
const run = await architect.scheduleTarget(targetSpec, overrides);
2936
const output = await run.result as BrowserBuilderOutput;
3037
expect(output.success).toBe(true);

packages/angular_devkit/build_angular/src/browser/specs/differential_loading_spec.ts

+9-7
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { Architect } from '@angular-devkit/architect';
1010
import { PathFragment } from '@angular-devkit/core';
1111
import { browserBuild, createArchitect, host } from '../../test-utils';
1212

13+
const TEST_TIMEOUT = 8 * 60 * 1000;
14+
1315
// tslint:disable-next-line: no-big-function
1416
describe('Browser Builder with differential loading', () => {
1517
const target = { project: 'app', target: 'build' };
@@ -59,7 +61,7 @@ describe('Browser Builder with differential loading', () => {
5961
] as PathFragment[];
6062

6163
expect(Object.keys(files)).toEqual(jasmine.arrayWithExactContents(expectedOutputs));
62-
});
64+
}, TEST_TIMEOUT);
6365

6466
it('emits all the neccessary files for target of ESNext', async () => {
6567
host.replaceInFile(
@@ -99,7 +101,7 @@ describe('Browser Builder with differential loading', () => {
99101
] as PathFragment[];
100102

101103
expect(Object.keys(files)).toEqual(jasmine.arrayWithExactContents(expectedOutputs));
102-
});
104+
}, TEST_TIMEOUT);
103105

104106
it('deactivates differential loading for watch mode', async () => {
105107
const { files } = await browserBuild(architect, host, target, { watch: true });
@@ -125,7 +127,7 @@ describe('Browser Builder with differential loading', () => {
125127
] as PathFragment[];
126128

127129
expect(Object.keys(files)).toEqual(jasmine.arrayWithExactContents(expectedOutputs));
128-
});
130+
}, TEST_TIMEOUT);
129131

130132
it('emits the right ES formats', async () => {
131133
const { files } = await browserBuild(architect, host, target, {
@@ -134,21 +136,21 @@ describe('Browser Builder with differential loading', () => {
134136
});
135137
expect(await files['main-es5.js']).not.toContain('const ');
136138
expect(await files['main-es2017.js']).toContain('const ');
137-
});
139+
}, TEST_TIMEOUT);
138140

139141
it('wraps ES5 scripts in an IIFE', async () => {
140142
const { files } = await browserBuild(architect, host, target, { optimization: false });
141143
expect(await files['main-es5.js']).toMatch(/^\(function \(\) \{/);
142144
expect(await files['main-es2017.js']).not.toMatch(/^\(function \(\) \{/);
143-
});
145+
}, TEST_TIMEOUT);
144146

145147
it('uses the right zone.js variant', async () => {
146148
const { files } = await browserBuild(architect, host, target, { optimization: false });
147149
expect(await files['polyfills-es5.js']).toContain('zone.js/plugins/zone-legacy');
148150
expect(await files['polyfills-es5.js']).toContain('registerElementPatch');
149151
expect(await files['polyfills-es2017.js']).not.toContain('zone.js/plugins/zone-legacy');
150152
expect(await files['polyfills-es2017.js']).not.toContain('registerElementPatch');
151-
});
153+
}, TEST_TIMEOUT);
152154

153155
it('adds `type="module"` when differential loading is needed', async () => {
154156
host.writeMultipleFiles({
@@ -165,5 +167,5 @@ describe('Browser Builder with differential loading', () => {
165167
'<script src="vendor-es2017.js" type="module"></script>' +
166168
'<script src="main-es2017.js" type="module"></script>',
167169
);
168-
});
170+
}, TEST_TIMEOUT);
169171
});

packages/angular_devkit/build_angular/src/browser/specs/errors_spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,6 @@ describe('Browser Builder errors', () => {
9090
// Wait for the builder to complete
9191
await run.stop();
9292

93-
expect(logs.join()).toContain(`export 'missingExport' was not found in 'rxjs'`);
93+
expect(logs.join()).toContain(`export 'missingExport' (imported as 'missingExport') was not found in 'rxjs'`);
9494
});
9595
});

packages/angular_devkit/build_angular/src/browser/specs/lazy-module_spec.ts

+14-23
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ describe('Browser Builder lazy modules', () => {
5858
host.writeMultipleFiles(lazyModuleFnImport);
5959

6060
const { files } = await browserBuild(architect, host, target);
61-
expect('lazy-lazy-module.js' in files).toBe(true);
61+
expect('src_app_lazy_lazy_module_ts.js' in files).toBe(true);
6262
});
6363

6464
it('supports lazy bundle for lazy routes with AOT', async () => {
@@ -67,7 +67,7 @@ describe('Browser Builder lazy modules', () => {
6767
addLazyLoadedModulesInTsConfig(host, lazyModuleFiles);
6868

6969
const { files } = await browserBuild(architect, host, target, { aot: true });
70-
const data = await files['lazy-lazy-module.js'];
70+
const data = await files['src_app_lazy_lazy_module_ts.js'];
7171
expect(data).not.toBeUndefined('Lazy module output bundle does not exist');
7272
expect(data).toContain('LazyModule.ɵmod');
7373
});
@@ -128,7 +128,7 @@ describe('Browser Builder lazy modules', () => {
128128
});
129129

130130
const { files } = await browserBuild(architect, host, target);
131-
expect(files['lazy-module.js']).not.toBeUndefined();
131+
expect(files['src_lazy-module_ts.js']).not.toBeUndefined();
132132
});
133133

134134
it(`supports lazy bundle for dynamic import() calls`, async () => {
@@ -139,19 +139,11 @@ describe('Browser Builder lazy modules', () => {
139139
import(/*webpackChunkName: '[request]'*/'./lazy-' + lazyFileName);
140140
`,
141141
});
142-
143-
const { files } = await browserBuild(architect, host, target);
144-
expect(files['lazy-module.js']).not.toBeUndefined();
145-
});
146-
147-
it(`supports lazy bundle for System.import() calls`, async () => {
148-
const lazyfiles = {
149-
'src/lazy-module.ts': 'export const value = 42;',
150-
'src/main.ts': `declare var System: any; System.import('./lazy-module');`,
151-
};
152-
153-
host.writeMultipleFiles(lazyfiles);
154-
addLazyLoadedModulesInTsConfig(host, lazyfiles);
142+
host.replaceInFile(
143+
'src/tsconfig.app.json',
144+
'"main.ts"',
145+
`"main.ts","lazy-module.ts"`,
146+
);
155147

156148
const { files } = await browserBuild(architect, host, target);
157149
expect(files['lazy-module.js']).not.toBeUndefined();
@@ -165,10 +157,9 @@ describe('Browser Builder lazy modules', () => {
165157
});
166158

167159
const { files } = await browserBuild(architect, host, target);
168-
expect(files['one.js']).not.toBeUndefined();
169-
expect(files['two.js']).not.toBeUndefined();
170-
// TODO: the chunk with common modules used to be called `common`, see why that changed.
171-
expect(files['default~one~two.js']).not.toBeUndefined();
160+
expect(files['src_one_ts.js']).not.toBeUndefined();
161+
expect(files['src_two_ts.js']).not.toBeUndefined();
162+
expect(files['default-node_modules_angular_common___ivy_ngcc___fesm2015_http_js.js']).toBeDefined();
172163
});
173164

174165
it(`supports disabling the common bundle`, async () => {
@@ -179,8 +170,8 @@ describe('Browser Builder lazy modules', () => {
179170
});
180171

181172
const { files } = await browserBuild(architect, host, target, { commonChunk: false });
182-
expect(files['one.js']).not.toBeUndefined();
183-
expect(files['two.js']).not.toBeUndefined();
184-
expect(files['common.js']).toBeUndefined();
173+
expect(files['src_one_ts.js']).not.toBeUndefined();
174+
expect(files['src_two_ts.js']).not.toBeUndefined();
175+
expect(files['default-node_modules_angular_common___ivy_ngcc___fesm2015_http_js.js']).toBeUndefined();
185176
});
186177
});

packages/angular_devkit/build_angular/src/browser/specs/rebuild_spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ describe('Browser Builder rebuilds', () => {
8181
debounceTime(rebuildDebounceTime),
8282
tap(result => {
8383
expect(result.success).toBe(true, 'build should succeed');
84-
const hasLazyChunk = host.scopedSync().exists(normalize('dist/lazy-lazy-module.js'));
84+
const hasLazyChunk = host.scopedSync().exists(normalize('dist/src_app_lazy_lazy_module_ts.js'));
8585
switch (phase) {
8686
case 1:
8787
// No lazy chunk should exist.

packages/angular_devkit/build_angular/src/browser/specs/resolve-json-module_spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,13 @@ describe('Browser Builder resolve json module', () => {
4646

4747
switch (buildCount) {
4848
case 1:
49-
expect(content).toContain('\\"foo\\":\\"1\\"');
49+
expect(content).toContain('"foo":"1"');
5050
host.writeMultipleFiles({
5151
'src/my-json-file.json': `{"foo": "2"}`,
5252
});
5353
break;
5454
case 2:
55-
expect(content).toContain('\\"foo\\":\\"2\\"');
55+
expect(content).toContain('"foo":"2"');
5656
break;
5757
}
5858

packages/angular_devkit/build_angular/src/browser/specs/web-worker_spec.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ describe('Browser Builder Web Worker support', () => {
4545
if (environment.production) { enableProdMode(); }
4646
platformBrowserDynamic().bootstrapModule(AppModule).catch(err => console.error(err));
4747
48-
const worker = new Worker('./app/app.worker', { type: 'module' });
48+
const worker = new Worker(new URL('./app/app.worker', import.meta.url), { type: 'module' });
4949
worker.onmessage = ({ data }) => {
5050
console.log('page got message:', data);
5151
};
@@ -96,14 +96,14 @@ describe('Browser Builder Web Worker support', () => {
9696

9797
// Worker bundle contains worker code.
9898
const workerContent = virtualFs.fileBufferToString(
99-
host.scopedSync().read(join(outputPath, '0.worker.js')));
99+
host.scopedSync().read(join(outputPath, 'src_app_app_worker_ts.js')));
100100
expect(workerContent).toContain('hello from worker');
101101
expect(workerContent).toContain('bar');
102102

103103
// Main bundle references worker.
104104
const mainContent = virtualFs.fileBufferToString(
105105
host.scopedSync().read(join(outputPath, 'main.js')));
106-
expect(mainContent).toContain('0.worker.js');
106+
expect(mainContent).toContain('src_app_app_worker_ts');
107107
expect(logs.join().includes('Warning')).toBe(false, 'Should show no warnings.');
108108
});
109109

@@ -117,7 +117,7 @@ describe('Browser Builder Web Worker support', () => {
117117
await browserBuild(architect, host, target, overrides);
118118

119119
// Worker bundle should have hash and minified code.
120-
const workerBundle = host.fileMatchExists(outputPath, /0\.[0-9a-f]{20}\.worker\.js/) as string;
120+
const workerBundle = host.fileMatchExists(outputPath, /src_app_app_worker_ts\.[0-9a-f]{20}\.js/) as string;
121121
expect(workerBundle).toBeTruthy('workerBundle should exist');
122122
const workerContent = virtualFs.fileBufferToString(
123123
host.scopedSync().read(join(outputPath, workerBundle)));
@@ -130,7 +130,7 @@ describe('Browser Builder Web Worker support', () => {
130130
expect(mainBundle).toBeTruthy('mainBundle should exist');
131131
const mainContent = virtualFs.fileBufferToString(
132132
host.scopedSync().read(join(outputPath, mainBundle)));
133-
expect(mainContent).toContain(workerBundle);
133+
expect(mainContent).toContain('src_app_app_worker_ts');
134134
});
135135

136136
it('rebuilds TS worker', async () => {
@@ -141,7 +141,7 @@ describe('Browser Builder Web Worker support', () => {
141141
};
142142

143143
let phase = 1;
144-
const workerPath = join(outputPath, '0.worker.js');
144+
const workerPath = join(outputPath, 'src_app_app_worker_ts.js');
145145
let workerContent = '';
146146

147147
// The current linux-based CI environments may not fully settled in regards to filesystem

packages/angular_devkit/build_angular/src/browser/tests/options/named-chunks_spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import { buildWebpackBrowser } from '../../index';
99
import { BASE_OPTIONS, BROWSER_BUILDER_INFO, describeBuilder } from '../setup';
1010

1111
const MAIN_OUTPUT = 'dist/main.js';
12-
const NAMED_LAZY_OUTPUT = 'dist/lazy-module.js';
13-
const UNNAMED_LAZY_OUTPUT = 'dist/0.js';
12+
const NAMED_LAZY_OUTPUT = 'dist/src_lazy-module_ts.js';
13+
const UNNAMED_LAZY_OUTPUT = 'dist/339.js';
1414

1515
describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => {
1616
describe('Option: "namedChunks"', () => {

0 commit comments

Comments
 (0)