Skip to content

Commit 44a0d0f

Browse files
alan-agius4vikerman
authored andcommitted
fix(@angular-devkit/build-angular): add type="module" on script tags when differential loading will be enabled
Fixes #14747
1 parent fdcd1f7 commit 44a0d0f

File tree

5 files changed

+253
-130
lines changed

5 files changed

+253
-130
lines changed

packages/angular_devkit/build_angular/src/angular-cli-files/plugins/index-html-webpack-plugin.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export interface IndexHtmlWebpackPluginOptions {
2020
deployUrl?: string;
2121
sri: boolean;
2222
noModuleEntrypoints: string[];
23+
moduleEntrypoints: string[];
2324
postTransform?: IndexHtmlTransform;
2425
}
2526

@@ -37,7 +38,6 @@ function readFile(filename: string, compilation: compilation.Compilation): Promi
3738
});
3839
}
3940

40-
4141
export class IndexHtmlWebpackPlugin {
4242
private _options: IndexHtmlWebpackPluginOptions;
4343

@@ -47,6 +47,7 @@ export class IndexHtmlWebpackPlugin {
4747
output: 'index.html',
4848
entrypoints: ['polyfills', 'main'],
4949
noModuleEntrypoints: [],
50+
moduleEntrypoints: [],
5051
sri: false,
5152
...options,
5253
};
@@ -56,23 +57,28 @@ export class IndexHtmlWebpackPlugin {
5657
compiler.hooks.emit.tapPromise('index-html-webpack-plugin', async compilation => {
5758
// Get input html file
5859
const inputContent = await readFile(this._options.input, compilation);
59-
(compilation as compilation.Compilation & { fileDependencies: Set<string> })
60-
.fileDependencies.add(this._options.input);
60+
(compilation as compilation.Compilation & {
61+
fileDependencies: Set<string>;
62+
}).fileDependencies.add(this._options.input);
6163

6264
// Get all files for selected entrypoints
6365
const files: FileInfo[] = [];
6466
const noModuleFiles: FileInfo[] = [];
67+
const moduleFiles: FileInfo[] = [];
6568

6669
for (const [entryName, entrypoint] of compilation.entrypoints) {
67-
const entryFiles: FileInfo[] = (entrypoint && entrypoint.getFiles() || [])
68-
.map((f: string): FileInfo => ({
70+
const entryFiles: FileInfo[] = ((entrypoint && entrypoint.getFiles()) || []).map(
71+
(f: string): FileInfo => ({
6972
name: entryName,
7073
file: f,
7174
extension: path.extname(f),
72-
}));
75+
}),
76+
);
7377

7478
if (this._options.noModuleEntrypoints.includes(entryName)) {
7579
noModuleFiles.push(...entryFiles);
80+
} else if (this._options.moduleEntrypoints.includes(entryName)) {
81+
moduleFiles.push(...entryFiles);
7682
} else {
7783
files.push(...entryFiles);
7884
}
@@ -88,6 +94,7 @@ export class IndexHtmlWebpackPlugin {
8894
files,
8995
noModuleFiles,
9096
loadOutputFile,
97+
moduleFiles,
9198
entrypoints: this._options.entrypoints,
9299
});
93100

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

Lines changed: 63 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,13 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
import {
9-
BuilderContext,
10-
BuilderOutput,
11-
createBuilder,
12-
} from '@angular-devkit/architect';
8+
import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect';
139
import {
1410
BuildResult,
1511
EmittedFiles,
1612
WebpackLoggingCallback,
1713
runWebpack,
18-
} from '@angular-devkit/build-webpack';
14+
} from '@angular-devkit/build-webpack';
1915
import {
2016
experimental,
2117
getSystemPath,
@@ -62,9 +58,10 @@ import { assertCompatibleAngularVersion } from '../utils/version';
6258
import { generateBrowserWebpackConfigFromContext } from '../utils/webpack-browser-config';
6359
import { Schema as BrowserBuilderSchema } from './schema';
6460

65-
export type BrowserBuilderOutput = json.JsonObject & BuilderOutput & {
66-
outputPath: string;
67-
};
61+
export type BrowserBuilderOutput = json.JsonObject &
62+
BuilderOutput & {
63+
outputPath: string;
64+
};
6865

6966
export function createBrowserLoggingCallback(
7067
verbose: boolean,
@@ -92,7 +89,7 @@ export async function buildBrowserWebpackConfigFromContext(
9289
options: BrowserBuilderSchema,
9390
context: BuilderContext,
9491
host: virtualFs.Host<fs.Stats> = new NodeJsSyncHost(),
95-
): Promise<{ workspace: experimental.workspace.Workspace, config: webpack.Configuration[] }> {
92+
): Promise<{ workspace: experimental.workspace.Workspace; config: webpack.Configuration[] }> {
9693
return generateBrowserWebpackConfigFromContext(
9794
options,
9895
context,
@@ -125,9 +122,7 @@ function getAnalyticsConfig(
125122

126123
// The category is the builder name if it's an angular builder.
127124
return {
128-
plugins: [
129-
new NgBuildAnalyticsPlugin(wco.projectRoot, context.analytics, category),
130-
],
125+
plugins: [new NgBuildAnalyticsPlugin(wco.projectRoot, context.analytics, category)],
131126
};
132127
}
133128

@@ -147,7 +142,7 @@ async function initialize(
147142
context: BuilderContext,
148143
host: virtualFs.Host<fs.Stats>,
149144
webpackConfigurationTransform?: ExecutionTransformer<webpack.Configuration>,
150-
): Promise<{ workspace: experimental.workspace.Workspace, config: webpack.Configuration[] }> {
145+
): Promise<{ workspace: experimental.workspace.Workspace; config: webpack.Configuration[] }> {
151146
const { config, workspace } = await buildBrowserWebpackConfigFromContext(options, context, host);
152147

153148
let transformedConfig;
@@ -173,9 +168,9 @@ export function buildWebpackBrowser(
173168
options: BrowserBuilderSchema,
174169
context: BuilderContext,
175170
transforms: {
176-
webpackConfiguration?: ExecutionTransformer<webpack.Configuration>,
177-
logging?: WebpackLoggingCallback,
178-
indexHtml?: IndexHtmlTransform,
171+
webpackConfiguration?: ExecutionTransformer<webpack.Configuration>;
172+
logging?: WebpackLoggingCallback;
173+
indexHtml?: IndexHtmlTransform;
179174
} = {},
180175
) {
181176
const host = new NodeJsSyncHost();
@@ -184,13 +179,14 @@ export function buildWebpackBrowser(
184179
// Check Angular version.
185180
assertCompatibleAngularVersion(context.workspaceRoot, context.logger);
186181

187-
const loggingFn = transforms.logging
188-
|| createBrowserLoggingCallback(!!options.verbose, context.logger);
182+
const loggingFn =
183+
transforms.logging || createBrowserLoggingCallback(!!options.verbose, context.logger);
189184

190185
return from(initialize(options, context, host, transforms.webpackConfiguration)).pipe(
191186
switchMap(({ workspace, config: configs }) => {
192187
const projectName = context.target
193-
? context.target.project : workspace.getDefaultProjectName();
188+
? context.target.project
189+
: workspace.getDefaultProjectName();
194190

195191
if (!projectName) {
196192
throw new Error('Must either have a target from the context or a default project.');
@@ -203,12 +199,11 @@ export function buildWebpackBrowser(
203199

204200
const tsConfig = readTsconfig(options.tsConfig, context.workspaceRoot);
205201
const target = tsConfig.options.target || ScriptTarget.ES5;
206-
const buildBrowserFeatures = new BuildBrowserFeatures(
207-
getSystemPath(projectRoot),
208-
target,
209-
);
202+
const buildBrowserFeatures = new BuildBrowserFeatures(getSystemPath(projectRoot), target);
210203

211-
if (target > ScriptTarget.ES2015 && buildBrowserFeatures.isDifferentialLoadingNeeded()) {
204+
const isDifferentialLoadingNeeded = buildBrowserFeatures.isDifferentialLoadingNeeded();
205+
206+
if (target > ScriptTarget.ES2015 && isDifferentialLoadingNeeded) {
212207
context.logger.warn(tags.stripIndent`
213208
WARNING: Using differential loading with targets ES5 and ES2016 or higher may
214209
cause problems. Browsers with support for ES2015 will load the ES2016+ scripts
@@ -219,14 +214,18 @@ export function buildWebpackBrowser(
219214
return from(configs).pipe(
220215
// the concurrency parameter (3rd parameter of mergeScan) is deliberately
221216
// set to 1 to make sure the build steps are executed in sequence.
222-
mergeScan((lastResult, config) => {
223-
// Make sure to only run the 2nd build step, if 1st one succeeded
224-
if (lastResult.success) {
225-
return runWebpack(config, context, { logging: loggingFn });
226-
} else {
227-
return of();
228-
}
229-
}, { success: true } as BuildResult, 1),
217+
mergeScan(
218+
(lastResult, config) => {
219+
// Make sure to only run the 2nd build step, if 1st one succeeded
220+
if (lastResult.success) {
221+
return runWebpack(config, context, { logging: loggingFn });
222+
} else {
223+
return of();
224+
}
225+
},
226+
{ success: true } as BuildResult,
227+
1,
228+
),
230229
bufferCount(configs.length),
231230
switchMap(buildEvents => {
232231
const success = buildEvents.every(r => r.success);
@@ -235,14 +234,19 @@ export function buildWebpackBrowser(
235234
let moduleFiles: EmittedFiles[] | undefined;
236235
let files: EmittedFiles[] | undefined;
237236

238-
const [ES5Result, ES2015Result] = buildEvents;
237+
const [firstBuild, secondBuild] = buildEvents;
239238

240239
if (buildEvents.length === 2) {
241-
noModuleFiles = ES5Result.emittedFiles;
242-
moduleFiles = ES2015Result.emittedFiles || [];
240+
noModuleFiles = firstBuild.emittedFiles;
241+
moduleFiles = secondBuild.emittedFiles || [];
242+
files = moduleFiles.filter(x => x.extension === '.css');
243+
} else if (options.watch && isDifferentialLoadingNeeded) {
244+
// differential loading is not enabled in watch mode
245+
// but we still want to use module type tags
246+
moduleFiles = firstBuild.emittedFiles || [];
243247
files = moduleFiles.filter(x => x.extension === '.css');
244248
} else {
245-
const { emittedFiles = [] } = ES5Result;
249+
const { emittedFiles = [] } = firstBuild;
246250
files = emittedFiles.filter(x => x.name !== 'polyfills-es5');
247251
noModuleFiles = emittedFiles.filter(x => x.name === 'polyfills-es5');
248252
}
@@ -260,8 +264,7 @@ export function buildWebpackBrowser(
260264
scripts: options.scripts,
261265
styles: options.styles,
262266
postTransform: transforms.indexHtml,
263-
})
264-
.pipe(
267+
}).pipe(
265268
map(() => ({ success: true })),
266269
catchError(error => of({ success: false, error: mapErrorToMessage(error) })),
267270
);
@@ -271,26 +274,31 @@ export function buildWebpackBrowser(
271274
}),
272275
concatMap(buildEvent => {
273276
if (buildEvent.success && !options.watch && options.serviceWorker) {
274-
return from(augmentAppWithServiceWorker(
275-
host,
276-
root,
277-
projectRoot,
278-
resolve(root, normalize(options.outputPath)),
279-
options.baseHref || '/',
280-
options.ngswConfigPath,
281-
).then(
282-
() => ({ success: true }),
283-
error => ({ success: false, error: mapErrorToMessage(error) }),
284-
));
277+
return from(
278+
augmentAppWithServiceWorker(
279+
host,
280+
root,
281+
projectRoot,
282+
resolve(root, normalize(options.outputPath)),
283+
options.baseHref || '/',
284+
options.ngswConfigPath,
285+
).then(
286+
() => ({ success: true }),
287+
error => ({ success: false, error: mapErrorToMessage(error) }),
288+
),
289+
);
285290
} else {
286291
return of(buildEvent);
287292
}
288293
}),
289-
map(event => ({
290-
...event,
291-
// If we use differential loading, both configs have the same outputs
292-
outputPath: path.resolve(context.workspaceRoot, options.outputPath),
293-
} as BrowserBuilderOutput)),
294+
map(
295+
event =>
296+
({
297+
...event,
298+
// If we use differential loading, both configs have the same outputs
299+
outputPath: path.resolve(context.workspaceRoot, options.outputPath),
300+
} as BrowserBuilderOutput),
301+
),
294302
);
295303
}),
296304
);

0 commit comments

Comments
 (0)