diff --git a/packages/@angular/cli/blueprints/ng2/files/angular-cli.json b/packages/@angular/cli/blueprints/ng2/files/angular-cli.json index 8a57d08bac51..a080fc378b1a 100644 --- a/packages/@angular/cli/blueprints/ng2/files/angular-cli.json +++ b/packages/@angular/cli/blueprints/ng2/files/angular-cli.json @@ -11,6 +11,7 @@ "assets", "favicon.ico" ], + "bundlesOutDir": ".", "index": "index.html", "main": "main.ts", "polyfills": "polyfills.ts", diff --git a/packages/@angular/cli/lib/config/schema.json b/packages/@angular/cli/lib/config/schema.json index c3c3ad42cef6..261e5defee6a 100644 --- a/packages/@angular/cli/lib/config/schema.json +++ b/packages/@angular/cli/lib/config/schema.json @@ -71,6 +71,10 @@ }, "default": [] }, + "bundlesOutDir": { + "type": "string", + "default": "." + }, "deployUrl": { "type": "string", "description": "" diff --git a/packages/@angular/cli/models/webpack-configs/common.ts b/packages/@angular/cli/models/webpack-configs/common.ts index 81829a3b3a68..5c20b33050d6 100644 --- a/packages/@angular/cli/models/webpack-configs/common.ts +++ b/packages/@angular/cli/models/webpack-configs/common.ts @@ -3,8 +3,13 @@ import * as path from 'path'; import { GlobCopyWebpackPlugin } from '../../plugins/glob-copy-webpack-plugin'; import { packageChunkSort } from '../../utilities/package-chunk-sort'; import { BaseHrefWebpackPlugin } from '../../lib/base-href-webpack'; -import { extraEntryParser, lazyChunksFilter, getOutputHashFormat } from './utils'; import { WebpackConfigOptions } from '../webpack-config'; +import { + extraEntryParser, + lazyChunksFilter, + getOutputHashFormat, + getBundleOutputPath, +} from './utils'; const autoprefixer = require('autoprefixer'); const ProgressPlugin = require('webpack/lib/ProgressPlugin'); @@ -96,18 +101,32 @@ export function getCommonConfig(wco: WebpackConfigOptions) { output: { path: path.resolve(projectRoot, buildOptions.outputPath), publicPath: buildOptions.deployUrl, - filename: `[name]${hashFormat.chunk}.bundle.js`, - chunkFilename: `[id]${hashFormat.chunk}.chunk.js` + filename: `${appConfig.bundlesOutDir}/[name]${hashFormat.chunk}.bundle.js`, + chunkFilename: `${appConfig.bundlesOutDir}/[id]${hashFormat.chunk}.chunk.js` }, module: { rules: [ - { enforce: 'pre', test: /\.js$/, loader: 'source-map-loader', exclude: [nodeModules] }, - { test: /\.json$/, loader: 'json-loader' }, - { test: /\.html$/, loader: 'raw-loader' }, - { test: /\.(eot|svg)$/, loader: `file-loader?name=[name]${hashFormat.file}.[ext]` }, + { + enforce: 'pre', + test: /\.js$/, + loader: `source-map-loader?name=${getBundleOutputPath(appConfig)}`, + exclude: [nodeModules] + }, + { + test: /\.json$/, + loader: `json-loader?name=${getBundleOutputPath(appConfig)}` + }, + { + test: /\.html$/, + loader: `raw-loader?name=${getBundleOutputPath(appConfig)}` + }, + { + test: /\.(eot|svg)$/, + loader: `file-loader?name=${getBundleOutputPath(appConfig, hashFormat)}` + }, { test: /\.(jpg|png|gif|otf|ttf|woff|woff2|cur|ani)$/, - loader: `url-loader?name=[name]${hashFormat.file}.[ext]&limit=10000` + loader: `url-loader?name=${getBundleOutputPath(appConfig, hashFormat)}&limit=10000` } ].concat(extraRules) }, diff --git a/packages/@angular/cli/models/webpack-configs/styles.ts b/packages/@angular/cli/models/webpack-configs/styles.ts index 532b0c8c99ed..a1c8d341ec47 100644 --- a/packages/@angular/cli/models/webpack-configs/styles.ts +++ b/packages/@angular/cli/models/webpack-configs/styles.ts @@ -161,7 +161,7 @@ export function getStylesConfig(wco: WebpackConfigOptions) { plugins: [ // extract global css from js files into own css file new ExtractTextPlugin({ - filename: `[name]${hashFormat.extract}.bundle.css`, + filename: `${appConfig.bundlesOutDir}/[name]${hashFormat.extract}.bundle.css`, disable: !buildOptions.extractCss }), new webpack.LoaderOptionsPlugin({ diff --git a/packages/@angular/cli/models/webpack-configs/utils.ts b/packages/@angular/cli/models/webpack-configs/utils.ts index e44e4d34b00b..c85a93943a9b 100644 --- a/packages/@angular/cli/models/webpack-configs/utils.ts +++ b/packages/@angular/cli/models/webpack-configs/utils.ts @@ -86,3 +86,19 @@ export function getOutputHashFormat(option: string, length = 20): HashFormat { /* tslint:enable:max-line-length */ return hashFormats[option] || hashFormats['none']; } + +export function getBundleOutputPath( + appConfig: any, + hashFormat?: HashFormat, +): string { + let result = ''; + if (appConfig.bundlesOutDir !== '.') { + result += `${appConfig.bundlesOutDir}/`; + if (hashFormat) { + result += `[name]${hashFormat.file}.[ext]`; + } else { + result += `[name].[ext]`; + } + } + return result; +} diff --git a/tests/e2e/tests/misc/bundles-output-path.ts b/tests/e2e/tests/misc/bundles-output-path.ts new file mode 100644 index 000000000000..df04e7a4b58e --- /dev/null +++ b/tests/e2e/tests/misc/bundles-output-path.ts @@ -0,0 +1,12 @@ +import { updateJsonFile } from '../../utils/project'; +import { expectFileToExist } from '../../../utils/fs'; + +export default function() { + return Promise.resolve() + .then(() => updateJsonFile('.angular-cli.json', configJson => { + configJson.apps[0].bundlesOutDir = 'bundles'; + })) + .then(() => ng('build', '--output-hashing=none')) + .then(() => !expectFileToExist('dist/main.bundle.js')) + .then(() => expectFileToExist('dist/bundles/main.bundle.js')); +}