Skip to content

Commit

Permalink
feat(@angular-devkit/build-webpack): report emitted files
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Alan Agius authored and alexeagle committed Mar 22, 2019
1 parent 778c5f5 commit 54a6bee
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 16 deletions.
2 changes: 2 additions & 0 deletions packages/angular_devkit/build_webpack/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ export * from './webpack';
export * from './webpack-dev-server';

export * from './plugins/architect';

export { EmittedFiles } from './utils';
47 changes: 47 additions & 0 deletions packages/angular_devkit/build_webpack/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* 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 * as webpack from 'webpack';

export interface EmittedFiles {
name?: string;
file: string;
initial: boolean;
extension: string;
}

export function getEmittedFiles(compilation: webpack.compilation.Compilation): EmittedFiles[] {
const getExtension = (file: string) => file.split('.').reverse()[0];
const files: EmittedFiles[] = [];

for (const chunk of Object.values(compilation.chunks)) {
const entry: Partial<EmittedFiles> = {
name: chunk.name,
initial: chunk.isOnlyInitial(),
};

for (const file of chunk.files) {
files.push({ ...entry, file, extension: getExtension(file) } as EmittedFiles);
}
}

for (const file of Object.keys(compilation.assets)) {
if (files.some(e => e.file === file)) {
// skip as this already exists
continue;
}

files.push({
file,
extension: getExtension(file),
initial: false,
});
}

return files;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ import { switchMap } from 'rxjs/operators';
import * as webpack from 'webpack';
import * as WebpackDevServer from 'webpack-dev-server';
import { ArchitectPlugin } from '../plugins/architect';
import { WebpackFactory, WebpackLoggingCallback } from '../webpack/index2';
import { getEmittedFiles } from '../utils';
import { BuildResult, WebpackFactory, WebpackLoggingCallback } from '../webpack/index2';
import { Schema as WebpackDevServerBuilderSchema } from './schema';

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


export type DevServerBuildOutput = BuilderOutput & {
export type DevServerBuildOutput = BuildResult & {
port: number;
family: string;
address: string;
Expand All @@ -33,7 +34,7 @@ export function runWebpackDevServer(
logging?: WebpackLoggingCallback,
webpackFactory?: WebpackFactory,
} = {},
): Observable<BuilderOutput> {
): Observable<DevServerBuildOutput> {
const createWebpack = options.webpackFactory || (config => of(webpack(config)));
const log: WebpackLoggingCallback = options.logging
|| ((stats, config) => context.logger.info(stats.toString(config.stats)));
Expand All @@ -52,7 +53,7 @@ export function runWebpackDevServer(
devServerConfig.stats = false;

return createWebpack(config).pipe(
switchMap(webpackCompiler => new Observable<BuilderOutput>(obs => {
switchMap(webpackCompiler => new Observable<DevServerBuildOutput>(obs => {
const server = new WebpackDevServer(webpackCompiler, devServerConfig);
let result: DevServerBuildOutput;

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

obs.next({
...result,
emittedFiles: getEmittedFiles(stats.compilation),
success: !stats.hasErrors(),
} as DevServerBuildOutput);
} as unknown as DevServerBuildOutput);
});

server.listen(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
*/
import { Architect } from '@angular-devkit/architect/src/index2';
import { TestingArchitectHost } from '@angular-devkit/architect/testing/index2';
import { experimental, join, normalize, schema } from '@angular-devkit/core';
import { experimental, normalize, schema } from '@angular-devkit/core';
import { NodeJsSyncHost } from '@angular-devkit/core/node';
import * as fs from 'fs';
import fetch from 'node-fetch'; // tslint:disable-line:no-implicit-dependencies
import * as path from 'path';
import { WorkspaceNodeModulesArchitectHost } from '../../../architect/node';
import { DevServerBuildOutput } from './index2';

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


describe('Dev Server Builder', () => {
Expand All @@ -42,7 +42,7 @@ describe('Dev Server Builder', () => {
}

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

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

await run.stop();
}, 30000);

it('works and returns emitted files', async () => {
const run = await architect.scheduleTarget(webpackTargetSpec);
const output = await run.result as DevServerBuildOutput;

expect(output.success).toBe(true);
expect(output.emittedFiles).toContain({
name: 'main',
initial: true,
file: 'bundle.js',
extension: 'js',
});
await run.stop();
}, 30000);
});
13 changes: 10 additions & 3 deletions packages/angular_devkit/build_webpack/src/webpack/index2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Observable, from, of } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import * as webpack from 'webpack';
import { ArchitectPlugin } from '../plugins/architect';
import { EmittedFiles, getEmittedFiles } from '../utils';
import { Schema as RealWebpackBuilderSchema } from './schema';

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

export type BuildResult = BuilderOutput & {
emittedFiles?: EmittedFiles[];
};

export function runWebpack(
config: webpack.Configuration,
Expand All @@ -32,7 +36,7 @@ export function runWebpack(
logging?: WebpackLoggingCallback,
webpackFactory?: WebpackFactory,
} = {},
): Observable<BuilderOutput> {
): Observable<BuildResult> {
const createWebpack = options.webpackFactory || (config => of(webpack(config)));
const log: WebpackLoggingCallback = options.logging
|| ((stats, config) => context.logger.info(stats.toString(config.stats)));
Expand All @@ -44,7 +48,7 @@ export function runWebpack(
});

return createWebpack(config).pipe(
switchMap(webpackCompiler => new Observable<BuilderOutput>(obs => {
switchMap(webpackCompiler => new Observable<BuildResult>(obs => {
const callback: webpack.Compiler.Handler = (err, stats) => {
if (err) {
return obs.error(err);
Expand All @@ -53,7 +57,10 @@ export function runWebpack(
// Log stats.
log(stats, config);

obs.next({ success: !stats.hasErrors() });
obs.next({
success: !stats.hasErrors(),
emittedFiles: getEmittedFiles(stats.compilation),
} as unknown as BuildResult);

if (!config.watch) {
obs.complete();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import * as path from 'path';
import { WorkspaceNodeModulesArchitectHost } from '../../../architect/node';
import { Architect } from '../../../architect/src/architect';
import { TestingArchitectHost } from '../../../architect/testing/testing-architect-host';
import { BuildResult } from './index2';

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


describe('Webpack Builder basic test', () => {
Expand All @@ -38,8 +39,8 @@ describe('Webpack Builder basic test', () => {
}

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

beforeEach(async () => {
await createArchitect(workspaceRoot);
Expand All @@ -53,11 +54,26 @@ describe('Webpack Builder basic test', () => {
expect(await vfHost.exists(join(outputPath, 'bundle.js')).toPromise()).toBe(true);
await run.stop();
});

it('works and returns emitted files', async () => {
const run = await architect.scheduleTarget({ project: 'app', target: 'build' });
const output = await run.result as BuildResult;

expect(output.success).toBe(true);
expect(output.emittedFiles).toContain({
name: 'main',
initial: true,
file: 'bundle.js',
extension: 'js',
});

await run.stop();
});
});

describe('Angular app', () => {
const workspaceRoot = join(devkitRoot, 'tests/angular_devkit/build_webpack/angular-app/');
const outputPath = join(workspaceRoot, 'dist/');
const workspaceRoot = path.join(devkitRoot, 'tests/angular_devkit/build_webpack/angular-app/');
const outputPath = join(normalize(workspaceRoot), 'dist/');

beforeEach(async () => {
await createArchitect(workspaceRoot);
Expand All @@ -72,5 +88,18 @@ describe('Webpack Builder basic test', () => {
expect(await vfHost.exists(join(outputPath, 'polyfills.js')).toPromise()).toBe(true);
await run.stop();
});

it('works and returns emitted files', async () => {
const run = await architect.scheduleTarget({ project: 'app', target: 'build-webpack' });
const output = await run.result as BuildResult;

expect(output.success).toBe(true);
expect(output.emittedFiles).toContain(
{ name: 'main', initial: true, file: 'main.js', extension: 'js' },
{ name: 'polyfills', initial: true, file: 'polyfills.js', extension: 'js' },
);

await run.stop();
});
});
});

0 comments on commit 54a6bee

Please sign in to comment.