From 57d966996e4e46265984b077c45dd9a7cec1b7c5 Mon Sep 17 00:00:00 2001 From: Filipe Silva Date: Fri, 11 Oct 2019 15:32:34 +0100 Subject: [PATCH 1/2] feat(@angular-devkit/architect): support multiple configs in WorkspaceNodeModulesArchitectHost Add support for parsing multiple configurations in a single string using comma as a separator. This support is only at the host level (`WorkspaceNodeModulesArchitectHost` in this case) and does not change the underlying Architect API. Different hosts are able to compose target options in different ways. --- .../node/node-modules-architect-host.ts | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/angular_devkit/architect/node/node-modules-architect-host.ts b/packages/angular_devkit/architect/node/node-modules-architect-host.ts index c78cd740648c..4a39e5a52596 100644 --- a/packages/angular_devkit/architect/node/node-modules-architect-host.ts +++ b/packages/angular_devkit/architect/node/node-modules-architect-host.ts @@ -100,16 +100,26 @@ export class WorkspaceNodeModulesArchitectHost implements ArchitectHost c.trim()); + for (const configuration of configurations) { + if (!(targetSpec['configurations'] && targetSpec['configurations'][configuration])) { + throw new Error(`Configuration '${configuration}' is not set in the workspace.`); + } else { + additionalOptions = { + ...additionalOptions, + ...targetSpec['configurations'][configuration], + }; + } + } } return { ...targetSpec['options'], - ...(target.configuration ? targetSpec['configurations'][target.configuration] : 0), + ...additionalOptions, }; } From 55c169976ca30c53ba6556fc509f3a71efd630bb Mon Sep 17 00:00:00 2001 From: Filipe Silva Date: Fri, 11 Oct 2019 16:12:32 +0100 Subject: [PATCH 2/2] feat(@angular/cli): support using multiple configurations It's now possible to use multiple configurations by separating them with a comma: ``` ng build --configuration=one,two,three ``` They will be applied from left to right. If `--prod` is also present, it will be considered to be the first configuration and thus able to be overriden. You can also use it in target strings: ``` "serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": { "browserTarget": "latest-project:build:one,two" }, "configurations": { "production": { "browserTarget": "latest-project:build:production,one,two" } } ``` Fix https://github.com/angular/angular-cli/issues/10612 --- .../angular/cli/models/architect-command.ts | 9 ++- .../e2e/tests/build/multiple-configs.ts | 71 +++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 tests/legacy-cli/e2e/tests/build/multiple-configs.ts diff --git a/packages/angular/cli/models/architect-command.ts b/packages/angular/cli/models/architect-command.ts index 34bf24a76c2f..5e64a6bff178 100644 --- a/packages/angular/cli/models/architect-command.ts +++ b/packages/angular/cli/models/architect-command.ts @@ -366,10 +366,15 @@ export abstract class ArchitectCommand< } else { project = commandOptions.project; target = this.target; - configuration = commandOptions.configuration; - if (!configuration && commandOptions.prod) { + if (commandOptions.prod) { + // The --prod flag will always be the first configuration, available to be overwritten + // by following configurations. configuration = 'production'; } + if (commandOptions.configuration) { + configuration = + `${configuration ? `${configuration},` : ''}${commandOptions.configuration}`; + } } if (!project) { diff --git a/tests/legacy-cli/e2e/tests/build/multiple-configs.ts b/tests/legacy-cli/e2e/tests/build/multiple-configs.ts new file mode 100644 index 000000000000..50b15f03517d --- /dev/null +++ b/tests/legacy-cli/e2e/tests/build/multiple-configs.ts @@ -0,0 +1,71 @@ +import { expectFileToExist } from '../../utils/fs'; +import { ng } from '../../utils/process'; +import { updateJsonFile } from '../../utils/project'; +import { expectToFail } from '../../utils/utils'; + +export default async function () { + await updateJsonFile('angular.json', workspaceJson => { + const appArchitect = workspaceJson.projects['test-project'].architect; + // These are the default options, that we'll overwrite in subsequent configs. + // extractCss defaults to false + // sourceMap defaults to true + appArchitect['options'] = { + outputPath: 'dist/latest-project', + index: 'src/index.html', + main: 'src/main.ts', + polyfills: 'src/polyfills.ts', + tsConfig: 'tsconfig.app.json', + assets: [ + 'src/favicon.ico', + 'src/assets', + ], + 'styles': [ + 'src/styles.css', + ], + 'scripts': [], + }; + const browserConfigs = appArchitect['build'].configurations; + browserConfigs['production'] = { + extractCss: true, + }; + browserConfigs['one'] = { + assets: [], + }; + browserConfigs['two'] = { + sourceMap: false, + }; + browserConfigs['three'] = { + extractCss: false, // Defaults to false when not set. + }; + }); + + // Test the base configuration. + await ng('build'); + await expectFileToExist('dist/test-project/favicon.ico'); + await expectFileToExist('dist/test-project/main-es2015.js.map'); + await expectFileToExist('dist/test-project/styles-es2015.js'); + await expectFileToExist('dist/test-project/vendor-es2015.js'); + // Test that --prod extracts css. + await ng('build', '--prod'); + await expectFileToExist('dist/test-project/styles.css'); + // But using a config overrides prod. + await ng('build', '--prod', '--configuration=three'); + await expectFileToExist('dist/test-project/styles-es2015.js'); + await expectToFail(() => expectFileToExist('dist/test-project/styles.css')); + // Use two configurations. + await ng('build', '--configuration=one,two', '--vendor-chunk=false'); + await expectToFail(() => expectFileToExist('dist/test-project/favicon.ico')); + await expectToFail(() => expectFileToExist('dist/test-project/main-es2015.js.map')); + // Use two configurations and two overrides, one of which overrides a config. + await ng('build', '--configuration=one,two', '--vendor-chunk=false', '--sourceMap=true'); + await expectToFail(() => expectFileToExist('dist/test-project/favicon.ico')); + await expectFileToExist('dist/test-project/main-es2015.js.map'); + await expectToFail(() => expectFileToExist('dist/test-project/vendor-es2015.js')); + // Use three configurations and a override, and prod at the end. + await ng('build', '--configuration=one,two,three', '--vendor-chunk=false', '--prod'); + await expectToFail(() => expectFileToExist('dist/test-project/favicon.ico')); + await expectToFail(() => expectFileToExist('dist/test-project/main-es2015.js.map')); + await expectToFail(() => expectFileToExist('dist/test-project/vendor-es2015.js')); + await expectFileToExist('dist/test-project/styles-es2015.js'); + await expectToFail(() => expectFileToExist('dist/test-project/styles.css')); +}