Skip to content

Commit d296778

Browse files
authored
feature(compiler): add support for AoT to the CLI. (#2333)
Also adding a new package, webpack, which is a plugin and loader for webpack that adds support for AoT. It is behind a `--aot` flag in the CLI that is supported by build and serve.
1 parent 3102453 commit d296778

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+1258
-172
lines changed

lib/bootstrap-local.js

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const ts = require('typescript');
77

88

99
global.angularCliIsLocal = true;
10+
global.angularCliPackages = require('./packages');
1011

1112
const compilerOptions = JSON.parse(fs.readFileSync(path.join(__dirname, '../tsconfig.json')));
1213

lib/packages.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ const packages = fs.readdirSync(packageRoot)
1111
.map(pkgName => ({ name: pkgName, root: path.join(packageRoot, pkgName) }))
1212
.filter(pkg => fs.statSync(pkg.root).isDirectory())
1313
.reduce((packages, pkg) => {
14-
let name = pkg == 'angular-cli' ? 'angular-cli' : `@angular-cli/${pkg.name}`;
14+
let pkgJson = JSON.parse(fs.readFileSync(path.join(pkg.root, 'package.json'), 'utf8'));
15+
let name = pkgJson['name'];
1516
packages[name] = {
17+
dist: path.join(__dirname, '../dist', pkg.name),
18+
packageJson: path.join(pkg.root, 'package.json'),
1619
root: pkg.root,
1720
main: path.resolve(pkg.root, 'src/index.ts')
1821
};

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
"fs.realpath": "^1.0.0",
7272
"glob": "^7.0.3",
7373
"handlebars": "^4.0.5",
74+
"html-loader": "^0.4.4",
7475
"html-webpack-plugin": "^2.19.0",
7576
"istanbul-instrumenter-loader": "^0.2.0",
7677
"json-loader": "^0.5.4",

packages/angular-cli/blueprints/component/index.js

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
var path = require('path');
2-
var chalk = require('chalk');
3-
var Blueprint = require('ember-cli/lib/models/blueprint');
4-
var dynamicPathParser = require('../../utilities/dynamic-path-parser');
1+
const path = require('path');
2+
const chalk = require('chalk');
3+
const Blueprint = require('ember-cli/lib/models/blueprint');
4+
const dynamicPathParser = require('../../utilities/dynamic-path-parser');
55
const findParentModule = require('../../utilities/find-parent-module').default;
6-
var getFiles = Blueprint.prototype.files;
6+
const getFiles = Blueprint.prototype.files;
77
const stringUtils = require('ember-cli-string-utils');
88
const astUtils = require('../../utilities/ast-utils');
9+
const NodeHost = require('@angular-cli/ast-tools').NodeHost;
910

1011
module.exports = {
1112
description: '',
@@ -117,7 +118,7 @@ module.exports = {
117118
if (!options['skip-import']) {
118119
returns.push(
119120
astUtils.addDeclarationToModule(this.pathToModule, className, importPath)
120-
.then(change => change.apply()));
121+
.then(change => change.apply(NodeHost)));
121122
}
122123

123124
return Promise.all(returns);

packages/angular-cli/blueprints/directive/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ var dynamicPathParser = require('../../utilities/dynamic-path-parser');
33
const stringUtils = require('ember-cli-string-utils');
44
const astUtils = require('../../utilities/ast-utils');
55
const findParentModule = require('../../utilities/find-parent-module').default;
6+
const NodeHost = require('@angular-cli/ast-tools').NodeHost;
67

78
module.exports = {
89
description: '',
@@ -73,7 +74,7 @@ module.exports = {
7374
if (!options['skip-import']) {
7475
returns.push(
7576
astUtils.addDeclarationToModule(this.pathToModule, className, importPath)
76-
.then(change => change.apply()));
77+
.then(change => change.apply(NodeHost)));
7778
}
7879

7980
return Promise.all(returns);

packages/angular-cli/blueprints/pipe/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ var dynamicPathParser = require('../../utilities/dynamic-path-parser');
33
const stringUtils = require('ember-cli-string-utils');
44
const astUtils = require('../../utilities/ast-utils');
55
const findParentModule = require('../../utilities/find-parent-module').default;
6+
const NodeHost = require('@angular-cli/ast-tools').NodeHost;
67

78
module.exports = {
89
description: '',
@@ -61,7 +62,7 @@ module.exports = {
6162
if (!options['skip-import']) {
6263
returns.push(
6364
astUtils.addDeclarationToModule(this.pathToModule, className, importPath)
64-
.then(change => change.apply()));
65+
.then(change => change.apply(NodeHost)));
6566
}
6667

6768
return Promise.all(returns);

packages/angular-cli/commands/build.ts

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export interface BuildOptions {
1010
watcher?: string;
1111
supressSizes: boolean;
1212
baseHref?: string;
13+
aot?: boolean;
1314
}
1415

1516
const BuildCommand = Command.extend({
@@ -30,6 +31,7 @@ const BuildCommand = Command.extend({
3031
{ name: 'watcher', type: String },
3132
{ name: 'suppress-sizes', type: Boolean, default: false },
3233
{ name: 'base-href', type: String, default: null, aliases: ['bh'] },
34+
{ name: 'aot', type: Boolean, default: false }
3335
],
3436

3537
run: function (commandOptions: BuildOptions) {

packages/angular-cli/commands/serve.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export interface ServeTaskOptions {
2525
ssl?: boolean;
2626
sslKey?: string;
2727
sslCert?: string;
28+
aot?: boolean;
2829
}
2930

3031
const ServeCommand = Command.extend({
@@ -77,7 +78,8 @@ const ServeCommand = Command.extend({
7778
{ name: 'environment', type: String, default: '', aliases: ['e'] },
7879
{ name: 'ssl', type: Boolean, default: false },
7980
{ name: 'ssl-key', type: String, default: 'ssl/server.key' },
80-
{ name: 'ssl-cert', type: String, default: 'ssl/server.crt' }
81+
{ name: 'ssl-cert', type: String, default: 'ssl/server.crt' },
82+
{ name: 'aot', type: Boolean, default: false }
8183
],
8284

8385
run: function(commandOptions: ServeTaskOptions) {

packages/angular-cli/models/webpack-build-common.ts

+5-24
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1+
import * as webpack from 'webpack';
12
import * as path from 'path';
3+
import {BaseHrefWebpackPlugin} from '@angular-cli/base-href-webpack';
4+
25
const CopyWebpackPlugin = require('copy-webpack-plugin');
36
const HtmlWebpackPlugin = require('html-webpack-plugin');
4-
import * as webpack from 'webpack';
5-
const atl = require('awesome-typescript-loader');
6-
7-
import { BaseHrefWebpackPlugin } from '@angular-cli/base-href-webpack';
8-
import { findLazyModules } from './find-lazy-modules';
97

108

119
export function getWebpackCommonConfig(
@@ -23,7 +21,6 @@ export function getWebpackCommonConfig(
2321
const scripts = appConfig.scripts
2422
? appConfig.scripts.map((script: string) => path.resolve(appRoot, script))
2523
: [];
26-
const lazyModules = findLazyModules(appRoot);
2724

2825
let entry: { [key: string]: string[] } = {
2926
main: [appMain]
@@ -56,21 +53,7 @@ export function getWebpackCommonConfig(
5653
}
5754
],
5855
loaders: [
59-
{
60-
test: /\.ts$/,
61-
loaders: [
62-
{
63-
loader: 'awesome-typescript-loader',
64-
query: {
65-
useForkChecker: true,
66-
tsconfig: path.resolve(appRoot, appConfig.tsconfig)
67-
}
68-
}, {
69-
loader: 'angular2-template-loader'
70-
}
71-
],
72-
exclude: [/\.(spec|e2e)\.ts$/]
73-
},
56+
// TypeScript loaders are separated into webpack-build-typescript.
7457

7558
// in main, load css as raw text
7659
       {
@@ -115,16 +98,14 @@ export function getWebpackCommonConfig(
11598

11699
       { test: /\.json$/, loader: 'json-loader' },
117100
       { test: /\.(jpg|png|gif)$/, loader: 'url-loader?limit=10000' },
118-
       { test: /\.html$/, loader: 'raw-loader' },
101+
       { test: /\.html$/, loader: 'html-loader' },
119102

120103
{ test: /\.(otf|woff|ttf|svg)$/, loader: 'url?limit=10000' },
121104
{ test: /\.woff2$/, loader: 'url?limit=10000&mimetype=font/woff2' },
122105
{ test: /\.eot$/, loader: 'file' }
123106
]
124107
},
125108
plugins: [
126-
new webpack.ContextReplacementPlugin(/.*/, appRoot, lazyModules),
127-
new atl.ForkCheckerPlugin(),
128109
new HtmlWebpackPlugin({
129110
template: path.resolve(appRoot, appConfig.index),
130111
chunksSortMode: 'dependency'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import * as path from 'path';
2+
import * as webpack from 'webpack';
3+
import {findLazyModules} from './find-lazy-modules';
4+
import {NgcWebpackPlugin} from '@ngtools/webpack';
5+
6+
const atl = require('awesome-typescript-loader');
7+
8+
const g: any = global;
9+
const webpackLoader: string = g['angularCliIsLocal']
10+
? g.angularCliPackages['@ngtools/webpack'].main
11+
: '@ngtools/webpack';
12+
13+
14+
export const getWebpackNonAotConfigPartial = function(projectRoot: string, appConfig: any) {
15+
const appRoot = path.resolve(projectRoot, appConfig.root);
16+
const lazyModules = findLazyModules(appRoot);
17+
18+
return {
19+
module: {
20+
loaders: [
21+
{
22+
test: /\.ts$/,
23+
loaders: [{
24+
loader: 'awesome-typescript-loader',
25+
query: {
26+
useForkChecker: true,
27+
tsconfig: path.resolve(appRoot, appConfig.tsconfig)
28+
}
29+
}, {
30+
loader: 'angular2-template-loader'
31+
}],
32+
exclude: [/\.(spec|e2e)\.ts$/]
33+
}
34+
],
35+
},
36+
plugins: [
37+
new webpack.ContextReplacementPlugin(/.*/, appRoot, lazyModules),
38+
new atl.ForkCheckerPlugin(),
39+
]
40+
};
41+
};
42+
43+
export const getWebpackAotConfigPartial = function(projectRoot: string, appConfig: any) {
44+
return {
45+
module: {
46+
loaders: [
47+
{
48+
test: /\.ts$/,
49+
loader: webpackLoader,
50+
exclude: [/\.(spec|e2e)\.ts$/]
51+
}
52+
]
53+
},
54+
plugins: [
55+
new NgcWebpackPlugin({
56+
project: path.resolve(projectRoot, appConfig.root, appConfig.tsconfig),
57+
baseDir: path.resolve(projectRoot, ''),
58+
entryModule: path.join(projectRoot, appConfig.root, 'app/app.module#AppModule'),
59+
genDir: path.join(projectRoot, appConfig.outDir, 'ngfactory')
60+
}),
61+
]
62+
};
63+
};

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

+23-15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import {
2+
getWebpackAotConfigPartial,
3+
getWebpackNonAotConfigPartial
4+
} from './webpack-build-typescript';
15
const webpackMerge = require('webpack-merge');
26
import { CliConfig } from './config';
37
import {
@@ -12,50 +16,54 @@ export class NgCliWebpackConfig {
1216
// TODO: When webpack2 types are finished lets replace all these any types
1317
// so this is more maintainable in the future for devs
1418
public config: any;
15-
private devConfigPartial: any;
16-
private prodConfigPartial: any;
17-
private baseConfig: any;
1819

1920
constructor(
2021
public ngCliProject: any,
2122
public target: string,
2223
public environment: string,
2324
outputDir?: string,
24-
baseHref?: string
25+
baseHref?: string,
26+
isAoT = false
2527
) {
2628
const config: CliConfig = CliConfig.fromProject();
2729
const appConfig = config.config.apps[0];
2830

2931
appConfig.outDir = outputDir || appConfig.outDir;
3032

31-
this.baseConfig = getWebpackCommonConfig(
33+
let baseConfig = getWebpackCommonConfig(
3234
this.ngCliProject.root,
3335
environment,
3436
appConfig,
3537
baseHref
3638
);
37-
this.devConfigPartial = getWebpackDevConfigPartial(this.ngCliProject.root, appConfig);
38-
this.prodConfigPartial = getWebpackProdConfigPartial(this.ngCliProject.root, appConfig);
39+
let targetConfigPartial = this.getTargetConfig(this.ngCliProject.root, appConfig);
40+
const typescriptConfigPartial = isAoT
41+
? getWebpackAotConfigPartial(this.ngCliProject.root, appConfig)
42+
: getWebpackNonAotConfigPartial(this.ngCliProject.root, appConfig);
3943

4044
if (appConfig.mobile) {
4145
let mobileConfigPartial = getWebpackMobileConfigPartial(this.ngCliProject.root, appConfig);
4246
let mobileProdConfigPartial = getWebpackMobileProdConfigPartial(this.ngCliProject.root,
4347
appConfig);
44-
this.baseConfig = webpackMerge(this.baseConfig, mobileConfigPartial);
45-
this.prodConfigPartial = webpackMerge(this.prodConfigPartial, mobileProdConfigPartial);
48+
baseConfig = webpackMerge(baseConfig, mobileConfigPartial);
49+
if (this.target == 'production') {
50+
targetConfigPartial = webpackMerge(targetConfigPartial, mobileProdConfigPartial);
51+
}
4652
}
4753

48-
this.generateConfig();
54+
this.config = webpackMerge(
55+
baseConfig,
56+
targetConfigPartial,
57+
typescriptConfigPartial
58+
);
4959
}
5060

51-
generateConfig(): void {
61+
getTargetConfig(projectRoot: string, appConfig: any): any {
5262
switch (this.target) {
5363
case 'development':
54-
this.config = webpackMerge(this.baseConfig, this.devConfigPartial);
55-
break;
64+
return getWebpackDevConfigPartial(projectRoot, appConfig);
5665
case 'production':
57-
this.config = webpackMerge(this.baseConfig, this.prodConfigPartial);
58-
break;
66+
return getWebpackProdConfigPartial(projectRoot, appConfig);
5967
default:
6068
throw new Error("Invalid build target. Only 'development' and 'production' are available.");
6169
}

packages/angular-cli/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"@angular/platform-browser": "^2.0.0",
3434
"@angular/platform-server": "^2.0.0",
3535
"@angular/tsc-wrapped": "^0.3.0",
36+
"@ngtools/webpack": "latest",
3637
"angular2-template-loader": "^0.5.0",
3738
"awesome-typescript-loader": "^2.2.3",
3839
"chalk": "^1.1.3",
@@ -53,6 +54,7 @@
5354
"fs.realpath": "^1.0.0",
5455
"glob": "^7.0.3",
5556
"handlebars": "^4.0.5",
57+
"html-loader": "^0.4.4",
5658
"html-webpack-plugin": "^2.19.0",
5759
"istanbul-instrumenter-loader": "^0.2.0",
5860
"json-loader": "^0.5.4",

packages/angular-cli/tasks/build-webpack-watch.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ export default Task.extend({
2121
runTaskOptions.target,
2222
runTaskOptions.environment,
2323
runTaskOptions.outputPath,
24-
runTaskOptions.baseHref
24+
runTaskOptions.baseHref,
25+
runTaskOptions.aot
2526
).config;
2627
const webpackCompiler: any = webpack(config);
2728

packages/angular-cli/tasks/build-webpack.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ export default <any>Task.extend({
1717

1818
const outputDir = runTaskOptions.outputPath || CliConfig.fromProject().config.apps[0].outDir;
1919
rimraf.sync(path.resolve(project.root, outputDir));
20-
2120
const config = new NgCliWebpackConfig(
2221
project,
2322
runTaskOptions.target,
2423
runTaskOptions.environment,
2524
outputDir,
26-
runTaskOptions.baseHref
25+
runTaskOptions.baseHref,
26+
runTaskOptions.aot
2727
).config;
2828

2929
// fail on build error

packages/angular-cli/tasks/serve-webpack.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,12 @@ export default Task.extend({
1919
let webpackCompiler: any;
2020

2121
let config = new NgCliWebpackConfig(
22-
this.project, commandOptions.target,
23-
commandOptions.environment
22+
this.project,
23+
commandOptions.target,
24+
commandOptions.environment,
25+
undefined,
26+
undefined,
27+
commandOptions.aot
2428
).config;
2529

2630
// This allows for live reload of page when changes are made to repo.

0 commit comments

Comments
 (0)