From 352fa71558ab9c280acd163b26d3e63c37466071 Mon Sep 17 00:00:00 2001 From: Filipe Silva Date: Thu, 4 Jul 2019 19:02:14 +0100 Subject: [PATCH 1/3] feat(@angular-devkit/build-optimizer): add BuildOptimizerWebpackPlugin Using this webpack allows determining in advance if files from a package should be skipped by the Build Optimizer loader. --- .../build_optimizer/src/_golden-api.d.ts | 6 ++++ .../angular_devkit/build_optimizer/README.md | 5 +++ .../src/build-optimizer/webpack-loader.ts | 27 +++++++++++++-- .../src/build-optimizer/webpack-plugin.ts | 33 +++++++++++++++++++ .../build_optimizer/src/index.ts | 1 + 5 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 packages/angular_devkit/build_optimizer/src/build-optimizer/webpack-plugin.ts diff --git a/etc/api/angular_devkit/build_optimizer/src/_golden-api.d.ts b/etc/api/angular_devkit/build_optimizer/src/_golden-api.d.ts index c545afc96c41..83e1865b8cba 100644 --- a/etc/api/angular_devkit/build_optimizer/src/_golden-api.d.ts +++ b/etc/api/angular_devkit/build_optimizer/src/_golden-api.d.ts @@ -1,5 +1,11 @@ export declare function buildOptimizer(options: BuildOptimizerOptions): TransformJavascriptOutput; +export declare const buildOptimizerLoaderPath: string; + +export declare class BuildOptimizerWebpackPlugin { + apply(compiler: Compiler): void; +} + export default function buildOptimizerLoader(this: webpack.loader.LoaderContext, content: string, previousSourceMap: RawSourceMap): void; export declare function getFoldFileTransformer(program: ts.Program): ts.TransformerFactory; diff --git a/packages/angular_devkit/build_optimizer/README.md b/packages/angular_devkit/build_optimizer/README.md index 0a7b3236badb..6b97c1b3588d 100644 --- a/packages/angular_devkit/build_optimizer/README.md +++ b/packages/angular_devkit/build_optimizer/README.md @@ -186,7 +186,12 @@ export interface BuildOptimizerOptions { ## Webpack loader usage: ```typescript +import { BuildOptimizerWebpackPlugin } from '@angular-devkit/build-optimizer'; + module.exports = { + plugins: [ + new BuildOptimizerWebpackPlugin(), + ] module: { rules: [ { diff --git a/packages/angular_devkit/build_optimizer/src/build-optimizer/webpack-loader.ts b/packages/angular_devkit/build_optimizer/src/build-optimizer/webpack-loader.ts index e05666f8b596..eadf1aacb161 100644 --- a/packages/angular_devkit/build_optimizer/src/build-optimizer/webpack-loader.ts +++ b/packages/angular_devkit/build_optimizer/src/build-optimizer/webpack-loader.ts @@ -17,9 +17,31 @@ interface BuildOptimizerLoaderOptions { sourceMap: boolean; } +const alwaysProcess = (path: string) => + // Always process TS files. + path.endsWith('.ts') || path.endsWith('.tsx') + // Always process factory files. + || path.endsWith('.ngfactory.js') || path.endsWith('.ngstyle.js'); + export default function buildOptimizerLoader (this: webpack.loader.LoaderContext, content: string, previousSourceMap: RawSourceMap) { this.cacheable(); + + const skipBuildOptimizer = this._module + && this._module.factoryMeta + && this._module.factoryMeta.skipBuildOptimizer; + + if (!alwaysProcess(this.resourcePath) && skipBuildOptimizer) { + // Skip loading processing this file with Build Optimizer if we determined in + // BuildOptimizerWebpackPlugin that we shouldn't. + + // Webpack typings for previousSourceMap are wrong, they are JSON objects and not strings. + // tslint:disable-next-line:no-any + this.callback(null, content, previousSourceMap as any); + + return; + } + const options: BuildOptimizerLoaderOptions = loaderUtils.getOptions(this) || {}; // Make up names of the intermediate files so we can chain the sourcemaps. @@ -33,12 +55,11 @@ export default function buildOptimizerLoader outputFilePath, emitSourceMap: options.sourceMap, isSideEffectFree: this._module - && this._module.factoryMeta - && this._module.factoryMeta.sideEffectFree, + && this._module.factoryMeta + && this._module.factoryMeta.sideEffectFree, }); if (boOutput.emitSkipped || boOutput.content === null) { - // Webpack typings for previousSourceMap are wrong, they are JSON objects and not strings. // tslint:disable-next-line:no-any this.callback(null, content, previousSourceMap as any); diff --git a/packages/angular_devkit/build_optimizer/src/build-optimizer/webpack-plugin.ts b/packages/angular_devkit/build_optimizer/src/build-optimizer/webpack-plugin.ts new file mode 100644 index 000000000000..b31f0ecbb6cf --- /dev/null +++ b/packages/angular_devkit/build_optimizer/src/build-optimizer/webpack-plugin.ts @@ -0,0 +1,33 @@ + +/** + * @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 { Compiler } from 'webpack'; // tslint:disable-line:no-implicit-dependencies + +export class BuildOptimizerWebpackPlugin { + apply(compiler: Compiler) { + compiler.hooks.normalModuleFactory.tap("BuildOptimizerWebpackPlugin", nmf => { + nmf.hooks.module.tap("BuildOptimizerWebpackPlugin", (module, data) => { + const resolveData = data.resourceResolveData; + if (resolveData && resolveData.descriptionFileData) { + // Only TS packages should use Build Optimizer. + const typings = resolveData.descriptionFileData.typings; + // Notes: + // - a TS package might not have defined typings but still use .d.ts files next to their + // .js files. We don't cover that case because the Angular Package Format (APF) calls for + // using the Typings field and Build Optimizer is geared towards APF. Maybe we could + // provide configuration options to the plugin to cover that case if there's demand. + // - a JS-only package that also happens to provides typings will also be flagged by this + // check. Not sure there's a good way to skip those. + module.factoryMeta.skipBuildOptimizer = !typings; + } + + return module; + }); + }); + } +} diff --git a/packages/angular_devkit/build_optimizer/src/index.ts b/packages/angular_devkit/build_optimizer/src/index.ts index 78ea03851fa8..9e11e4d3052e 100644 --- a/packages/angular_devkit/build_optimizer/src/index.ts +++ b/packages/angular_devkit/build_optimizer/src/index.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ export { default as buildOptimizerLoader } from './build-optimizer/webpack-loader'; +export { BuildOptimizerWebpackPlugin } from './build-optimizer/webpack-plugin'; export { buildOptimizer } from './build-optimizer/build-optimizer'; export { transformJavascript } from './helpers/transform-javascript'; From 9c6c5113d12ad9f5326c426dacc9db1d451d5f39 Mon Sep 17 00:00:00 2001 From: Filipe Silva Date: Thu, 4 Jul 2019 19:11:07 +0100 Subject: [PATCH 2/3] feat(@angular-devkit/build-angular): run Build Optimizer only on TS-generated files --- .../src/angular-cli-files/models/webpack-configs/common.ts | 2 ++ 1 file changed, 2 insertions(+) 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 a553b2d98508..47d241805b2a 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 @@ -5,6 +5,7 @@ * 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 { BuildOptimizerWebpackPlugin } from '@angular-devkit/build-optimizer'; import { tags } from '@angular-devkit/core'; import * as CopyWebpackPlugin from 'copy-webpack-plugin'; import * as path from 'path'; @@ -251,6 +252,7 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { let buildOptimizerUseRule; if (buildOptions.buildOptimizer) { + extraPlugins.push(new BuildOptimizerWebpackPlugin()); buildOptimizerUseRule = { use: [ { From 0958f71eb0ddd4daa60db066072cdeab2247d31d Mon Sep 17 00:00:00 2001 From: Filipe Silva Date: Thu, 4 Jul 2019 19:13:33 +0100 Subject: [PATCH 3/3] refactor: use buildOptimizerLoaderPath --- .../angular-cli-files/models/webpack-configs/common.ts | 10 +++++----- .../models/webpack-configs/typescript.ts | 4 ++-- .../src/build-optimizer/webpack-loader.ts | 2 ++ packages/angular_devkit/build_optimizer/src/index.ts | 5 ++++- 4 files changed, 13 insertions(+), 8 deletions(-) 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 47d241805b2a..e2677039b0f7 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 @@ -5,7 +5,10 @@ * 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 { BuildOptimizerWebpackPlugin } from '@angular-devkit/build-optimizer'; +import { + BuildOptimizerWebpackPlugin, + buildOptimizerLoaderPath, +} from '@angular-devkit/build-optimizer'; import { tags } from '@angular-devkit/core'; import * as CopyWebpackPlugin from 'copy-webpack-plugin'; import * as path from 'path'; @@ -35,9 +38,6 @@ const TerserPlugin = require('terser-webpack-plugin'); // tslint:disable-next-line:no-any const g: any = typeof global !== 'undefined' ? global : {}; -export const buildOptimizerLoader: string = g['_DevKitIsLocal'] - ? require.resolve('@angular-devkit/build-optimizer/src/build-optimizer/webpack-loader') - : '@angular-devkit/build-optimizer/webpack-loader'; // tslint:disable-next-line:no-big-function export function getCommonConfig(wco: WebpackConfigOptions): Configuration { @@ -256,7 +256,7 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { buildOptimizerUseRule = { use: [ { - loader: buildOptimizerLoader, + loader: buildOptimizerLoaderPath, options: { sourceMap: scriptsSourceMap }, }, ], diff --git a/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/typescript.ts b/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/typescript.ts index 9660f0fe662a..eef882522e00 100644 --- a/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/typescript.ts +++ b/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/typescript.ts @@ -7,6 +7,7 @@ */ // tslint:disable // TODO: cleanup this file, it's copied as is from Angular CLI. +import { buildOptimizerLoaderPath } from '@angular-devkit/build-optimizer'; import * as path from 'path'; import { AngularCompilerPlugin, @@ -14,7 +15,6 @@ import { NgToolsLoader, PLATFORM } from '@ngtools/webpack'; -import { buildOptimizerLoader } from './common'; import { WebpackConfigOptions, BuildOptions } from '../build-options'; function _pluginOptionsOverrides( @@ -113,7 +113,7 @@ export function getAotConfig(wco: WebpackConfigOptions, i18nExtract = false) { const loaders: any[] = [NgToolsLoader]; if (buildOptions.buildOptimizer) { loaders.unshift({ - loader: buildOptimizerLoader, + loader: buildOptimizerLoaderPath, options: { sourceMap: buildOptions.sourceMap.scripts } }); } diff --git a/packages/angular_devkit/build_optimizer/src/build-optimizer/webpack-loader.ts b/packages/angular_devkit/build_optimizer/src/build-optimizer/webpack-loader.ts index eadf1aacb161..ec4c138a7900 100644 --- a/packages/angular_devkit/build_optimizer/src/build-optimizer/webpack-loader.ts +++ b/packages/angular_devkit/build_optimizer/src/build-optimizer/webpack-loader.ts @@ -17,6 +17,8 @@ interface BuildOptimizerLoaderOptions { sourceMap: boolean; } +export const buildOptimizerLoaderPath = __filename; + const alwaysProcess = (path: string) => // Always process TS files. path.endsWith('.ts') || path.endsWith('.tsx') diff --git a/packages/angular_devkit/build_optimizer/src/index.ts b/packages/angular_devkit/build_optimizer/src/index.ts index 9e11e4d3052e..96b201de9f66 100644 --- a/packages/angular_devkit/build_optimizer/src/index.ts +++ b/packages/angular_devkit/build_optimizer/src/index.ts @@ -5,7 +5,10 @@ * 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 */ -export { default as buildOptimizerLoader } from './build-optimizer/webpack-loader'; +export { + default as buildOptimizerLoader, + buildOptimizerLoaderPath, +} from './build-optimizer/webpack-loader'; export { BuildOptimizerWebpackPlugin } from './build-optimizer/webpack-plugin'; export { buildOptimizer } from './build-optimizer/build-optimizer';