diff --git a/docs/documentation/build.md b/docs/documentation/build.md
index ddd16cfec8e5..a78428af1deb 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.
+
+
+ bundle-dependencies
+
+ --bundle-dependencies
+
+
+ In a server build, state whether `all` or `none` dependencies should be bundles in the output.
+
+
diff --git a/packages/@angular/cli/commands/build.ts b/packages/@angular/cli/commands/build.ts
index 55eda26e789d..b4ab4e7aeca1 100644
--- a/packages/@angular/cli/commands/build.ts
+++ b/packages/@angular/cli/commands/build.ts
@@ -183,6 +183,13 @@ export const baseBuildCommandOptions: any = [
default: false,
aliases: ['sri'],
description: 'Enables the use of subresource integrity validation.'
+ },
+ {
+ name: 'bundle-dependencies',
+ type: ['none', 'all'],
+ default: 'none',
+ description: 'Available on server platform only. Which external dependencies to bundle into '
+ + 'the module. By default, all of node_modules will be kept as requires.'
}
];
diff --git a/packages/@angular/cli/models/build-options.ts b/packages/@angular/cli/models/build-options.ts
index 28cce0341396..6ed4dcafb87a 100644
--- a/packages/@angular/cli/models/build-options.ts
+++ b/packages/@angular/cli/models/build-options.ts
@@ -17,6 +17,7 @@ export interface BuildOptions {
locale?: string;
missingTranslation?: string;
extractCss?: boolean;
+ bundleDependencies?: 'none' | 'all';
watch?: boolean;
outputHashing?: string;
poll?: number;
diff --git a/packages/@angular/cli/models/webpack-config.ts b/packages/@angular/cli/models/webpack-config.ts
index a63654213d94..a1e8e438b1d5 100644
--- a/packages/@angular/cli/models/webpack-config.ts
+++ b/packages/@angular/cli/models/webpack-config.ts
@@ -1,3 +1,4 @@
+import { readTsconfig } from '../utilities/read-tsconfig';
const webpackMerge = require('webpack-merge');
import { CliConfig } from './config';
import { BuildOptions } from './build-options';
@@ -17,6 +18,7 @@ export interface WebpackConfigOptions {
projectRoot: string;
buildOptions: T;
appConfig: any;
+ tsConfig: any;
}
export class NgCliWebpackConfig {
@@ -33,7 +35,10 @@ export class NgCliWebpackConfig {
buildOptions = this.addTargetDefaults(buildOptions);
buildOptions = this.mergeConfigs(buildOptions, appConfig, projectRoot);
- this.wco = { projectRoot, buildOptions, appConfig };
+ const tsconfigPath = path.resolve(projectRoot, appConfig.root, appConfig.tsconfig);
+ const tsConfig = readTsconfig(tsconfigPath);
+
+ this.wco = { projectRoot, buildOptions, appConfig, tsConfig };
}
public buildConfig() {
diff --git a/packages/@angular/cli/models/webpack-configs/browser.ts b/packages/@angular/cli/models/webpack-configs/browser.ts
index bea9cfb97407..579b34991512 100644
--- a/packages/@angular/cli/models/webpack-configs/browser.ts
+++ b/packages/@angular/cli/models/webpack-configs/browser.ts
@@ -1,6 +1,7 @@
import * as fs from 'fs';
import * as webpack from 'webpack';
import * as path from 'path';
+import * as ts from 'typescript';
const HtmlWebpackPlugin = require('html-webpack-plugin');
const SubresourceIntegrityPlugin = require('webpack-subresource-integrity');
@@ -67,7 +68,16 @@ export function getBrowserConfig(wco: WebpackConfigOptions) {
}));
}
+ const supportES2015 = wco.tsConfig.options.target !== ts.ScriptTarget.ES3
+ && wco.tsConfig.options.target !== ts.ScriptTarget.ES5;
+
return {
+ resolve: {
+ mainFields: [
+ ...(supportES2015 ? ['es2015'] : []),
+ 'browser', 'module', 'main'
+ ]
+ },
output: {
crossOriginLoading: buildOptions.subresourceIntegrity ? 'anonymous' : false
},
@@ -91,6 +101,19 @@ export function getBrowserConfig(wco: WebpackConfigOptions) {
minChunks: Infinity,
name: 'inline'
})
- ].concat(extraPlugins)
+ ].concat(extraPlugins),
+ node: {
+ fs: 'empty',
+ // `global` should be kept true, removing it resulted in a
+ // massive size increase with Build Optimizer on AIO.
+ global: true,
+ crypto: 'empty',
+ tls: 'empty',
+ net: 'empty',
+ process: true,
+ module: false,
+ clearImmediate: false,
+ setImmediate: false
+ }
};
}
diff --git a/packages/@angular/cli/models/webpack-configs/common.ts b/packages/@angular/cli/models/webpack-configs/common.ts
index 732427a97deb..3acc15d01de3 100644
--- a/packages/@angular/cli/models/webpack-configs/common.ts
+++ b/packages/@angular/cli/models/webpack-configs/common.ts
@@ -1,13 +1,11 @@
import * as webpack from 'webpack';
import * as path from 'path';
import * as CopyWebpackPlugin from 'copy-webpack-plugin';
-import * as ts from 'typescript';
import { NamedLazyChunksWebpackPlugin } from '../../plugins/named-lazy-chunks-webpack-plugin';
import { InsertConcatAssetsWebpackPlugin } from '../../plugins/insert-concat-assets-webpack-plugin';
import { extraEntryParser, getOutputHashFormat, AssetPattern } from './utils';
import { isDirectory } from '../../utilities/is-directory';
import { WebpackConfigOptions } from '../webpack-config';
-import { readTsconfig } from '../../utilities/read-tsconfig';
const ConcatPlugin = require('webpack-concat-plugin');
const ProgressPlugin = require('webpack/lib/ProgressPlugin');
@@ -160,19 +158,11 @@ export function getCommonConfig(wco: WebpackConfigOptions) {
}
// Read the tsconfig to determine if we should prefer ES2015 modules.
- const tsconfigPath = path.resolve(projectRoot, appConfig.root, appConfig.tsconfig);
- const tsConfig = readTsconfig(tsconfigPath);
- const supportES2015 = tsConfig.options.target !== ts.ScriptTarget.ES3
- && tsConfig.options.target !== ts.ScriptTarget.ES5;
return {
resolve: {
extensions: ['.ts', '.js'],
modules: ['node_modules', nodeModules],
- mainFields: [
- ...(supportES2015 ? ['es2015'] : []),
- 'browser', 'module', 'main'
- ],
symlinks: !buildOptions.preserveSymlinks
},
resolveLoader: {
@@ -201,19 +191,6 @@ export function getCommonConfig(wco: WebpackConfigOptions) {
},
plugins: [
new webpack.NoEmitOnErrorsPlugin()
- ].concat(extraPlugins),
- node: {
- fs: 'empty',
- // `global` should be kept true, removing it resulted in a
- // massive size increase with Build Optimizer on AIO.
- global: true,
- crypto: 'empty',
- tls: 'empty',
- net: 'empty',
- process: true,
- module: false,
- clearImmediate: false,
- setImmediate: false
- }
+ ].concat(extraPlugins)
};
}
diff --git a/packages/@angular/cli/models/webpack-configs/server.ts b/packages/@angular/cli/models/webpack-configs/server.ts
index c3e5fb465587..48a40281ffe1 100644
--- a/packages/@angular/cli/models/webpack-configs/server.ts
+++ b/packages/@angular/cli/models/webpack-configs/server.ts
@@ -1,18 +1,32 @@
import { WebpackConfigOptions } from '../webpack-config';
+import * as ts from 'typescript';
/**
* Returns a partial specific to creating a bundle for node
- * @param _wco Options which are include the build options and app config
+ * @param wco Options which are include the build options and app config
*/
-export function getServerConfig(_wco: WebpackConfigOptions) {
- return {
+export function getServerConfig(wco: WebpackConfigOptions) {
+ const supportES2015 = wco.tsConfig.options.target !== ts.ScriptTarget.ES3
+ && wco.tsConfig.options.target !== ts.ScriptTarget.ES5;
+
+ const config: any = {
+ resolve: {
+ mainFields: [
+ ...(supportES2015 ? ['es2015'] : []),
+ 'main', 'module',
+ ],
+ },
target: 'node',
output: {
libraryTarget: 'commonjs'
},
- externals: [
+ node: false,
+ };
+
+ if (wco.buildOptions.bundleDependencies == 'none') {
+ config.externals = [
/^@angular/,
- function (_: any, request: any, callback: (error?: any, result?: any) => void) {
+ (_: any, request: any, callback: (error?: any, result?: any) => void) => {
// Absolute & Relative paths are not externals
if (request.match(/^\.{0,2}\//)) {
return callback();
@@ -33,6 +47,8 @@ export function getServerConfig(_wco: WebpackConfigOptions) {
callback();
}
}
- ]
- };
+ ];
+ }
+
+ return config;
}
diff --git a/tests/e2e/tests/build/platform-server.ts b/tests/e2e/tests/build/platform-server.ts
index e040242fd760..210d0a41832e 100644
--- a/tests/e2e/tests/build/platform-server.ts
+++ b/tests/e2e/tests/build/platform-server.ts
@@ -1,9 +1,16 @@
import { normalize } from 'path';
import { updateJsonFile, updateTsConfig } from '../../utils/project';
-import { expectFileToMatch, writeFile, replaceInFile, prependToFile } from '../../utils/fs';
+import {
+ expectFileToMatch,
+ writeFile,
+ replaceInFile,
+ prependToFile,
+ appendToFile,
+} from '../../utils/fs';
import { ng, silentNpm, exec } from '../../utils/process';
import { getGlobalVariable } from '../../utils/env';
+import { expectToFail } from '../../utils/utils';
export default function () {
// Skip this in Appveyor tests.
@@ -34,6 +41,7 @@ export default function () {
dependencies['@angular/platform-server'] = platformServerVersion;
}))
.then(() => updateTsConfig(tsConfig => {
+ tsConfig.compilerOptions.types = ['node'];
tsConfig['angularCompilerOptions'] = {
entryModule: 'app/app.module#AppModule'
};
@@ -73,5 +81,28 @@ export default function () {
.then(() => replaceInFile('./index.js', /renderModule/g, 'renderModuleFactory'))
.then(() => exec(normalize('node'), 'index.js'))
.then(() => expectFileToMatch('dist/index.html',
- new RegExp('Here are some links to help you start:
')));
+ new RegExp('Here are some links to help you start:
')))
+ .then(() => expectFileToMatch('./dist/main.bundle.js',
+ /require\(["']@angular\/[^"']*["']\)/))
+
+ // Check externals.
+ .then(() => prependToFile('./src/app/app.module.ts', `
+ import 'zone.js/dist/zone-node';
+ import 'reflect-metadata';
+ `)
+ .then(() => appendToFile('./src/app/app.module.ts', `
+ import * as fs from 'fs';
+ import { renderModule } from '@angular/platform-server';
+
+ renderModule(AppModule, \{
+ url: '/',
+ document: ''
+ \}).then(html => \{
+ fs.writeFileSync('dist/index.html', html);
+ \});
+ `)))
+ .then(() => ng('build', '--bundle-dependencies=all'))
+ .then(() => expectToFail(() => expectFileToMatch('./dist/main.bundle.js',
+ /require\(["']@angular\/[^"']*["']\)/)))
+ .then(() => exec(normalize('node'), 'dist/main.bundle.js'));
}