Skip to content

Commit 54a6bee

Browse files
Alan Agiusalexeagle
Alan Agius
authored andcommitted
feat(@angular-devkit/build-webpack): report emitted files
With this change the builder will report the emitted chunks and assets after the compilation, this is needed for deferential loading so that we can build an index from the outputs of multiple builds
1 parent 778c5f5 commit 54a6bee

File tree

6 files changed

+117
-16
lines changed

6 files changed

+117
-16
lines changed

packages/angular_devkit/build_webpack/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ export * from './webpack';
1010
export * from './webpack-dev-server';
1111

1212
export * from './plugins/architect';
13+
14+
export { EmittedFiles } from './utils';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import * as webpack from 'webpack';
10+
11+
export interface EmittedFiles {
12+
name?: string;
13+
file: string;
14+
initial: boolean;
15+
extension: string;
16+
}
17+
18+
export function getEmittedFiles(compilation: webpack.compilation.Compilation): EmittedFiles[] {
19+
const getExtension = (file: string) => file.split('.').reverse()[0];
20+
const files: EmittedFiles[] = [];
21+
22+
for (const chunk of Object.values(compilation.chunks)) {
23+
const entry: Partial<EmittedFiles> = {
24+
name: chunk.name,
25+
initial: chunk.isOnlyInitial(),
26+
};
27+
28+
for (const file of chunk.files) {
29+
files.push({ ...entry, file, extension: getExtension(file) } as EmittedFiles);
30+
}
31+
}
32+
33+
for (const file of Object.keys(compilation.assets)) {
34+
if (files.some(e => e.file === file)) {
35+
// skip as this already exists
36+
continue;
37+
}
38+
39+
files.push({
40+
file,
41+
extension: getExtension(file),
42+
initial: false,
43+
});
44+
}
45+
46+
return files;
47+
}

packages/angular_devkit/build_webpack/src/webpack-dev-server/index2.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ import { switchMap } from 'rxjs/operators';
1313
import * as webpack from 'webpack';
1414
import * as WebpackDevServer from 'webpack-dev-server';
1515
import { ArchitectPlugin } from '../plugins/architect';
16-
import { WebpackFactory, WebpackLoggingCallback } from '../webpack/index2';
16+
import { getEmittedFiles } from '../utils';
17+
import { BuildResult, WebpackFactory, WebpackLoggingCallback } from '../webpack/index2';
1718
import { Schema as WebpackDevServerBuilderSchema } from './schema';
1819

1920
const webpackMerge = require('webpack-merge');
2021

2122

22-
export type DevServerBuildOutput = BuilderOutput & {
23+
export type DevServerBuildOutput = BuildResult & {
2324
port: number;
2425
family: string;
2526
address: string;
@@ -33,7 +34,7 @@ export function runWebpackDevServer(
3334
logging?: WebpackLoggingCallback,
3435
webpackFactory?: WebpackFactory,
3536
} = {},
36-
): Observable<BuilderOutput> {
37+
): Observable<DevServerBuildOutput> {
3738
const createWebpack = options.webpackFactory || (config => of(webpack(config)));
3839
const log: WebpackLoggingCallback = options.logging
3940
|| ((stats, config) => context.logger.info(stats.toString(config.stats)));
@@ -52,7 +53,7 @@ export function runWebpackDevServer(
5253
devServerConfig.stats = false;
5354

5455
return createWebpack(config).pipe(
55-
switchMap(webpackCompiler => new Observable<BuilderOutput>(obs => {
56+
switchMap(webpackCompiler => new Observable<DevServerBuildOutput>(obs => {
5657
const server = new WebpackDevServer(webpackCompiler, devServerConfig);
5758
let result: DevServerBuildOutput;
5859

@@ -62,8 +63,9 @@ export function runWebpackDevServer(
6263

6364
obs.next({
6465
...result,
66+
emittedFiles: getEmittedFiles(stats.compilation),
6567
success: !stats.hasErrors(),
66-
} as DevServerBuildOutput);
68+
} as unknown as DevServerBuildOutput);
6769
});
6870

6971
server.listen(

packages/angular_devkit/build_webpack/src/webpack-dev-server/index2_spec_large.ts

+17-3
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77
*/
88
import { Architect } from '@angular-devkit/architect/src/index2';
99
import { TestingArchitectHost } from '@angular-devkit/architect/testing/index2';
10-
import { experimental, join, normalize, schema } from '@angular-devkit/core';
10+
import { experimental, normalize, schema } from '@angular-devkit/core';
1111
import { NodeJsSyncHost } from '@angular-devkit/core/node';
1212
import * as fs from 'fs';
1313
import fetch from 'node-fetch'; // tslint:disable-line:no-implicit-dependencies
1414
import * as path from 'path';
1515
import { WorkspaceNodeModulesArchitectHost } from '../../../architect/node';
1616
import { DevServerBuildOutput } from './index2';
1717

18-
const devkitRoot = normalize((global as any)._DevKitRoot); // tslint:disable-line:no-any
18+
const devkitRoot = (global as any)._DevKitRoot; // tslint:disable-line:no-any
1919

2020

2121
describe('Dev Server Builder', () => {
@@ -42,7 +42,7 @@ describe('Dev Server Builder', () => {
4242
}
4343

4444
beforeEach(async () => {
45-
await createArchitect(join(devkitRoot, 'tests/angular_devkit/build_webpack/basic-app/'));
45+
await createArchitect(path.join(devkitRoot, 'tests/angular_devkit/build_webpack/basic-app/'));
4646
});
4747

4848
it('works', async () => {
@@ -55,4 +55,18 @@ describe('Dev Server Builder', () => {
5555

5656
await run.stop();
5757
}, 30000);
58+
59+
it('works and returns emitted files', async () => {
60+
const run = await architect.scheduleTarget(webpackTargetSpec);
61+
const output = await run.result as DevServerBuildOutput;
62+
63+
expect(output.success).toBe(true);
64+
expect(output.emittedFiles).toContain({
65+
name: 'main',
66+
initial: true,
67+
file: 'bundle.js',
68+
extension: 'js',
69+
});
70+
await run.stop();
71+
}, 30000);
5872
});

packages/angular_devkit/build_webpack/src/webpack/index2.ts

+10-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { Observable, from, of } from 'rxjs';
1111
import { switchMap } from 'rxjs/operators';
1212
import * as webpack from 'webpack';
1313
import { ArchitectPlugin } from '../plugins/architect';
14+
import { EmittedFiles, getEmittedFiles } from '../utils';
1415
import { Schema as RealWebpackBuilderSchema } from './schema';
1516

1617
const webpackMerge = require('webpack-merge');
@@ -24,6 +25,9 @@ export interface WebpackFactory {
2425
(config: webpack.Configuration): Observable<webpack.Compiler>;
2526
}
2627

28+
export type BuildResult = BuilderOutput & {
29+
emittedFiles?: EmittedFiles[];
30+
};
2731

2832
export function runWebpack(
2933
config: webpack.Configuration,
@@ -32,7 +36,7 @@ export function runWebpack(
3236
logging?: WebpackLoggingCallback,
3337
webpackFactory?: WebpackFactory,
3438
} = {},
35-
): Observable<BuilderOutput> {
39+
): Observable<BuildResult> {
3640
const createWebpack = options.webpackFactory || (config => of(webpack(config)));
3741
const log: WebpackLoggingCallback = options.logging
3842
|| ((stats, config) => context.logger.info(stats.toString(config.stats)));
@@ -44,7 +48,7 @@ export function runWebpack(
4448
});
4549

4650
return createWebpack(config).pipe(
47-
switchMap(webpackCompiler => new Observable<BuilderOutput>(obs => {
51+
switchMap(webpackCompiler => new Observable<BuildResult>(obs => {
4852
const callback: webpack.Compiler.Handler = (err, stats) => {
4953
if (err) {
5054
return obs.error(err);
@@ -53,7 +57,10 @@ export function runWebpack(
5357
// Log stats.
5458
log(stats, config);
5559

56-
obs.next({ success: !stats.hasErrors() });
60+
obs.next({
61+
success: !stats.hasErrors(),
62+
emittedFiles: getEmittedFiles(stats.compilation),
63+
} as unknown as BuildResult);
5764

5865
if (!config.watch) {
5966
obs.complete();

packages/angular_devkit/build_webpack/src/webpack/index2_spec_large.ts

+34-5
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ import * as path from 'path';
1212
import { WorkspaceNodeModulesArchitectHost } from '../../../architect/node';
1313
import { Architect } from '../../../architect/src/architect';
1414
import { TestingArchitectHost } from '../../../architect/testing/testing-architect-host';
15+
import { BuildResult } from './index2';
1516

16-
const devkitRoot = normalize((global as any)._DevKitRoot); // tslint:disable-line:no-any
17+
const devkitRoot = (global as any)._DevKitRoot; // tslint:disable-line:no-any
1718

1819

1920
describe('Webpack Builder basic test', () => {
@@ -38,8 +39,8 @@ describe('Webpack Builder basic test', () => {
3839
}
3940

4041
describe('basic app', () => {
41-
const workspaceRoot = join(devkitRoot, 'tests/angular_devkit/build_webpack/basic-app/');
42-
const outputPath = join(workspaceRoot, 'dist');
42+
const workspaceRoot = path.join(devkitRoot, 'tests/angular_devkit/build_webpack/basic-app/');
43+
const outputPath = join(normalize(workspaceRoot), 'dist');
4344

4445
beforeEach(async () => {
4546
await createArchitect(workspaceRoot);
@@ -53,11 +54,26 @@ describe('Webpack Builder basic test', () => {
5354
expect(await vfHost.exists(join(outputPath, 'bundle.js')).toPromise()).toBe(true);
5455
await run.stop();
5556
});
57+
58+
it('works and returns emitted files', async () => {
59+
const run = await architect.scheduleTarget({ project: 'app', target: 'build' });
60+
const output = await run.result as BuildResult;
61+
62+
expect(output.success).toBe(true);
63+
expect(output.emittedFiles).toContain({
64+
name: 'main',
65+
initial: true,
66+
file: 'bundle.js',
67+
extension: 'js',
68+
});
69+
70+
await run.stop();
71+
});
5672
});
5773

5874
describe('Angular app', () => {
59-
const workspaceRoot = join(devkitRoot, 'tests/angular_devkit/build_webpack/angular-app/');
60-
const outputPath = join(workspaceRoot, 'dist/');
75+
const workspaceRoot = path.join(devkitRoot, 'tests/angular_devkit/build_webpack/angular-app/');
76+
const outputPath = join(normalize(workspaceRoot), 'dist/');
6177

6278
beforeEach(async () => {
6379
await createArchitect(workspaceRoot);
@@ -72,5 +88,18 @@ describe('Webpack Builder basic test', () => {
7288
expect(await vfHost.exists(join(outputPath, 'polyfills.js')).toPromise()).toBe(true);
7389
await run.stop();
7490
});
91+
92+
it('works and returns emitted files', async () => {
93+
const run = await architect.scheduleTarget({ project: 'app', target: 'build-webpack' });
94+
const output = await run.result as BuildResult;
95+
96+
expect(output.success).toBe(true);
97+
expect(output.emittedFiles).toContain(
98+
{ name: 'main', initial: true, file: 'main.js', extension: 'js' },
99+
{ name: 'polyfills', initial: true, file: 'polyfills.js', extension: 'js' },
100+
);
101+
102+
await run.stop();
103+
});
75104
});
76105
});

0 commit comments

Comments
 (0)