Skip to content

Commit 07a0630

Browse files
committed
feat(@angular/cli): add ngo support
1 parent 64e6b94 commit 07a0630

File tree

10 files changed

+81
-13
lines changed

10 files changed

+81
-13
lines changed

docs/documentation/build.md

+10
Original file line numberDiff line numberDiff line change
@@ -322,3 +322,13 @@ Note: service worker support is experimental and subject to change.
322322
Show circular dependency warnings on builds.
323323
</p>
324324
</details>
325+
326+
<details>
327+
<summary>ngo</summary>
328+
<p>
329+
<code>--ngo</code>
330+
</p>
331+
<p>
332+
Enables NGO optimizations when using `--aot`.
333+
</p>
334+
</details>

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
"magic-string": "^0.19.0",
7575
"memory-fs": "^0.4.1",
7676
"minimatch": "^3.0.3",
77+
"ngo-loader": "github:angular/ngo",
7778
"node-modules-path": "^1.0.0",
7879
"nopt": "^4.0.1",
7980
"opn": "4.0.2",

packages/@angular/cli/commands/build.ts

+22-7
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export const baseBuildCommandOptions: any = [
2020
{
2121
name: 'environment',
2222
type: String,
23-
aliases: ['e'] ,
23+
aliases: ['e'],
2424
description: 'Defines the build environment.'
2525
},
2626
{
@@ -43,7 +43,6 @@ export const baseBuildCommandOptions: any = [
4343
{
4444
name: 'vendor-chunk',
4545
type: Boolean,
46-
default: true,
4746
aliases: ['vc'],
4847
description: 'Use a separate bundle containing only vendor libraries.'
4948
},
@@ -144,6 +143,12 @@ export const baseBuildCommandOptions: any = [
144143
type: Boolean,
145144
aliases: ['scd'],
146145
description: 'Show circular dependency warnings on builds.'
146+
},
147+
{
148+
name: 'ngo',
149+
type: Boolean,
150+
default: false,
151+
description: 'Enables NGO optimizations when using `--aot`.'
147152
}
148153
];
149154

@@ -158,15 +163,25 @@ const BuildCommand = Command.extend({
158163

159164
availableOptions: baseBuildCommandOptions.concat([
160165
{
161-
name: 'stats-json',
162-
type: Boolean,
163-
default: false,
164-
description: oneLine`Generates a \`stats.json\` file which can be analyzed using tools
166+
name: 'stats-json',
167+
type: Boolean,
168+
default: false,
169+
description: oneLine`Generates a \`stats.json\` file which can be analyzed using tools
165170
such as: \`webpack-bundle-analyzer\` or https://webpack.github.io/analyse.`
166-
}
171+
}
167172
]),
168173

169174
run: function (commandOptions: BuildTaskOptions) {
175+
176+
// Remove vendor chunk if undefined and --ngo if on.
177+
if (commandOptions.vendorChunk === undefined) {
178+
if (commandOptions.ngo) {
179+
commandOptions.vendorChunk = false;
180+
} else {
181+
commandOptions.vendorChunk = true;
182+
}
183+
}
184+
170185
// Check angular version.
171186
Version.assertAngularVersionIs2_3_1OrHigher(this.project.root);
172187

packages/@angular/cli/models/build-options.ts

+1
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@ export interface BuildOptions {
2121
preserveSymlinks?: boolean;
2222
extractLicenses?: boolean;
2323
showCircularDependencies?: boolean;
24+
ngo?: boolean;
2425
}

packages/@angular/cli/models/webpack-config.ts

+4
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ export class NgCliWebpackConfig {
7070
if (buildOptions.target !== 'development' && buildOptions.target !== 'production') {
7171
throw new Error("Invalid build target. Only 'development' and 'production' are available.");
7272
}
73+
74+
if (buildOptions.ngo && !(buildOptions.aot || buildOptions.target === 'production')) {
75+
throw new Error('The `--ngo` option cannot be used without `--aot` (or `--prod`).');
76+
}
7377
}
7478

7579
// Fill in defaults for build targets

packages/@angular/cli/models/webpack-configs/common.ts

+11
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const CircularDependencyPlugin = require('circular-dependency-plugin');
1818
* require('json-loader')
1919
* require('url-loader')
2020
* require('file-loader')
21+
* require('ngo-loader')
2122
*/
2223

2324
export function getCommonConfig(wco: WebpackConfigOptions) {
@@ -79,6 +80,16 @@ export function getCommonConfig(wco: WebpackConfigOptions) {
7980
}));
8081
}
8182

83+
if (buildOptions.ngo) {
84+
extraRules.push({
85+
test: /\.js$/,
86+
use: [{
87+
loader: 'ngo-loader',
88+
options: { sourceMap: buildOptions.sourcemaps }
89+
}]
90+
});
91+
}
92+
8293
return {
8394
resolve: {
8495
extensions: ['.ts', '.js'],

packages/@angular/cli/models/webpack-configs/production.ts

+12-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { StaticAssetPlugin } from '../../plugins/static-asset';
77
import { GlobCopyWebpackPlugin } from '../../plugins/glob-copy-webpack-plugin';
88
import { WebpackConfigOptions } from '../webpack-config';
99

10+
const PurifyPlugin = require('ngo-loader').PurifyPlugin;
1011
const licensePlugin = require('license-webpack-plugin');
1112

1213
export const getProdConfig = function (wco: WebpackConfigOptions) {
@@ -91,19 +92,27 @@ export const getProdConfig = function (wco: WebpackConfigOptions) {
9192
}));
9293
}
9394

95+
const uglifyCompressOptions: any = { screw_ie8: true, warnings: buildOptions.verbose };
96+
97+
if (buildOptions.ngo) {
98+
// This plugin must be before webpack.optimize.UglifyJsPlugin.
99+
extraPlugins.push(new PurifyPlugin());
100+
uglifyCompressOptions.pure_getters = true;
101+
}
102+
94103
return {
95104
entry: entryPoints,
96-
plugins: [
105+
plugins: extraPlugins.concat([
97106
new webpack.EnvironmentPlugin({
98107
'NODE_ENV': 'production'
99108
}),
100109
new (<any>webpack).HashedModuleIdsPlugin(),
101110
new webpack.optimize.UglifyJsPlugin(<any>{
102111
mangle: { screw_ie8: true },
103-
compress: { screw_ie8: true, warnings: buildOptions.verbose },
112+
compress: uglifyCompressOptions,
104113
sourceMap: buildOptions.sourcemaps,
105114
comments: false
106115
})
107-
].concat(extraPlugins)
116+
])
108117
};
109118
};

packages/@angular/cli/models/webpack-configs/typescript.ts

+10-3
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ function _createAotPlugin(wco: WebpackConfigOptions, options: any) {
7474
}, options));
7575
}
7676

77-
7877
export const getNonAotConfig = function(wco: WebpackConfigOptions) {
7978
const { appConfig, projectRoot } = wco;
8079
const tsConfigPath = path.resolve(projectRoot, appConfig.root, appConfig.tsconfig);
@@ -86,7 +85,7 @@ export const getNonAotConfig = function(wco: WebpackConfigOptions) {
8685
};
8786

8887
export const getAotConfig = function(wco: WebpackConfigOptions) {
89-
const { projectRoot, appConfig } = wco;
88+
const { projectRoot, buildOptions, appConfig } = wco;
9089
const tsConfigPath = path.resolve(projectRoot, appConfig.root, appConfig.tsconfig);
9190
const testTsConfigPath = path.resolve(projectRoot, appConfig.root, appConfig.testTsconfig);
9291

@@ -99,8 +98,16 @@ export const getAotConfig = function(wco: WebpackConfigOptions) {
9998
pluginOptions.exclude = exclude;
10099
}
101100

101+
let ngoLoader: any = [];
102+
if (buildOptions.ngo) {
103+
ngoLoader = [{
104+
loader: 'ngo-loader',
105+
options: { sourceMap: buildOptions.sourcemaps }
106+
}];
107+
}
108+
102109
return {
103-
module: { rules: [{ test: /\.ts$/, loader: webpackLoader }] },
110+
module: { rules: [{ test: /\.ts$/, use: [...ngoLoader, webpackLoader] }] },
104111
plugins: [ _createAotPlugin(wco, pluginOptions) ]
105112
};
106113
};

packages/@angular/cli/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"lodash": "^4.11.1",
6060
"memory-fs": "^0.4.1",
6161
"minimatch": "^3.0.3",
62+
"ngo-loader": "github:angular/ngo",
6263
"node-modules-path": "^1.0.0",
6364
"nopt": "^4.0.1",
6465
"opn": "4.0.2",

tests/e2e/tests/build/ngo.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { ng } from '../../utils/process';
2+
import { expectFileToMatch } from '../../utils/fs';
3+
import { expectToFail } from '../../utils/utils';
4+
5+
6+
export default function () {
7+
return ng('build', '--aot', '--ngo')
8+
.then(() => expectToFail(() => expectFileToMatch('dist/vendor.js', /\.decorators =/)));
9+
}

0 commit comments

Comments
 (0)