fix(@angular-devkit/build-angular): run build steps for differential loading in sequence to avoid confusing progress information

Before, the build tasks ran in parallel and so the different webpack
instances competed over the same lines on the console.

To fail fast and to prevent to show the same errors twice, the second
build step is not executed if the first one fails.

As running these tasks in sequence causes issues with watch mode, this
PR also disables differential loading when watch mode is requested.
This commit is contained in:
ManfredSteyer 2019-04-18 20:28:12 +02:00 committed by Minko Gechev
parent 4807ff00d1
commit 201856a5ec
3 changed files with 47 additions and 10 deletions

View File

@ -10,7 +10,7 @@ import {
BuilderOutput,
createBuilder,
} from '@angular-devkit/architect';
import { WebpackLoggingCallback, runWebpack } from '@angular-devkit/build-webpack';
import { BuildResult, WebpackLoggingCallback, runWebpack } from '@angular-devkit/build-webpack';
import {
experimental,
join,
@ -23,8 +23,8 @@ import {
import { NodeJsSyncHost } from '@angular-devkit/core/node';
import * as fs from 'fs';
import * as path from 'path';
import { from, of, zip } from 'rxjs';
import { catchError, concatMap, map, switchMap } from 'rxjs/operators';
import { concat, from, of, zip } from 'rxjs';
import { bufferCount, catchError, concatMap, map, mergeScan, switchMap } from 'rxjs/operators';
import * as webpack from 'webpack';
import { NgBuildAnalyticsPlugin } from '../../plugins/webpack/analytics';
import { WebpackConfigOptions } from '../angular-cli-files/models/build-options';
@ -182,12 +182,18 @@ export function buildWebpackBrowser(
normalize(workspace.getProject(projectName).root),
);
// We use zip because when having multiple builds we want to wait
// for all builds to finish before processeding
return zip(
...configs.map(config => runWebpack(config, context, { logging: loggingFn })),
)
.pipe(
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),
bufferCount(configs.length),
switchMap(buildEvents => {
const success = buildEvents.every(r => r.success);
if (success && buildEvents.length === 2 && options.index) {

View File

@ -55,7 +55,8 @@ export async function generateWebpackConfig(
// However this config generation is used by multiple builders such as dev-server
const scriptTarget = tsConfig.options.target;
const differentialLoading = context.builder.builderName === 'browser'
&& isDifferentialLoadingNeeded(projectRoot, scriptTarget);
&& isDifferentialLoadingNeeded(projectRoot, scriptTarget) && !options.watch;
const scriptTargets = [scriptTarget];
if (differentialLoading) {

View File

@ -63,6 +63,36 @@ describe('Browser Builder with differential loading', () => {
.toEqual(jasmine.arrayWithExactContents(expectedOutputs));
});
it('deactivates differential loading for watch mode', async () => {
const { files } = await browserBuild(architect, host, target, { watch: true });
const expectedOutputs = [
'favicon.ico',
'index.html',
'main.js',
'main.js.map',
'polyfills-es5.js',
'polyfills-es5.js.map',
'polyfills.js',
'polyfills.js.map',
'runtime.js',
'runtime.js.map',
'styles.js',
'styles.js.map',
'vendor.js',
'vendor.js.map',
] as PathFragment[];
expect(Object.keys(files))
.toEqual(jasmine.arrayWithExactContents(expectedOutputs));
});
it('emits the right ES formats', async () => {
const { files } = await browserBuild(architect, host, target, { optimization: true });
expect(await files['main-es5.js']).not.toContain('class');