Skip to content

feat(@angular/cli): add a bundle dependencies flag #7937

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/documentation/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -366,3 +366,13 @@ Note: service worker support is experimental and subject to change.
Use file name for lazy loaded chunks.
</p>
</details>

<details>
<summary>bundle-dependencies</summary>
<p>
<code>--bundle-dependencies</code>
</p>
<p>
In a server build, state whether `all` or `none` dependencies should be bundles in the output.
</p>
</details>
7 changes: 7 additions & 0 deletions packages/@angular/cli/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.'
}
];

Expand Down
1 change: 1 addition & 0 deletions packages/@angular/cli/models/build-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface BuildOptions {
locale?: string;
missingTranslation?: string;
extractCss?: boolean;
bundleDependencies?: 'none' | 'all';
watch?: boolean;
outputHashing?: string;
poll?: number;
Expand Down
7 changes: 6 additions & 1 deletion packages/@angular/cli/models/webpack-config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { readTsconfig } from '../utilities/read-tsconfig';
const webpackMerge = require('webpack-merge');
import { CliConfig } from './config';
import { BuildOptions } from './build-options';
Expand All @@ -17,6 +18,7 @@ export interface WebpackConfigOptions<T extends BuildOptions = BuildOptions> {
projectRoot: string;
buildOptions: T;
appConfig: any;
tsConfig: any;
}

export class NgCliWebpackConfig<T extends BuildOptions = BuildOptions> {
Expand All @@ -33,7 +35,10 @@ export class NgCliWebpackConfig<T extends BuildOptions = BuildOptions> {
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() {
Expand Down
25 changes: 24 additions & 1 deletion packages/@angular/cli/models/webpack-configs/browser.ts
Original file line number Diff line number Diff line change
@@ -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');

Expand Down Expand Up @@ -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
},
Expand All @@ -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
}
};
}
25 changes: 1 addition & 24 deletions packages/@angular/cli/models/webpack-configs/common.ts
Original file line number Diff line number Diff line change
@@ -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');
Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -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)
};
}
30 changes: 23 additions & 7 deletions packages/@angular/cli/models/webpack-configs/server.ts
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -33,6 +47,8 @@ export function getServerConfig(_wco: WebpackConfigOptions) {
callback();
}
}
]
};
];
}

return config;
}
35 changes: 33 additions & 2 deletions tests/e2e/tests/build/platform-server.ts
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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'
};
Expand Down Expand Up @@ -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('<h2 _ngcontent-c0="">Here are some links to help you start: </h2>')));
new RegExp('<h2 _ngcontent-c0="">Here are some links to help you start: </h2>')))
.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: '<app-root></app-root>'
\}).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'));
}