Skip to content

Commit 3080666

Browse files
committed
refactor(@angular-devkit/build-angular): integrate Ivy Webpack compiler plugin
This change integrates the Ivy Webpack compiler plugin into the browser builder. When Ivy is enabled, which is the default behavior for applications, this plugin will now be used. If needed, the previous plugin can still be used by enabling the `NG_BUILD_IVY_LEGACY` environment variable.
1 parent 427f095 commit 3080666

File tree

4 files changed

+131
-20
lines changed

4 files changed

+131
-20
lines changed

packages/angular_devkit/build_angular/src/utils/environment-options.ts

+5
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,8 @@ export const cachingBasePath = (() => {
8282
// Build profiling
8383
const profilingVariable = process.env['NG_BUILD_PROFILING'];
8484
export const profilingEnabled = isPresent(profilingVariable) && isEnabled(profilingVariable);
85+
86+
// Legacy Webpack plugin with Ivy
87+
const legacyIvyVariable = process.env['NG_BUILD_IVY_LEGACY'];
88+
export const legacyIvyPluginEnabled =
89+
isPresent(legacyIvyVariable) && !isDisabled(legacyIvyVariable);

packages/angular_devkit/build_angular/src/webpack/configs/common.ts

+8
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,14 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration {
350350

351351
if (buildOptions.namedChunks && !isWebpackFiveOrHigher()) {
352352
extraPlugins.push(new NamedLazyChunksPlugin());
353+
354+
// Provide full names for lazy routes that use the deprecated string format
355+
extraPlugins.push(
356+
new ContextReplacementPlugin(
357+
/\@angular[\\\/]core[\\\/]/,
358+
(data: { chunkName?: string }) => (data.chunkName = '[request]'),
359+
),
360+
);
353361
}
354362

355363
if (!differentialLoadingMode) {

packages/angular_devkit/build_angular/src/webpack/configs/typescript.ts

+118-16
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,79 @@
77
*/
88
// tslint:disable
99
// TODO: cleanup this file, it's copied as is from Angular CLI.
10+
import { CompilerOptions } from '@angular/compiler-cli';
1011
import { buildOptimizerLoaderPath } from '@angular-devkit/build-optimizer';
11-
import * as path from 'path';
1212
import {
1313
AngularCompilerPlugin,
1414
AngularCompilerPluginOptions,
1515
NgToolsLoader,
16-
PLATFORM
16+
PLATFORM,
17+
ivy,
1718
} from '@ngtools/webpack';
19+
import * as path from 'path';
20+
import { RuleSetLoader } from 'webpack';
1821
import { WebpackConfigOptions, BuildOptions } from '../../utils/build-options';
22+
import { legacyIvyPluginEnabled } from '../../utils/environment-options';
23+
24+
function canUseIvyPlugin(wco: WebpackConfigOptions): boolean {
25+
// Can only be used with Ivy
26+
if (!wco.tsConfig.options.enableIvy) {
27+
return false;
28+
}
29+
30+
// Allow fallback to legacy build system via environment variable ('NG_BUILD_IVY_LEGACY=1')
31+
if (legacyIvyPluginEnabled) {
32+
wco.logger.warn(
33+
'"NG_BUILD_IVY_LEGACY" environment variable detected. Using legacy Ivy build system.',
34+
);
35+
36+
return false;
37+
}
38+
39+
// Lazy modules option uses the deprecated string format for lazy routes
40+
if (wco.buildOptions.lazyModules && wco.buildOptions.lazyModules.length > 0) {
41+
return false;
42+
}
43+
44+
// This pass relies on internals of the original plugin
45+
if (wco.buildOptions.experimentalRollupPass) {
46+
return false;
47+
}
48+
49+
return true;
50+
}
51+
52+
function createIvyPlugin(
53+
wco: WebpackConfigOptions,
54+
aot: boolean,
55+
tsconfig: string,
56+
): ivy.AngularWebpackPlugin {
57+
const { buildOptions } = wco;
58+
const optimize = buildOptions.optimization.scripts;
59+
60+
const compilerOptions: CompilerOptions = {
61+
skipTemplateCodegen: !aot,
62+
sourceMap: buildOptions.sourceMap.scripts,
63+
};
64+
65+
if (buildOptions.preserveSymlinks !== undefined) {
66+
compilerOptions.preserveSymlinks = buildOptions.preserveSymlinks;
67+
}
68+
69+
const fileReplacements: Record<string, string> = {};
70+
if (buildOptions.fileReplacements) {
71+
for (const replacement of buildOptions.fileReplacements) {
72+
fileReplacements[replacement.replace] = replacement.with;
73+
}
74+
}
75+
76+
return new ivy.AngularWebpackPlugin({
77+
tsconfig,
78+
compilerOptions,
79+
fileReplacements,
80+
emitNgModuleScope: !optimize,
81+
});
82+
}
1983

2084
function _pluginOptionsOverrides(
2185
buildOptions: BuildOptions,
@@ -103,40 +167,78 @@ function _createAotPlugin(
103167

104168
export function getNonAotConfig(wco: WebpackConfigOptions) {
105169
const { tsConfigPath } = wco;
170+
const useIvyOnlyPlugin = canUseIvyPlugin(wco);
106171

107172
return {
108-
module: { rules: [{ test: /\.tsx?$/, loader: NgToolsLoader }] },
109-
plugins: [_createAotPlugin(wco, { tsConfigPath, skipCodeGeneration: true })]
173+
module: {
174+
rules: [
175+
{
176+
test: useIvyOnlyPlugin ? /\.[jt]sx?$/ : /\.tsx?$/,
177+
loader: useIvyOnlyPlugin
178+
? ivy.AngularWebpackLoaderPath
179+
: NgToolsLoader,
180+
},
181+
],
182+
},
183+
plugins: [
184+
useIvyOnlyPlugin
185+
? createIvyPlugin(wco, false, tsConfigPath)
186+
: _createAotPlugin(wco, { tsConfigPath, skipCodeGeneration: true }),
187+
],
110188
};
111189
}
112190

113191
export function getAotConfig(wco: WebpackConfigOptions, i18nExtract = false) {
114192
const { tsConfigPath, buildOptions } = wco;
193+
const optimize = buildOptions.optimization.scripts;
194+
const useIvyOnlyPlugin = canUseIvyPlugin(wco) && !i18nExtract;
115195

116-
const loaders: any[] = [NgToolsLoader];
196+
let buildOptimizerRules: RuleSetLoader[] = [];
117197
if (buildOptions.buildOptimizer) {
118-
loaders.unshift({
198+
buildOptimizerRules = [{
119199
loader: buildOptimizerLoaderPath,
120200
options: { sourceMap: buildOptions.sourceMap.scripts }
121-
});
201+
}];
122202
}
123203

124-
const test = /(?:\.ngfactory\.js|\.ngstyle\.js|\.tsx?)$/;
125-
const optimize = wco.buildOptions.optimization.scripts;
126-
127204
return {
128-
module: { rules: [{ test, use: loaders }] },
205+
module: {
206+
rules: [
207+
{
208+
test: useIvyOnlyPlugin ? /\.tsx?$/ : /(?:\.ngfactory\.js|\.ngstyle\.js|\.tsx?)$/,
209+
use: [
210+
...buildOptimizerRules,
211+
useIvyOnlyPlugin ? ivy.AngularWebpackLoaderPath : NgToolsLoader,
212+
],
213+
},
214+
// "allowJs" support with ivy plugin - ensures build optimizer is not run twice
215+
...(useIvyOnlyPlugin
216+
? [
217+
{
218+
test: /\.jsx?$/,
219+
use: [ivy.AngularWebpackLoaderPath],
220+
},
221+
]
222+
: []),
223+
],
224+
},
129225
plugins: [
130-
_createAotPlugin(
131-
wco,
132-
{ tsConfigPath, emitClassMetadata: !optimize, emitNgModuleScope: !optimize },
133-
i18nExtract,
134-
),
226+
useIvyOnlyPlugin
227+
? createIvyPlugin(wco, true, tsConfigPath)
228+
: _createAotPlugin(
229+
wco,
230+
{ tsConfigPath, emitClassMetadata: !optimize, emitNgModuleScope: !optimize },
231+
i18nExtract,
232+
),
135233
],
136234
};
137235
}
138236

139237
export function getTypescriptWorkerPlugin(wco: WebpackConfigOptions, workerTsConfigPath: string) {
238+
if (canUseIvyPlugin(wco)) {
239+
return createIvyPlugin(wco, false, workerTsConfigPath);
240+
}
241+
140242
const { buildOptions } = wco;
141243

142244
let pluginOptions: AngularCompilerPluginOptions = {

packages/ngtools/webpack/src/ivy/loader.ts

-4
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@ export function angularWebpackLoader(
1515
// tslint:disable-next-line: no-any
1616
map: any,
1717
) {
18-
if (this.loaderIndex !== this.loaders.length - 1) {
19-
this.emitWarning('The Angular Webpack loader does not support chaining prior to the loader.');
20-
}
21-
2218
const callback = this.async();
2319
if (!callback) {
2420
throw new Error('Invalid webpack version');

0 commit comments

Comments
 (0)