From e72693a948eaba0bd5b00427d80f31c8d4e89758 Mon Sep 17 00:00:00 2001 From: Sumit Arora Date: Tue, 14 Feb 2017 11:13:17 -0500 Subject: [PATCH] feat(@angular/cli): remove deduped licenses --- docs/documentation/build.md | 2 ++ package.json | 1 + packages/@angular/cli/commands/build.ts | 9 ++++++++- packages/@angular/cli/models/build-options.ts | 1 + .../cli/models/webpack-configs/production.ts | 11 ++++++++++- packages/@angular/cli/package.json | 1 + packages/@angular/cli/tasks/eject.ts | 12 ++++++++++-- tests/e2e/tests/build/dev-build.ts | 4 +++- tests/e2e/tests/build/extract-licenses.ts | 10 ++++++++++ tests/e2e/tests/build/prod-build.ts | 1 + 10 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 tests/e2e/tests/build/extract-licenses.ts diff --git a/docs/documentation/build.md b/docs/documentation/build.md index a04e8397cf..bb1186f776 100644 --- a/docs/documentation/build.md +++ b/docs/documentation/build.md @@ -81,6 +81,8 @@ Flag | `--dev` | `--prod` `--sourcemaps` | `true` | `false` `--extract-css` | `false` | `true` +`--extract-licenses` Extract all licenses in a separate file, in the case of production builds only. +`--i18n-file` Localization file to use for i18n. `--prod` also sets the following non-flaggable settings: - Adds service worker if configured in `.angular-cli.json`. - Replaces `process.env.NODE_ENV` in modules with the `production` value (this is needed for some libraries, like react). diff --git a/package.json b/package.json index 76d69c8f39..d5e33047af 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "less": "^2.7.2", "less-loader": "^4.0.2", "loader-utils": "^1.0.2", + "license-webpack-plugin": "^0.4.3", "lodash": "^4.11.1", "magic-string": "^0.19.0", "memory-fs": "^0.4.1", diff --git a/packages/@angular/cli/commands/build.ts b/packages/@angular/cli/commands/build.ts index 98e69a1114..5431d72fb7 100644 --- a/packages/@angular/cli/commands/build.ts +++ b/packages/@angular/cli/commands/build.ts @@ -96,7 +96,8 @@ export const baseBuildCommandOptions: any = [ }, { name: 'watch', - type: Boolean, default: false, + type: Boolean, + default: false, aliases: ['w'], description: 'Run build when files change.' }, @@ -131,6 +132,12 @@ export const baseBuildCommandOptions: any = [ type: Boolean, default: false, description: 'Do not use the real path when resolving modules.' + }, + { + name: 'extract-licenses', + type: Boolean, + default: true, + description: 'Extract all licenses in a separate file, in the case of production builds only.' } ]; diff --git a/packages/@angular/cli/models/build-options.ts b/packages/@angular/cli/models/build-options.ts index cff1068e4f..bea9436e18 100644 --- a/packages/@angular/cli/models/build-options.ts +++ b/packages/@angular/cli/models/build-options.ts @@ -19,4 +19,5 @@ export interface BuildOptions { app?: string; deleteOutputPath?: boolean; preserveSymlinks?: boolean; + extractLicenses?: boolean; } diff --git a/packages/@angular/cli/models/webpack-configs/production.ts b/packages/@angular/cli/models/webpack-configs/production.ts index 1794471bfb..904cd5468b 100644 --- a/packages/@angular/cli/models/webpack-configs/production.ts +++ b/packages/@angular/cli/models/webpack-configs/production.ts @@ -7,6 +7,7 @@ import { StaticAssetPlugin } from '../../plugins/static-asset'; import { GlobCopyWebpackPlugin } from '../../plugins/glob-copy-webpack-plugin'; import { WebpackConfigOptions } from '../webpack-config'; +const licensePlugin = require('license-webpack-plugin'); export const getProdConfig = function (wco: WebpackConfigOptions) { const { projectRoot, buildOptions, appConfig } = wco; @@ -79,6 +80,13 @@ export const getProdConfig = function (wco: WebpackConfigOptions) { entryPoints['sw-register'] = [registerPath]; } + if (buildOptions.extractLicenses) { + extraPlugins.push(new licensePlugin({ + pattern: /^(MIT|ISC|BSD.*)$/, + suppressErrors: true + })); + } + return { entry: entryPoints, plugins: [ @@ -89,7 +97,8 @@ export const getProdConfig = function (wco: WebpackConfigOptions) { new webpack.optimize.UglifyJsPlugin({ mangle: { screw_ie8: true }, compress: { screw_ie8: true, warnings: buildOptions.verbose }, - sourceMap: buildOptions.sourcemaps + sourceMap: buildOptions.sourcemaps, + comments: false }) ].concat(extraPlugins) }; diff --git a/packages/@angular/cli/package.json b/packages/@angular/cli/package.json index c5b82809f1..f159add0d6 100644 --- a/packages/@angular/cli/package.json +++ b/packages/@angular/cli/package.json @@ -52,6 +52,7 @@ "json-loader": "^0.5.4", "less": "^2.7.2", "less-loader": "^4.0.2", + "license-webpack-plugin": "^0.4.2", "lodash": "^4.11.1", "memory-fs": "^0.4.1", "minimatch": "^3.0.3", diff --git a/packages/@angular/cli/tasks/eject.ts b/packages/@angular/cli/tasks/eject.ts index 97f1435dfa..ea23fef07c 100644 --- a/packages/@angular/cli/tasks/eject.ts +++ b/packages/@angular/cli/tasks/eject.ts @@ -21,6 +21,7 @@ const angularCliPlugins = require('../plugins/webpack'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const SilentError = require('silent-error'); +const licensePlugin = require('license-webpack-plugin'); const Task = require('../ember-cli/lib/models/task'); const ProgressPlugin = require('webpack/lib/ProgressPlugin'); @@ -134,6 +135,12 @@ class JsonWebpackSerializer { return plugin.defaultValues; } + private _licenseWebpackPlugin(plugin: any) { + return { + 'pattern': plugin.pattern + }; + } + private _pluginsReplacer(plugins: any[]) { return plugins.map(plugin => { let args = plugin.options || undefined; @@ -180,13 +187,14 @@ class JsonWebpackSerializer { args = this._environmentPlugin(plugin); this._addImport('webpack', 'EnvironmentPlugin'); break; - + case licensePlugin: + args = this._licenseWebpackPlugin(plugin); + this.variableImports['license-webpack-plugin'] = 'licensePlugin'; default: if (plugin.constructor.name == 'AngularServiceWorkerPlugin') { this._addImport('@angular/service-worker/build/webpack', plugin.constructor.name); } break; - } const argsSerialized = JSON.stringify(args, (k, v) => this._replacer(k, v), 2) || ''; diff --git a/tests/e2e/tests/build/dev-build.ts b/tests/e2e/tests/build/dev-build.ts index 85eb154ab3..34729084b0 100644 --- a/tests/e2e/tests/build/dev-build.ts +++ b/tests/e2e/tests/build/dev-build.ts @@ -1,7 +1,8 @@ import {ng} from '../../utils/process'; -import {expectFileToMatch} from '../../utils/fs'; +import {expectFileToMatch, expectFileToExist} from '../../utils/fs'; import {expectGitToBeClean} from '../../utils/git'; import {getGlobalVariable} from '../../utils/env'; +import {expectToFail} from '../../utils/utils'; export default function() { @@ -9,6 +10,7 @@ export default function() { return ng('build', '--env=dev') .then(() => expectFileToMatch('dist/index.html', 'main.bundle.js')) + .then(() => expectToFail(() => expectFileToExist('dist/3rdpartylicenses.txt'))) // If this is an ejected test, the eject will create files so git will not be clean. .then(() => !ejected && expectGitToBeClean()); } diff --git a/tests/e2e/tests/build/extract-licenses.ts b/tests/e2e/tests/build/extract-licenses.ts new file mode 100644 index 0000000000..601c97796b --- /dev/null +++ b/tests/e2e/tests/build/extract-licenses.ts @@ -0,0 +1,10 @@ +import {join} from 'path'; +import {expectFileToExist} from '../../utils/fs'; +import {expectToFail} from '../../utils/utils'; +import {ng} from '../../utils/process'; + +export default function() { + return ng('build', '--prod', '--extract-licenses=false') + .then(() => expectFileToExist(join(process.cwd(), 'dist'))) + .then(() => expectToFail(() => expectFileToExist('dist/3rdpartylicenses.txt'))); +} diff --git a/tests/e2e/tests/build/prod-build.ts b/tests/e2e/tests/build/prod-build.ts index 8b064460d1..a42d01624e 100644 --- a/tests/e2e/tests/build/prod-build.ts +++ b/tests/e2e/tests/build/prod-build.ts @@ -17,6 +17,7 @@ export default function() { // Check for cache busting hash script src .then(() => expectFileToMatch('dist/index.html', /main\.[0-9a-f]{20}\.bundle\.js/)) .then(() => expectFileToMatch('dist/index.html', /styles\.[0-9a-f]{20}\.bundle\.css/)) + .then(() => expectFileToExist('dist/3rdpartylicenses.txt')) // Defaults to AoT .then(() => { const main = readdirSync('./dist').find(name => !!name.match(/main.[a-z0-9]+\.bundle\.js/));