diff --git a/docs/documentation/build.md b/docs/documentation/build.md
index 09b453aeb8f3..7da7e30e7ae5 100644
--- a/docs/documentation/build.md
+++ b/docs/documentation/build.md
@@ -366,3 +366,13 @@ Note: service worker support is experimental and subject to change.
Use file name for lazy loaded chunks.
+
+
+ compilation-target
+
+ --compilation-target
(aliases: -ct, -es5, -es2015
) default value: ES5
+
+
+ Defines the compilation target (ES5 or ES2015).
+
+
diff --git a/package-lock.json b/package-lock.json
index 57bff5d23332..604cff1a2f8d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -94,6 +94,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/copy-webpack-plugin/-/copy-webpack-plugin-4.0.0.tgz",
"integrity": "sha1-v9qPXtOIzwnEJqvlER/Mt/PyXw0=",
+ "dev": true,
"requires": {
"@types/minimatch": "3.0.0",
"@types/webpack": "3.0.9"
@@ -173,7 +174,8 @@
"@types/minimatch": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.0.tgz",
- "integrity": "sha512-BnRgLPs1oy9gV8b4dAW8jilIa7Kpe3uNucAvv4RkY7Yt6rj9E6Kk4Qa/18LOSinA+XDBPdzx3MqYPEP5mw8rDA=="
+ "integrity": "sha512-BnRgLPs1oy9gV8b4dAW8jilIa7Kpe3uNucAvv4RkY7Yt6rj9E6Kk4Qa/18LOSinA+XDBPdzx3MqYPEP5mw8rDA==",
+ "dev": true
},
"@types/minimist": {
"version": "1.2.0",
@@ -193,7 +195,8 @@
"@types/node": {
"version": "6.0.87",
"resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.87.tgz",
- "integrity": "sha512-Xo0pYENOBaGtJUhi50KH6gdBNQmZQQxAwBArsJpBd15ncoz+LZD5Ev14vuezcw62CsQ1q6bM++7jA6jfwaAbfQ=="
+ "integrity": "sha512-Xo0pYENOBaGtJUhi50KH6gdBNQmZQQxAwBArsJpBd15ncoz+LZD5Ev14vuezcw62CsQ1q6bM++7jA6jfwaAbfQ==",
+ "dev": true
},
"@types/request": {
"version": "2.0.1",
@@ -224,17 +227,20 @@
"@types/source-map": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/@types/source-map/-/source-map-0.5.1.tgz",
- "integrity": "sha512-/GVAjL1Y8puvZab63n8tsuBiYwZt1bApMdx58/msQ9ID5T05ov+wm/ZV1DvYC/DKKEygpTJViqQvkh5Rhrl4CA=="
+ "integrity": "sha512-/GVAjL1Y8puvZab63n8tsuBiYwZt1bApMdx58/msQ9ID5T05ov+wm/ZV1DvYC/DKKEygpTJViqQvkh5Rhrl4CA==",
+ "dev": true
},
"@types/tapable": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-0.2.3.tgz",
- "integrity": "sha1-CIiw8gzH5Y4cIqGIi06WPu+qgQo="
+ "integrity": "sha1-CIiw8gzH5Y4cIqGIi06WPu+qgQo=",
+ "dev": true
},
"@types/uglify-js": {
"version": "2.6.29",
"resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-2.6.29.tgz",
"integrity": "sha512-BdFLCZW0GTl31AbqXSak8ss/MqEZ3DN2MH9rkAyGoTuzK7ifGUlX+u0nfbWeTsa7IPcZhtn8BlpYBXSV+vqGhQ==",
+ "dev": true,
"requires": {
"@types/source-map": "0.5.1"
}
@@ -243,6 +249,7 @@
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-3.0.9.tgz",
"integrity": "sha512-xXqusBBKbYb8fA1jtE3iO75uRW1ejqGuH93V+6fhbfNY59ndKjfhftJVxcSaYAMDjmFTRBHy82d+513JKuHD5g==",
+ "dev": true,
"requires": {
"@types/node": "6.0.87",
"@types/tapable": "0.2.3",
@@ -7496,54 +7503,22 @@
"optional": true
},
"uglifyjs-webpack-plugin": {
- "version": "0.4.6",
- "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz",
- "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=",
+ "version": "1.0.0-beta.1",
+ "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.0.0-beta.1.tgz",
+ "integrity": "sha1-WVwU2Lhb7dcIq3pugRiU++DmmJA=",
"requires": {
"source-map": "0.5.6",
- "uglify-js": "2.8.29",
+ "uglify-es": "3.0.28",
"webpack-sources": "1.0.1"
},
"dependencies": {
- "camelcase": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
- "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk="
- },
- "cliui": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
- "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
- "requires": {
- "center-align": "0.1.3",
- "right-align": "0.1.3",
- "wordwrap": "0.0.2"
- }
- },
- "uglify-js": {
- "version": "2.8.29",
- "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
- "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
+ "uglify-es": {
+ "version": "3.0.28",
+ "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.0.28.tgz",
+ "integrity": "sha512-xw1hJsSp361OO0Sq0XvNyTI2wfQ4eKNljfSYyeYX/dz9lKEDj+DK+A8CzB0NmoCwWX1MnEx9f16HlkKXyG65CQ==",
"requires": {
- "source-map": "0.5.6",
- "uglify-to-browserify": "1.0.2",
- "yargs": "3.10.0"
- }
- },
- "wordwrap": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
- "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8="
- },
- "yargs": {
- "version": "3.10.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
- "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
- "requires": {
- "camelcase": "1.2.1",
- "cliui": "2.1.0",
- "decamelize": "1.2.0",
- "window-size": "0.1.0"
+ "commander": "2.11.0",
+ "source-map": "0.5.6"
}
}
}
@@ -7868,11 +7843,64 @@
"has-flag": "2.0.0"
}
},
+ "uglify-js": {
+ "version": "2.8.29",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
+ "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
+ "requires": {
+ "source-map": "0.5.6",
+ "uglify-to-browserify": "1.0.2",
+ "yargs": "3.10.0"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
+ "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk="
+ },
+ "cliui": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
+ "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
+ "requires": {
+ "center-align": "0.1.3",
+ "right-align": "0.1.3",
+ "wordwrap": "0.0.2"
+ }
+ },
+ "yargs": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
+ "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
+ "requires": {
+ "camelcase": "1.2.1",
+ "cliui": "2.1.0",
+ "decamelize": "1.2.0",
+ "window-size": "0.1.0"
+ }
+ }
+ }
+ },
+ "uglifyjs-webpack-plugin": {
+ "version": "0.4.6",
+ "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz",
+ "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=",
+ "requires": {
+ "source-map": "0.5.6",
+ "uglify-js": "2.8.29",
+ "webpack-sources": "1.0.1"
+ }
+ },
"which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
},
+ "wordwrap": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
+ "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8="
+ },
"yargs": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz",
diff --git a/package.json b/package.json
index 9f8fa594217c..99c0332bef82 100644
--- a/package.json
+++ b/package.json
@@ -92,6 +92,7 @@
"stylus": "^0.54.5",
"stylus-loader": "^3.0.1",
"typescript": "~2.4.2",
+ "uglifyjs-webpack-plugin": "1.0.0-beta.1",
"url-loader": "^0.5.7",
"webpack": "~3.5.5",
"webpack-concat-plugin": "1.4.0",
diff --git a/packages/@angular/cli/commands/build.ts b/packages/@angular/cli/commands/build.ts
index 96fcb7ceae2c..b249ba9f2821 100644
--- a/packages/@angular/cli/commands/build.ts
+++ b/packages/@angular/cli/commands/build.ts
@@ -9,7 +9,7 @@ const Command = require('../ember-cli/lib/models/command');
const config = CliConfig.fromProject() || CliConfig.fromGlobal();
const buildConfigDefaults = config.getPaths('defaults.build', [
'sourcemaps', 'baseHref', 'progress', 'poll', 'deleteOutputPath', 'preserveSymlinks',
- 'showCircularDependencies', 'commonChunk', 'namedChunks'
+ 'showCircularDependencies', 'commonChunk', 'namedChunks', 'compilationTarget'
]);
// defaults for BuildOptions
@@ -176,6 +176,18 @@ export const baseBuildCommandOptions: any = [
aliases: ['nc'],
description: 'Use file name for lazy loaded chunks.',
default: buildConfigDefaults['namedChunks']
+ },
+ {
+ name: 'compilation-target',
+ type: String,
+ default: buildConfigDefaults['compilationTarget'],
+ aliases: [
+ 'ct',
+ { 'ES5': 'ES5' }, { 'ES2015': 'ES2015' },
+ // Support lowercase in the alias.
+ { 'es5': 'ES5' }, { 'es2015': 'ES2015' },
+ ],
+ description: 'Defines the compilation target (ES5 or ES2015).'
}
];
diff --git a/packages/@angular/cli/lib/config/schema.json b/packages/@angular/cli/lib/config/schema.json
index 9a890308dbab..6ebbfda4d53d 100644
--- a/packages/@angular/cli/lib/config/schema.json
+++ b/packages/@angular/cli/lib/config/schema.json
@@ -496,6 +496,11 @@
"namedChunks": {
"description": "Use file name for lazy loaded chunks.",
"type": "boolean"
+ },
+ "compilationTarget": {
+ "description": "Defines the compilation target (ES5 or ES2015).",
+ "type": "string",
+ "default": "ES5"
}
}
},
diff --git a/packages/@angular/cli/models/build-options.ts b/packages/@angular/cli/models/build-options.ts
index 3bc2706a10ce..b57cace5665a 100644
--- a/packages/@angular/cli/models/build-options.ts
+++ b/packages/@angular/cli/models/build-options.ts
@@ -25,4 +25,5 @@ export interface BuildOptions {
showCircularDependencies?: boolean;
buildOptimizer?: boolean;
namedChunks?: boolean;
+ compilationTarget?: 'ES5' | 'ES2015';
}
diff --git a/packages/@angular/cli/models/webpack-config.ts b/packages/@angular/cli/models/webpack-config.ts
index 35ed6d50042c..f4bde668b4ab 100644
--- a/packages/@angular/cli/models/webpack-config.ts
+++ b/packages/@angular/cli/models/webpack-config.ts
@@ -74,6 +74,13 @@ export class NgCliWebpackConfig {
throw new Error("Invalid build target. Only 'development' and 'production' are available.");
}
+ buildOptions.compilationTarget = buildOptions.compilationTarget || 'ES5';
+ buildOptions.compilationTarget =
+ buildOptions.compilationTarget.toUpperCase() as 'ES5' | 'ES2015';
+ if (buildOptions.compilationTarget !== 'ES5' && buildOptions.compilationTarget !== 'ES2015') {
+ throw new Error("Invalid compilation target. Only 'ES5' and 'ES2015' are available.");
+ }
+
if (buildOptions.buildOptimizer
&& !(buildOptions.aot || buildOptions.target === 'production')) {
throw new Error('The `--build-optimizer` option cannot be used without `--aot`.');
diff --git a/packages/@angular/cli/models/webpack-configs/common.ts b/packages/@angular/cli/models/webpack-configs/common.ts
index 82afcaab7cff..1194a499a640 100644
--- a/packages/@angular/cli/models/webpack-configs/common.ts
+++ b/packages/@angular/cli/models/webpack-configs/common.ts
@@ -153,6 +153,10 @@ export function getCommonConfig(wco: WebpackConfigOptions) {
resolve: {
extensions: ['.ts', '.js'],
modules: ['node_modules', nodeModules],
+ mainFields: [
+ ...(buildOptions.compilationTarget === 'ES2015' ? ['es2015'] : []),
+ 'browser', 'module', 'main'
+ ],
symlinks: !buildOptions.preserveSymlinks
},
resolveLoader: {
diff --git a/packages/@angular/cli/models/webpack-configs/production.ts b/packages/@angular/cli/models/webpack-configs/production.ts
index dc81100d2085..9aa27e85b02c 100644
--- a/packages/@angular/cli/models/webpack-configs/production.ts
+++ b/packages/@angular/cli/models/webpack-configs/production.ts
@@ -9,12 +9,14 @@ import { StaticAssetPlugin } from '../../plugins/static-asset';
import { GlobCopyWebpackPlugin } from '../../plugins/glob-copy-webpack-plugin';
import { WebpackConfigOptions } from '../webpack-config';
+const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
+
export const getProdConfig = function (wco: WebpackConfigOptions) {
const { projectRoot, buildOptions, appConfig } = wco;
let extraPlugins: any[] = [];
- let entryPoints: {[key: string]: string[]} = {};
+ let entryPoints: { [key: string]: string[] } = {};
if (appConfig.serviceWorker) {
const nodeModules = path.resolve(projectRoot, 'node_modules');
@@ -66,7 +68,7 @@ export const getProdConfig = function (wco: WebpackConfigOptions) {
extraPlugins.push(new GlobCopyWebpackPlugin({
patterns: [
'ngsw-manifest.json',
- {glob: 'ngsw-manifest.json', input: path.resolve(projectRoot, appConfig.root), output: ''}
+ { glob: 'ngsw-manifest.json', input: path.resolve(projectRoot, appConfig.root), output: '' }
],
globOptions: {
cwd: projectRoot,
@@ -99,7 +101,7 @@ export const getProdConfig = function (wco: WebpackConfigOptions) {
}));
}
- const uglifyCompressOptions: any = { screw_ie8: true, warnings: buildOptions.verbose };
+ const uglifyCompressOptions: any = {};
if (buildOptions.buildOptimizer) {
// This plugin must be before webpack.optimize.UglifyJsPlugin.
@@ -110,21 +112,43 @@ export const getProdConfig = function (wco: WebpackConfigOptions) {
uglifyCompressOptions.passes = 3;
}
+ if (buildOptions.compilationTarget === 'ES2015') {
+ extraPlugins.push(new UglifyJSPlugin({
+ sourceMap: buildOptions.sourcemaps,
+ uglifyOptions: {
+ ecma: 6,
+ warnings: buildOptions.verbose,
+ ie8: false,
+ mangle: true,
+ compress: uglifyCompressOptions,
+ output: {
+ ascii_only: true,
+ comments: false
+ },
+ }
+ }));
+ } else {
+ uglifyCompressOptions.screw_ie8 = true;
+ uglifyCompressOptions.warnings = buildOptions.verbose;
+ extraPlugins.push(new webpack.optimize.UglifyJsPlugin({
+ mangle: { screw_ie8: true },
+ compress: uglifyCompressOptions,
+ output: { ascii_only: true },
+ sourceMap: buildOptions.sourcemaps,
+ comments: false
+ }));
+
+ }
+
return {
entry: entryPoints,
- plugins: extraPlugins.concat([
+ plugins: [
new webpack.EnvironmentPlugin({
'NODE_ENV': 'production'
}),
new webpack.HashedModuleIdsPlugin(),
new webpack.optimize.ModuleConcatenationPlugin(),
- new webpack.optimize.UglifyJsPlugin({
- mangle: { screw_ie8: true },
- compress: uglifyCompressOptions,
- output: { ascii_only: true },
- sourceMap: buildOptions.sourcemaps,
- comments: false
- })
- ])
+ ...extraPlugins
+ ]
};
};
diff --git a/packages/@angular/cli/models/webpack-configs/typescript.ts b/packages/@angular/cli/models/webpack-configs/typescript.ts
index 348dc6369805..01c7a53de74c 100644
--- a/packages/@angular/cli/models/webpack-configs/typescript.ts
+++ b/packages/@angular/cli/models/webpack-configs/typescript.ts
@@ -63,6 +63,9 @@ function _createAotPlugin(wco: WebpackConfigOptions, options: any) {
};
}
+ options.compilerOptions = options.compilerOptions || {};
+ options.compilerOptions.target = buildOptions.compilationTarget;
+
return new AotPlugin(Object.assign({}, {
mainPath: path.join(projectRoot, appConfig.root, appConfig.main),
i18nFile: buildOptions.i18nFile,
diff --git a/packages/@angular/cli/package.json b/packages/@angular/cli/package.json
index fdac6d70b8aa..8e755aebac95 100644
--- a/packages/@angular/cli/package.json
+++ b/packages/@angular/cli/package.json
@@ -76,6 +76,7 @@
"stylus": "^0.54.5",
"stylus-loader": "^3.0.1",
"typescript": ">=2.0.0 <2.6.0",
+ "uglifyjs-webpack-plugin": "1.0.0-beta.1",
"url-loader": "^0.5.7",
"webpack": "~3.5.5",
"webpack-concat-plugin": "1.4.0",
diff --git a/packages/@angular/cli/tasks/eject.ts b/packages/@angular/cli/tasks/eject.ts
index 242404503843..a910d961a139 100644
--- a/packages/@angular/cli/tasks/eject.ts
+++ b/packages/@angular/cli/tasks/eject.ts
@@ -24,6 +24,7 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
const SilentError = require('silent-error');
const CircularDependencyPlugin = require('circular-dependency-plugin');
const ConcatPlugin = require('webpack-concat-plugin');
+const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const Task = require('../ember-cli/lib/models/task');
const ProgressPlugin = require('webpack/lib/ProgressPlugin');
@@ -158,6 +159,10 @@ class JsonWebpackSerializer {
return plugin.settings;
}
+ private _uglifyjsPlugin(plugin: any) {
+ return plugin.options;
+ }
+
private _pluginsReplacer(plugins: any[]) {
return plugins.map(plugin => {
let args = plugin.options || undefined;
@@ -229,6 +234,10 @@ class JsonWebpackSerializer {
args = this._concatPlugin(plugin);
this.variableImports['webpack-concat-plugin'] = 'ConcatPlugin';
break;
+ case UglifyJSPlugin:
+ args = this._uglifyjsPlugin(plugin);
+ this.variableImports['uglifyjs-webpack-plugin'] = 'UglifyJsPlugin';
+ break;
default:
if (plugin.constructor.name == 'AngularServiceWorkerPlugin') {
this._addImport('@angular/service-worker/build/webpack', plugin.constructor.name);
@@ -546,6 +555,7 @@ export default Task.extend({
'circular-dependency-plugin',
'webpack-concat-plugin',
'copy-webpack-plugin',
+ 'uglifyjs-webpack-plugin',
].forEach((packageName: string) => {
packageJson['devDependencies'][packageName] = ourPackageJson['dependencies'][packageName];
});
diff --git a/tests/e2e/tests/build/compilation-target.ts b/tests/e2e/tests/build/compilation-target.ts
new file mode 100644
index 000000000000..9b6313e4cc36
--- /dev/null
+++ b/tests/e2e/tests/build/compilation-target.ts
@@ -0,0 +1,18 @@
+import { appendToFile, expectFileToMatch } from '../../utils/fs';
+import { ng } from '../../utils/process';
+import { expectToFail } from '../../utils/utils';
+
+
+export default function () {
+ return Promise.resolve()
+ .then(() => ng('build', '--prod', '--es2015'))
+ // Check class constructors are present in the vendor output.
+ .then(() => expectFileToMatch('dist/vendor.bundle.js', /class \w{constructor\(\){/))
+ // Force import a known ES6 module and build with prod and es6.
+ // ES6 modules will cause UglifyJS to fail on a ES5 compilation target.
+ .then(() => appendToFile('src/main.ts', `
+ import * as es6module from '@angular/core/@angular/core';
+ console.log(es6module);
+ `))
+ .then(() => expectToFail(() => ng('build', '--prod', '--es5')));
+}