diff --git a/packages/angular_devkit/build_angular/package.json b/packages/angular_devkit/build_angular/package.json index ce0dabd9a0e2..654ed49fff1a 100644 --- a/packages/angular_devkit/build_angular/package.json +++ b/packages/angular_devkit/build_angular/package.json @@ -40,6 +40,7 @@ "semver": "5.5.1", "source-map-support": "0.5.9", "source-map-loader": "0.2.4", + "speed-measure-webpack-plugin": "^1.2.3", "stats-webpack-plugin": "0.7.0", "style-loader": "0.23.0", "stylus": "0.54.5", diff --git a/packages/angular_devkit/build_angular/src/angular-cli-files/models/build-options.ts b/packages/angular_devkit/build_angular/src/angular-cli-files/models/build-options.ts index 4dac5449818a..3ee86311eacc 100644 --- a/packages/angular_devkit/build_angular/src/angular-cli-files/models/build-options.ts +++ b/packages/angular_devkit/build_angular/src/angular-cli-files/models/build-options.ts @@ -52,6 +52,7 @@ export interface BuildOptions { skipAppShell?: boolean; statsJson: boolean; forkTypeChecker: boolean; + profile?: boolean; main: string; index: string; diff --git a/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/common.ts b/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/common.ts index 3a59950deb0c..5bf3b61b4849 100644 --- a/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/common.ts +++ b/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/common.ts @@ -9,7 +9,7 @@ // TODO: cleanup this file, it's copied as is from Angular CLI. import * as path from 'path'; -import { HashedModuleIdsPlugin } from 'webpack'; +import { HashedModuleIdsPlugin, debug } from 'webpack'; import * as CopyWebpackPlugin from 'copy-webpack-plugin'; import { getOutputHashFormat } from './utils'; import { isDirectory } from '../../utilities/is-directory'; @@ -69,6 +69,12 @@ export function getCommonConfig(wco: WebpackConfigOptions) { ]; } + if (buildOptions.profile) { + extraPlugins.push(new debug.ProfilingPlugin({ + outputPath: path.resolve(root, 'chrome-profiler-events.json'), + })) + } + // determine hashing format const hashFormat = getOutputHashFormat(buildOptions.outputHashing as any); diff --git a/packages/angular_devkit/build_angular/src/browser/index.ts b/packages/angular_devkit/build_angular/src/browser/index.ts index 8b81938e3fbf..39cb7eb43819 100644 --- a/packages/angular_devkit/build_angular/src/browser/index.ts +++ b/packages/angular_devkit/build_angular/src/browser/index.ts @@ -12,7 +12,7 @@ import { BuilderContext, } from '@angular-devkit/architect'; import { LoggingCallback, WebpackBuilder } from '@angular-devkit/build-webpack'; -import { Path, getSystemPath, normalize, resolve, virtualFs } from '@angular-devkit/core'; +import { Path, getSystemPath, join, normalize, resolve, virtualFs } from '@angular-devkit/core'; import * as fs from 'fs'; import { Observable, concat, of, throwError } from 'rxjs'; import { concatMap, last, tap } from 'rxjs/operators'; @@ -36,6 +36,7 @@ import { } from '../angular-cli-files/utilities/stats'; import { defaultProgress, normalizeAssetPatterns, normalizeFileReplacements } from '../utils'; import { AssetPatternObject, BrowserBuilderSchema, CurrentFileReplacement } from './schema'; +const SpeedMeasurePlugin = require('speed-measure-webpack-plugin'); const webpackMerge = require('webpack-merge'); @@ -154,7 +155,18 @@ export class BrowserBuilder implements Builder { webpackConfigs.push(typescriptConfigPartial); } - return webpackMerge(webpackConfigs); + const webpackConfig = webpackMerge(webpackConfigs); + + if (options.profile) { + const smp = new SpeedMeasurePlugin({ + outputFormat: 'json', + outputTarget: getSystemPath(join(root, 'speed-measure-plugin.json')), + }); + + return smp.wrap(webpackConfig); + } + + return webpackConfig; } private _deleteOutputDir(root: Path, outputPath: Path, host: virtualFs.Host) { diff --git a/packages/angular_devkit/build_angular/src/browser/schema.d.ts b/packages/angular_devkit/build_angular/src/browser/schema.d.ts index 11a63aaa698d..5b66793292ad 100644 --- a/packages/angular_devkit/build_angular/src/browser/schema.d.ts +++ b/packages/angular_devkit/build_angular/src/browser/schema.d.ts @@ -222,6 +222,11 @@ export interface BrowserBuilderSchema { * Budget thresholds to ensure parts of your application stay within boundaries which you set. */ budgets: Budget[]; + + /** + * Output profile events for Chrome profiler. + */ + profile: boolean; } export type AssetPattern = string | AssetPatternObject; diff --git a/packages/angular_devkit/build_angular/src/browser/schema.json b/packages/angular_devkit/build_angular/src/browser/schema.json index 0539ceb4b628..c29931dee0b8 100644 --- a/packages/angular_devkit/build_angular/src/browser/schema.json +++ b/packages/angular_devkit/build_angular/src/browser/schema.json @@ -236,6 +236,11 @@ "$ref": "#/definitions/budget" }, "default": [] + }, + "profile": { + "type": "boolean", + "description": "Output profile events for Chrome profiler.", + "default": false } }, "additionalProperties": false, diff --git a/packages/angular_devkit/build_angular/test/browser/profile_spec_large.ts b/packages/angular_devkit/build_angular/test/browser/profile_spec_large.ts new file mode 100644 index 000000000000..e17349953549 --- /dev/null +++ b/packages/angular_devkit/build_angular/test/browser/profile_spec_large.ts @@ -0,0 +1,29 @@ +/** + * @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 { runTargetSpec } from '@angular-devkit/architect/testing'; +import { normalize } from '@angular-devkit/core'; +import { tap } from 'rxjs/operators'; +import { browserTargetSpec, host } from '../utils'; + + +describe('Browser Builder profile', () => { + beforeEach(done => host.initialize().toPromise().then(done, done.fail)); + afterEach(done => host.restore().toPromise().then(done, done.fail)); + + it('works', (done) => { + const overrides = { profile: true }; + runTargetSpec(host, browserTargetSpec, overrides).pipe( + tap((buildEvent) => expect(buildEvent.success).toBe(true)), + tap(() => { + expect(host.scopedSync().exists(normalize('chrome-profiler-events.json'))).toBe(true); + expect(host.scopedSync().exists(normalize('speed-measure-plugin.json'))).toBe(true); + }), + ).toPromise().then(done, done.fail); + }); +}); diff --git a/yarn.lock b/yarn.lock index 30fa0b8d6592..3068ceb26aaa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7082,6 +7082,12 @@ spdy@^3.4.1: select-hose "^2.0.0" spdy-transport "^2.0.18" +speed-measure-webpack-plugin@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.2.3.tgz#de170b5cefbfa1c039d95e639edd3ad50cfc7c48" + dependencies: + chalk "^2.0.1" + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"