Skip to content

Commit 51fdccd

Browse files
committed
feat(@angular/cli): add a bundle dependencies flag
This flag allows people who know what theyre doing to bundle the server build together with all dependencies. It should only be used when the whole rendering process is part of the main.ts or one of its dependencies. Fixes angular#7903.
1 parent b6dfa8d commit 51fdccd

File tree

8 files changed

+105
-35
lines changed

8 files changed

+105
-35
lines changed

docs/documentation/build.md

+10
Original file line numberDiff line numberDiff line change
@@ -366,3 +366,13 @@ Note: service worker support is experimental and subject to change.
366366
Use file name for lazy loaded chunks.
367367
</p>
368368
</details>
369+
370+
<details>
371+
<summary>bundle-dependencies</summary>
372+
<p>
373+
<code>--bundle-dependencies</code>
374+
</p>
375+
<p>
376+
In a server build, state whether `all` or `none` dependencies should be bundles in the output.
377+
</p>
378+
</details>

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

+7
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,13 @@ export const baseBuildCommandOptions: any = [
183183
default: false,
184184
aliases: ['sri'],
185185
description: 'Enables the use of subresource integrity validation.'
186+
},
187+
{
188+
name: 'bundle-dependencies',
189+
type: ['none', 'all'],
190+
default: 'none',
191+
description: 'Which external dependencies to bundle into the module. By default, '
192+
+ 'all of node_modules will be kept as requires.'
186193
}
187194
];
188195

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

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export interface BuildOptions {
1717
locale?: string;
1818
missingTranslation?: string;
1919
extractCss?: boolean;
20+
bundleDependencies?: 'none' | 'all';
2021
watch?: boolean;
2122
outputHashing?: string;
2223
poll?: number;

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

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { readTsconfig } from '../utilities/read-tsconfig';
12
const webpackMerge = require('webpack-merge');
23
import { CliConfig } from './config';
34
import { BuildOptions } from './build-options';
@@ -17,6 +18,7 @@ export interface WebpackConfigOptions<T extends BuildOptions = BuildOptions> {
1718
projectRoot: string;
1819
buildOptions: T;
1920
appConfig: any;
21+
tsConfig: any;
2022
}
2123

2224
export class NgCliWebpackConfig<T extends BuildOptions = BuildOptions> {
@@ -33,7 +35,10 @@ export class NgCliWebpackConfig<T extends BuildOptions = BuildOptions> {
3335
buildOptions = this.addTargetDefaults(buildOptions);
3436
buildOptions = this.mergeConfigs(buildOptions, appConfig, projectRoot);
3537

36-
this.wco = { projectRoot, buildOptions, appConfig };
38+
const tsconfigPath = path.resolve(projectRoot, appConfig.root, appConfig.tsconfig);
39+
const tsConfig = readTsconfig(tsconfigPath);
40+
41+
this.wco = { projectRoot, buildOptions, appConfig, tsConfig };
3742
}
3843

3944
public buildConfig() {

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

+24-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as fs from 'fs';
22
import * as webpack from 'webpack';
33
import * as path from 'path';
4+
import * as ts from 'typescript';
45
const HtmlWebpackPlugin = require('html-webpack-plugin');
56
const SubresourceIntegrityPlugin = require('webpack-subresource-integrity');
67

@@ -67,7 +68,16 @@ export function getBrowserConfig(wco: WebpackConfigOptions) {
6768
}));
6869
}
6970

71+
const supportES2015 = wco.tsConfig.options.target !== ts.ScriptTarget.ES3
72+
&& wco.tsConfig.options.target !== ts.ScriptTarget.ES5;
73+
7074
return {
75+
resolve: {
76+
mainFields: [
77+
...(supportES2015 ? ['es2015'] : []),
78+
'browser', 'module', 'main'
79+
]
80+
},
7181
output: {
7282
crossOriginLoading: buildOptions.subresourceIntegrity ? 'anonymous' : false
7383
},
@@ -91,6 +101,19 @@ export function getBrowserConfig(wco: WebpackConfigOptions) {
91101
minChunks: Infinity,
92102
name: 'inline'
93103
})
94-
].concat(extraPlugins)
104+
].concat(extraPlugins),
105+
node: {
106+
fs: 'empty',
107+
// `global` should be kept true, removing it resulted in a
108+
// massive size increase with Build Optimizer on AIO.
109+
global: true,
110+
crypto: 'empty',
111+
tls: 'empty',
112+
net: 'empty',
113+
process: true,
114+
module: false,
115+
clearImmediate: false,
116+
setImmediate: false
117+
}
95118
};
96119
}

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

+1-24
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import * as webpack from 'webpack';
22
import * as path from 'path';
33
import * as CopyWebpackPlugin from 'copy-webpack-plugin';
4-
import * as ts from 'typescript';
54
import { NamedLazyChunksWebpackPlugin } from '../../plugins/named-lazy-chunks-webpack-plugin';
65
import { InsertConcatAssetsWebpackPlugin } from '../../plugins/insert-concat-assets-webpack-plugin';
76
import { extraEntryParser, getOutputHashFormat, AssetPattern } from './utils';
87
import { isDirectory } from '../../utilities/is-directory';
98
import { WebpackConfigOptions } from '../webpack-config';
10-
import { readTsconfig } from '../../utilities/read-tsconfig';
119

1210
const ConcatPlugin = require('webpack-concat-plugin');
1311
const ProgressPlugin = require('webpack/lib/ProgressPlugin');
@@ -160,19 +158,11 @@ export function getCommonConfig(wco: WebpackConfigOptions) {
160158
}
161159

162160
// Read the tsconfig to determine if we should prefer ES2015 modules.
163-
const tsconfigPath = path.resolve(projectRoot, appConfig.root, appConfig.tsconfig);
164-
const tsConfig = readTsconfig(tsconfigPath);
165-
const supportES2015 = tsConfig.options.target !== ts.ScriptTarget.ES3
166-
&& tsConfig.options.target !== ts.ScriptTarget.ES5;
167161

168162
return {
169163
resolve: {
170164
extensions: ['.ts', '.js'],
171165
modules: ['node_modules', nodeModules],
172-
mainFields: [
173-
...(supportES2015 ? ['es2015'] : []),
174-
'browser', 'module', 'main'
175-
],
176166
symlinks: !buildOptions.preserveSymlinks
177167
},
178168
resolveLoader: {
@@ -201,19 +191,6 @@ export function getCommonConfig(wco: WebpackConfigOptions) {
201191
},
202192
plugins: [
203193
new webpack.NoEmitOnErrorsPlugin()
204-
].concat(extraPlugins),
205-
node: {
206-
fs: 'empty',
207-
// `global` should be kept true, removing it resulted in a
208-
// massive size increase with Build Optimizer on AIO.
209-
global: true,
210-
crypto: 'empty',
211-
tls: 'empty',
212-
net: 'empty',
213-
process: true,
214-
module: false,
215-
clearImmediate: false,
216-
setImmediate: false
217-
}
194+
].concat(extraPlugins)
218195
};
219196
}

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

+23-7
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,32 @@
11
import { WebpackConfigOptions } from '../webpack-config';
2+
import * as ts from 'typescript';
23

34
/**
45
* Returns a partial specific to creating a bundle for node
5-
* @param _wco Options which are include the build options and app config
6+
* @param wco Options which are include the build options and app config
67
*/
7-
export function getServerConfig(_wco: WebpackConfigOptions) {
8-
return {
8+
export function getServerConfig(wco: WebpackConfigOptions) {
9+
const supportES2015 = wco.tsConfig.options.target !== ts.ScriptTarget.ES3
10+
&& wco.tsConfig.options.target !== ts.ScriptTarget.ES5;
11+
12+
const config: any = {
13+
resolve: {
14+
mainFields: [
15+
...(supportES2015 ? ['es2015'] : []),
16+
'main', 'module',
17+
],
18+
},
919
target: 'node',
1020
output: {
1121
libraryTarget: 'commonjs'
1222
},
13-
externals: [
23+
node: false,
24+
};
25+
26+
if (wco.buildOptions.bundleDependencies == 'none') {
27+
config.externals = [
1428
/^@angular/,
15-
function (_: any, request: any, callback: (error?: any, result?: any) => void) {
29+
(_: any, request: any, callback: (error?: any, result?: any) => void) => {
1630
// Absolute & Relative paths are not externals
1731
if (request.match(/^\.{0,2}\//)) {
1832
return callback();
@@ -33,6 +47,8 @@ export function getServerConfig(_wco: WebpackConfigOptions) {
3347
callback();
3448
}
3549
}
36-
]
37-
};
50+
];
51+
}
52+
53+
return config;
3854
}

tests/e2e/tests/build/platform-server.ts

+33-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
import { normalize } from 'path';
22

33
import { updateJsonFile, updateTsConfig } from '../../utils/project';
4-
import { expectFileToMatch, writeFile, replaceInFile, prependToFile } from '../../utils/fs';
4+
import {
5+
expectFileToMatch,
6+
writeFile,
7+
replaceInFile,
8+
prependToFile,
9+
appendToFile,
10+
} from '../../utils/fs';
511
import { ng, silentNpm, exec } from '../../utils/process';
612
import { getGlobalVariable } from '../../utils/env';
13+
import { expectToFail } from '../../utils/utils';
714

815
export default function () {
916
// Skip this in Appveyor tests.
@@ -34,6 +41,7 @@ export default function () {
3441
dependencies['@angular/platform-server'] = platformServerVersion;
3542
}))
3643
.then(() => updateTsConfig(tsConfig => {
44+
tsConfig.compilerOptions.types = ['node'];
3745
tsConfig['angularCompilerOptions'] = {
3846
entryModule: 'app/app.module#AppModule'
3947
};
@@ -73,5 +81,28 @@ export default function () {
7381
.then(() => replaceInFile('./index.js', /renderModule/g, 'renderModuleFactory'))
7482
.then(() => exec(normalize('node'), 'index.js'))
7583
.then(() => expectFileToMatch('dist/index.html',
76-
new RegExp('<h2 _ngcontent-c0="">Here are some links to help you start: </h2>')));
84+
new RegExp('<h2 _ngcontent-c0="">Here are some links to help you start: </h2>')))
85+
.then(() => expectFileToMatch('./dist/main.bundle.js',
86+
/require\(["']@angular\/[^"']*["']\)/))
87+
88+
// Check externals.
89+
.then(() => prependToFile('./src/app/app.module.ts', `
90+
import 'zone.js/dist/zone-node';
91+
import 'reflect-metadata';
92+
`)
93+
.then(() => appendToFile('./src/app/app.module.ts', `
94+
import * as fs from 'fs';
95+
import { renderModule } from '@angular/platform-server';
96+
97+
renderModule(AppModule, \{
98+
url: '/',
99+
document: '<app-root></app-root>'
100+
\}).then(html => \{
101+
fs.writeFileSync('dist/index.html', html);
102+
\});
103+
`)))
104+
.then(() => ng('build', '--bundle-dependencies=all'))
105+
.then(() => expectToFail(() => expectFileToMatch('./dist/main.bundle.js',
106+
/require\(["']@angular\/[^"']*["']\)/)))
107+
.then(() => exec(normalize('node'), 'dist/main.bundle.js'));
77108
}

0 commit comments

Comments
 (0)