Skip to content

Commit

Permalink
fix(@angular-devkit/build-angular): add type="module" on script tag…
Browse files Browse the repository at this point in the history
…s when differential loading will be enabled

Fixes #14747
  • Loading branch information
alan-agius4 authored and vikerman committed Jun 26, 2019
1 parent fdcd1f7 commit 44a0d0f
Show file tree
Hide file tree
Showing 5 changed files with 253 additions and 130 deletions.
Expand Up @@ -20,6 +20,7 @@ export interface IndexHtmlWebpackPluginOptions {
deployUrl?: string;
sri: boolean;
noModuleEntrypoints: string[];
moduleEntrypoints: string[];
postTransform?: IndexHtmlTransform;
}

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


export class IndexHtmlWebpackPlugin {
private _options: IndexHtmlWebpackPluginOptions;

Expand All @@ -47,6 +47,7 @@ export class IndexHtmlWebpackPlugin {
output: 'index.html',
entrypoints: ['polyfills', 'main'],
noModuleEntrypoints: [],
moduleEntrypoints: [],
sri: false,
...options,
};
Expand All @@ -56,23 +57,28 @@ export class IndexHtmlWebpackPlugin {
compiler.hooks.emit.tapPromise('index-html-webpack-plugin', async compilation => {
// Get input html file
const inputContent = await readFile(this._options.input, compilation);
(compilation as compilation.Compilation & { fileDependencies: Set<string> })
.fileDependencies.add(this._options.input);
(compilation as compilation.Compilation & {
fileDependencies: Set<string>;
}).fileDependencies.add(this._options.input);

// Get all files for selected entrypoints
const files: FileInfo[] = [];
const noModuleFiles: FileInfo[] = [];
const moduleFiles: FileInfo[] = [];

for (const [entryName, entrypoint] of compilation.entrypoints) {
const entryFiles: FileInfo[] = (entrypoint && entrypoint.getFiles() || [])
.map((f: string): FileInfo => ({
const entryFiles: FileInfo[] = ((entrypoint && entrypoint.getFiles()) || []).map(
(f: string): FileInfo => ({
name: entryName,
file: f,
extension: path.extname(f),
}));
}),
);

if (this._options.noModuleEntrypoints.includes(entryName)) {
noModuleFiles.push(...entryFiles);
} else if (this._options.moduleEntrypoints.includes(entryName)) {
moduleFiles.push(...entryFiles);
} else {
files.push(...entryFiles);
}
Expand All @@ -88,6 +94,7 @@ export class IndexHtmlWebpackPlugin {
files,
noModuleFiles,
loadOutputFile,
moduleFiles,
entrypoints: this._options.entrypoints,
});

Expand Down
118 changes: 63 additions & 55 deletions packages/angular_devkit/build_angular/src/browser/index.ts
Expand Up @@ -5,17 +5,13 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {
BuilderContext,
BuilderOutput,
createBuilder,
} from '@angular-devkit/architect';
import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect';
import {
BuildResult,
EmittedFiles,
WebpackLoggingCallback,
runWebpack,
} from '@angular-devkit/build-webpack';
} from '@angular-devkit/build-webpack';
import {
experimental,
getSystemPath,
Expand Down Expand Up @@ -62,9 +58,10 @@ import { assertCompatibleAngularVersion } from '../utils/version';
import { generateBrowserWebpackConfigFromContext } from '../utils/webpack-browser-config';
import { Schema as BrowserBuilderSchema } from './schema';

export type BrowserBuilderOutput = json.JsonObject & BuilderOutput & {
outputPath: string;
};
export type BrowserBuilderOutput = json.JsonObject &
BuilderOutput & {
outputPath: string;
};

export function createBrowserLoggingCallback(
verbose: boolean,
Expand Down Expand Up @@ -92,7 +89,7 @@ export async function buildBrowserWebpackConfigFromContext(
options: BrowserBuilderSchema,
context: BuilderContext,
host: virtualFs.Host<fs.Stats> = new NodeJsSyncHost(),
): Promise<{ workspace: experimental.workspace.Workspace, config: webpack.Configuration[] }> {
): Promise<{ workspace: experimental.workspace.Workspace; config: webpack.Configuration[] }> {
return generateBrowserWebpackConfigFromContext(
options,
context,
Expand Down Expand Up @@ -125,9 +122,7 @@ function getAnalyticsConfig(

// The category is the builder name if it's an angular builder.
return {
plugins: [
new NgBuildAnalyticsPlugin(wco.projectRoot, context.analytics, category),
],
plugins: [new NgBuildAnalyticsPlugin(wco.projectRoot, context.analytics, category)],
};
}

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

let transformedConfig;
Expand All @@ -173,9 +168,9 @@ export function buildWebpackBrowser(
options: BrowserBuilderSchema,
context: BuilderContext,
transforms: {
webpackConfiguration?: ExecutionTransformer<webpack.Configuration>,
logging?: WebpackLoggingCallback,
indexHtml?: IndexHtmlTransform,
webpackConfiguration?: ExecutionTransformer<webpack.Configuration>;
logging?: WebpackLoggingCallback;
indexHtml?: IndexHtmlTransform;
} = {},
) {
const host = new NodeJsSyncHost();
Expand All @@ -184,13 +179,14 @@ export function buildWebpackBrowser(
// Check Angular version.
assertCompatibleAngularVersion(context.workspaceRoot, context.logger);

const loggingFn = transforms.logging
|| createBrowserLoggingCallback(!!options.verbose, context.logger);
const loggingFn =
transforms.logging || createBrowserLoggingCallback(!!options.verbose, context.logger);

return from(initialize(options, context, host, transforms.webpackConfiguration)).pipe(
switchMap(({ workspace, config: configs }) => {
const projectName = context.target
? context.target.project : workspace.getDefaultProjectName();
? context.target.project
: workspace.getDefaultProjectName();

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

const tsConfig = readTsconfig(options.tsConfig, context.workspaceRoot);
const target = tsConfig.options.target || ScriptTarget.ES5;
const buildBrowserFeatures = new BuildBrowserFeatures(
getSystemPath(projectRoot),
target,
);
const buildBrowserFeatures = new BuildBrowserFeatures(getSystemPath(projectRoot), target);

if (target > ScriptTarget.ES2015 && buildBrowserFeatures.isDifferentialLoadingNeeded()) {
const isDifferentialLoadingNeeded = buildBrowserFeatures.isDifferentialLoadingNeeded();

if (target > ScriptTarget.ES2015 && isDifferentialLoadingNeeded) {
context.logger.warn(tags.stripIndent`
WARNING: Using differential loading with targets ES5 and ES2016 or higher may
cause problems. Browsers with support for ES2015 will load the ES2016+ scripts
Expand All @@ -219,14 +214,18 @@ export function buildWebpackBrowser(
return from(configs).pipe(
// the concurrency parameter (3rd parameter of mergeScan) is deliberately
// set to 1 to make sure the build steps are executed in sequence.
mergeScan((lastResult, config) => {
// Make sure to only run the 2nd build step, if 1st one succeeded
if (lastResult.success) {
return runWebpack(config, context, { logging: loggingFn });
} else {
return of();
}
}, { success: true } as BuildResult, 1),
mergeScan(
(lastResult, config) => {
// Make sure to only run the 2nd build step, if 1st one succeeded
if (lastResult.success) {
return runWebpack(config, context, { logging: loggingFn });
} else {
return of();
}
},
{ success: true } as BuildResult,
1,
),
bufferCount(configs.length),
switchMap(buildEvents => {
const success = buildEvents.every(r => r.success);
Expand All @@ -235,14 +234,19 @@ export function buildWebpackBrowser(
let moduleFiles: EmittedFiles[] | undefined;
let files: EmittedFiles[] | undefined;

const [ES5Result, ES2015Result] = buildEvents;
const [firstBuild, secondBuild] = buildEvents;

if (buildEvents.length === 2) {
noModuleFiles = ES5Result.emittedFiles;
moduleFiles = ES2015Result.emittedFiles || [];
noModuleFiles = firstBuild.emittedFiles;
moduleFiles = secondBuild.emittedFiles || [];
files = moduleFiles.filter(x => x.extension === '.css');
} else if (options.watch && isDifferentialLoadingNeeded) {
// differential loading is not enabled in watch mode
// but we still want to use module type tags
moduleFiles = firstBuild.emittedFiles || [];
files = moduleFiles.filter(x => x.extension === '.css');
} else {
const { emittedFiles = [] } = ES5Result;
const { emittedFiles = [] } = firstBuild;
files = emittedFiles.filter(x => x.name !== 'polyfills-es5');
noModuleFiles = emittedFiles.filter(x => x.name === 'polyfills-es5');
}
Expand All @@ -260,8 +264,7 @@ export function buildWebpackBrowser(
scripts: options.scripts,
styles: options.styles,
postTransform: transforms.indexHtml,
})
.pipe(
}).pipe(
map(() => ({ success: true })),
catchError(error => of({ success: false, error: mapErrorToMessage(error) })),
);
Expand All @@ -271,26 +274,31 @@ export function buildWebpackBrowser(
}),
concatMap(buildEvent => {
if (buildEvent.success && !options.watch && options.serviceWorker) {
return from(augmentAppWithServiceWorker(
host,
root,
projectRoot,
resolve(root, normalize(options.outputPath)),
options.baseHref || '/',
options.ngswConfigPath,
).then(
() => ({ success: true }),
error => ({ success: false, error: mapErrorToMessage(error) }),
));
return from(
augmentAppWithServiceWorker(
host,
root,
projectRoot,
resolve(root, normalize(options.outputPath)),
options.baseHref || '/',
options.ngswConfigPath,
).then(
() => ({ success: true }),
error => ({ success: false, error: mapErrorToMessage(error) }),
),
);
} else {
return of(buildEvent);
}
}),
map(event => ({
...event,
// If we use differential loading, both configs have the same outputs
outputPath: path.resolve(context.workspaceRoot, options.outputPath),
} as BrowserBuilderOutput)),
map(
event =>
({
...event,
// If we use differential loading, both configs have the same outputs
outputPath: path.resolve(context.workspaceRoot, options.outputPath),
} as BrowserBuilderOutput),
),
);
}),
);
Expand Down

0 comments on commit 44a0d0f

Please sign in to comment.