Skip to content

Commit

Permalink
feature(compiler): add support for AoT to the CLI. (#2333)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
hansl committed Sep 28, 2016
1 parent 3102453 commit d296778
Show file tree
Hide file tree
Showing 58 changed files with 1,258 additions and 172 deletions.
1 change: 1 addition & 0 deletions lib/bootstrap-local.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const ts = require('typescript');


global.angularCliIsLocal = true;
global.angularCliPackages = require('./packages');

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

Expand Down
5 changes: 4 additions & 1 deletion lib/packages.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ const packages = fs.readdirSync(packageRoot)
.map(pkgName => ({ name: pkgName, root: path.join(packageRoot, pkgName) }))
.filter(pkg => fs.statSync(pkg.root).isDirectory())
.reduce((packages, pkg) => {
let name = pkg == 'angular-cli' ? 'angular-cli' : `@angular-cli/${pkg.name}`;
let pkgJson = JSON.parse(fs.readFileSync(path.join(pkg.root, 'package.json'), 'utf8'));
let name = pkgJson['name'];
packages[name] = {
dist: path.join(__dirname, '../dist', pkg.name),
packageJson: path.join(pkg.root, 'package.json'),
root: pkg.root,
main: path.resolve(pkg.root, 'src/index.ts')
};
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"fs.realpath": "^1.0.0",
"glob": "^7.0.3",
"handlebars": "^4.0.5",
"html-loader": "^0.4.4",
"html-webpack-plugin": "^2.19.0",
"istanbul-instrumenter-loader": "^0.2.0",
"json-loader": "^0.5.4",
Expand Down
13 changes: 7 additions & 6 deletions packages/angular-cli/blueprints/component/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
var path = require('path');
var chalk = require('chalk');
var Blueprint = require('ember-cli/lib/models/blueprint');
var dynamicPathParser = require('../../utilities/dynamic-path-parser');
const path = require('path');
const chalk = require('chalk');
const Blueprint = require('ember-cli/lib/models/blueprint');
const dynamicPathParser = require('../../utilities/dynamic-path-parser');
const findParentModule = require('../../utilities/find-parent-module').default;
var getFiles = Blueprint.prototype.files;
const getFiles = Blueprint.prototype.files;
const stringUtils = require('ember-cli-string-utils');
const astUtils = require('../../utilities/ast-utils');
const NodeHost = require('@angular-cli/ast-tools').NodeHost;

module.exports = {
description: '',
Expand Down Expand Up @@ -117,7 +118,7 @@ module.exports = {
if (!options['skip-import']) {
returns.push(
astUtils.addDeclarationToModule(this.pathToModule, className, importPath)
.then(change => change.apply()));
.then(change => change.apply(NodeHost)));
}

return Promise.all(returns);
Expand Down
3 changes: 2 additions & 1 deletion packages/angular-cli/blueprints/directive/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ var dynamicPathParser = require('../../utilities/dynamic-path-parser');
const stringUtils = require('ember-cli-string-utils');
const astUtils = require('../../utilities/ast-utils');
const findParentModule = require('../../utilities/find-parent-module').default;
const NodeHost = require('@angular-cli/ast-tools').NodeHost;

module.exports = {
description: '',
Expand Down Expand Up @@ -73,7 +74,7 @@ module.exports = {
if (!options['skip-import']) {
returns.push(
astUtils.addDeclarationToModule(this.pathToModule, className, importPath)
.then(change => change.apply()));
.then(change => change.apply(NodeHost)));
}

return Promise.all(returns);
Expand Down
3 changes: 2 additions & 1 deletion packages/angular-cli/blueprints/pipe/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ var dynamicPathParser = require('../../utilities/dynamic-path-parser');
const stringUtils = require('ember-cli-string-utils');
const astUtils = require('../../utilities/ast-utils');
const findParentModule = require('../../utilities/find-parent-module').default;
const NodeHost = require('@angular-cli/ast-tools').NodeHost;

module.exports = {
description: '',
Expand Down Expand Up @@ -61,7 +62,7 @@ module.exports = {
if (!options['skip-import']) {
returns.push(
astUtils.addDeclarationToModule(this.pathToModule, className, importPath)
.then(change => change.apply()));
.then(change => change.apply(NodeHost)));
}

return Promise.all(returns);
Expand Down
2 changes: 2 additions & 0 deletions packages/angular-cli/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface BuildOptions {
watcher?: string;
supressSizes: boolean;
baseHref?: string;
aot?: boolean;
}

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

run: function (commandOptions: BuildOptions) {
Expand Down
4 changes: 3 additions & 1 deletion packages/angular-cli/commands/serve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface ServeTaskOptions {
ssl?: boolean;
sslKey?: string;
sslCert?: string;
aot?: boolean;
}

const ServeCommand = Command.extend({
Expand Down Expand Up @@ -77,7 +78,8 @@ const ServeCommand = Command.extend({
{ name: 'environment', type: String, default: '', aliases: ['e'] },
{ name: 'ssl', type: Boolean, default: false },
{ name: 'ssl-key', type: String, default: 'ssl/server.key' },
{ name: 'ssl-cert', type: String, default: 'ssl/server.crt' }
{ name: 'ssl-cert', type: String, default: 'ssl/server.crt' },
{ name: 'aot', type: Boolean, default: false }
],

run: function(commandOptions: ServeTaskOptions) {
Expand Down
29 changes: 5 additions & 24 deletions packages/angular-cli/models/webpack-build-common.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import * as webpack from 'webpack';
import * as path from 'path';
import {BaseHrefWebpackPlugin} from '@angular-cli/base-href-webpack';

const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
import * as webpack from 'webpack';
const atl = require('awesome-typescript-loader');

import { BaseHrefWebpackPlugin } from '@angular-cli/base-href-webpack';
import { findLazyModules } from './find-lazy-modules';


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

let entry: { [key: string]: string[] } = {
main: [appMain]
Expand Down Expand Up @@ -56,21 +53,7 @@ export function getWebpackCommonConfig(
}
],
loaders: [
{
test: /\.ts$/,
loaders: [
{
loader: 'awesome-typescript-loader',
query: {
useForkChecker: true,
tsconfig: path.resolve(appRoot, appConfig.tsconfig)
}
}, {
loader: 'angular2-template-loader'
}
],
exclude: [/\.(spec|e2e)\.ts$/]
},
// TypeScript loaders are separated into webpack-build-typescript.

// in main, load css as raw text
       {
Expand Down Expand Up @@ -115,16 +98,14 @@ export function getWebpackCommonConfig(

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

{ test: /\.(otf|woff|ttf|svg)$/, loader: 'url?limit=10000' },
{ test: /\.woff2$/, loader: 'url?limit=10000&mimetype=font/woff2' },
{ test: /\.eot$/, loader: 'file' }
]
},
plugins: [
new webpack.ContextReplacementPlugin(/.*/, appRoot, lazyModules),
new atl.ForkCheckerPlugin(),
new HtmlWebpackPlugin({
template: path.resolve(appRoot, appConfig.index),
chunksSortMode: 'dependency'
Expand Down
63 changes: 63 additions & 0 deletions packages/angular-cli/models/webpack-build-typescript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import * as path from 'path';
import * as webpack from 'webpack';
import {findLazyModules} from './find-lazy-modules';
import {NgcWebpackPlugin} from '@ngtools/webpack';

const atl = require('awesome-typescript-loader');

const g: any = global;
const webpackLoader: string = g['angularCliIsLocal']
? g.angularCliPackages['@ngtools/webpack'].main
: '@ngtools/webpack';


export const getWebpackNonAotConfigPartial = function(projectRoot: string, appConfig: any) {
const appRoot = path.resolve(projectRoot, appConfig.root);
const lazyModules = findLazyModules(appRoot);

return {
module: {
loaders: [
{
test: /\.ts$/,
loaders: [{
loader: 'awesome-typescript-loader',
query: {
useForkChecker: true,
tsconfig: path.resolve(appRoot, appConfig.tsconfig)
}
}, {
loader: 'angular2-template-loader'
}],
exclude: [/\.(spec|e2e)\.ts$/]
}
],
},
plugins: [
new webpack.ContextReplacementPlugin(/.*/, appRoot, lazyModules),
new atl.ForkCheckerPlugin(),
]
};
};

export const getWebpackAotConfigPartial = function(projectRoot: string, appConfig: any) {
return {
module: {
loaders: [
{
test: /\.ts$/,
loader: webpackLoader,
exclude: [/\.(spec|e2e)\.ts$/]
}
]
},
plugins: [
new NgcWebpackPlugin({
project: path.resolve(projectRoot, appConfig.root, appConfig.tsconfig),
baseDir: path.resolve(projectRoot, ''),
entryModule: path.join(projectRoot, appConfig.root, 'app/app.module#AppModule'),
genDir: path.join(projectRoot, appConfig.outDir, 'ngfactory')
}),
]
};
};
38 changes: 23 additions & 15 deletions packages/angular-cli/models/webpack-config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import {
getWebpackAotConfigPartial,
getWebpackNonAotConfigPartial
} from './webpack-build-typescript';
const webpackMerge = require('webpack-merge');
import { CliConfig } from './config';
import {
Expand All @@ -12,50 +16,54 @@ export class NgCliWebpackConfig {
// TODO: When webpack2 types are finished lets replace all these any types
// so this is more maintainable in the future for devs
public config: any;
private devConfigPartial: any;
private prodConfigPartial: any;
private baseConfig: any;

constructor(
public ngCliProject: any,
public target: string,
public environment: string,
outputDir?: string,
baseHref?: string
baseHref?: string,
isAoT = false
) {
const config: CliConfig = CliConfig.fromProject();
const appConfig = config.config.apps[0];

appConfig.outDir = outputDir || appConfig.outDir;

this.baseConfig = getWebpackCommonConfig(
let baseConfig = getWebpackCommonConfig(
this.ngCliProject.root,
environment,
appConfig,
baseHref
);
this.devConfigPartial = getWebpackDevConfigPartial(this.ngCliProject.root, appConfig);
this.prodConfigPartial = getWebpackProdConfigPartial(this.ngCliProject.root, appConfig);
let targetConfigPartial = this.getTargetConfig(this.ngCliProject.root, appConfig);
const typescriptConfigPartial = isAoT
? getWebpackAotConfigPartial(this.ngCliProject.root, appConfig)
: getWebpackNonAotConfigPartial(this.ngCliProject.root, appConfig);

if (appConfig.mobile) {
let mobileConfigPartial = getWebpackMobileConfigPartial(this.ngCliProject.root, appConfig);
let mobileProdConfigPartial = getWebpackMobileProdConfigPartial(this.ngCliProject.root,
appConfig);
this.baseConfig = webpackMerge(this.baseConfig, mobileConfigPartial);
this.prodConfigPartial = webpackMerge(this.prodConfigPartial, mobileProdConfigPartial);
baseConfig = webpackMerge(baseConfig, mobileConfigPartial);
if (this.target == 'production') {
targetConfigPartial = webpackMerge(targetConfigPartial, mobileProdConfigPartial);
}
}

this.generateConfig();
this.config = webpackMerge(
baseConfig,
targetConfigPartial,
typescriptConfigPartial
);
}

generateConfig(): void {
getTargetConfig(projectRoot: string, appConfig: any): any {
switch (this.target) {
case 'development':
this.config = webpackMerge(this.baseConfig, this.devConfigPartial);
break;
return getWebpackDevConfigPartial(projectRoot, appConfig);
case 'production':
this.config = webpackMerge(this.baseConfig, this.prodConfigPartial);
break;
return getWebpackProdConfigPartial(projectRoot, appConfig);
default:
throw new Error("Invalid build target. Only 'development' and 'production' are available.");
}
Expand Down
2 changes: 2 additions & 0 deletions packages/angular-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@angular/platform-browser": "^2.0.0",
"@angular/platform-server": "^2.0.0",
"@angular/tsc-wrapped": "^0.3.0",
"@ngtools/webpack": "latest",
"angular2-template-loader": "^0.5.0",
"awesome-typescript-loader": "^2.2.3",
"chalk": "^1.1.3",
Expand All @@ -53,6 +54,7 @@
"fs.realpath": "^1.0.0",
"glob": "^7.0.3",
"handlebars": "^4.0.5",
"html-loader": "^0.4.4",
"html-webpack-plugin": "^2.19.0",
"istanbul-instrumenter-loader": "^0.2.0",
"json-loader": "^0.5.4",
Expand Down
3 changes: 2 additions & 1 deletion packages/angular-cli/tasks/build-webpack-watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export default Task.extend({
runTaskOptions.target,
runTaskOptions.environment,
runTaskOptions.outputPath,
runTaskOptions.baseHref
runTaskOptions.baseHref,
runTaskOptions.aot
).config;
const webpackCompiler: any = webpack(config);

Expand Down
4 changes: 2 additions & 2 deletions packages/angular-cli/tasks/build-webpack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ export default <any>Task.extend({

const outputDir = runTaskOptions.outputPath || CliConfig.fromProject().config.apps[0].outDir;
rimraf.sync(path.resolve(project.root, outputDir));

const config = new NgCliWebpackConfig(
project,
runTaskOptions.target,
runTaskOptions.environment,
outputDir,
runTaskOptions.baseHref
runTaskOptions.baseHref,
runTaskOptions.aot
).config;

// fail on build error
Expand Down
8 changes: 6 additions & 2 deletions packages/angular-cli/tasks/serve-webpack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ export default Task.extend({
let webpackCompiler: any;

let config = new NgCliWebpackConfig(
this.project, commandOptions.target,
commandOptions.environment
this.project,
commandOptions.target,
commandOptions.environment,
undefined,
undefined,
commandOptions.aot
).config;

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

0 comments on commit d296778

Please sign in to comment.