Skip to content

Commit 78f7648

Browse files
jkremsalan-agius4
authored andcommitted
feat(@angular-devkit/architect): merge object options from CLI
We recently introduced the ability to pass object values from the command line (#28362). @clydin noticed that the initial behavior didn't work well for `--define`: It completely replaced all values even if just one of multiple defines is specified. This updates the architect to support merging of object options. If both the base option (e.g. from `angular.json`) and the override (e.g. from a CLI `--flag`) are objects, the objects are merged. See: #28362
1 parent af6e200 commit 78f7648

File tree

3 files changed

+111
-4
lines changed

3 files changed

+111
-4
lines changed

packages/angular_devkit/architect/src/architect.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import {
4545
SimpleScheduler,
4646
createJobHandler,
4747
} from './jobs';
48+
import { mergeOptions } from './options';
4849
import { scheduleByName, scheduleByTarget } from './schedule-by-name';
4950

5051
const inputSchema = require('./input-schema.json');
@@ -71,10 +72,7 @@ function _createJobHandlerFromBuilderInfo(
7172
concatMap(async (message) => {
7273
if (message.kind === JobInboundMessageKind.Input) {
7374
const v = message.value as BuilderInput;
74-
const options = {
75-
...baseOptions,
76-
...v.options,
77-
};
75+
const options = mergeOptions(baseOptions, v.options);
7876

7977
// Validate v against the options schema.
8078
const validation = await registry.compile(info.optionSchema);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import { json } from '@angular-devkit/core';
10+
11+
import { BuilderInput } from './api';
12+
13+
type OverrideOptions = BuilderInput['options'];
14+
15+
export function mergeOptions(
16+
baseOptions: json.JsonObject,
17+
overrideOptions: OverrideOptions,
18+
): json.JsonObject {
19+
if (!overrideOptions) {
20+
return { ...baseOptions };
21+
}
22+
23+
const options = {
24+
...baseOptions,
25+
...overrideOptions,
26+
};
27+
28+
// For object-object overrides, we merge one layer deep.
29+
for (const key of Object.keys(overrideOptions)) {
30+
const override = overrideOptions[key];
31+
const base = baseOptions[key];
32+
33+
if (json.isJsonObject(base) && json.isJsonObject(override)) {
34+
options[key] = { ...base, ...override };
35+
}
36+
}
37+
38+
return options;
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import { mergeOptions } from './options';
10+
11+
describe('mergeOptions', () => {
12+
it('overwrites literal values', () => {
13+
expect(
14+
mergeOptions(
15+
{
16+
onlyBase: 'base',
17+
a: 'foo',
18+
b: 42,
19+
c: true,
20+
},
21+
{
22+
onlyOverride: 'override',
23+
a: 'bar',
24+
b: 43,
25+
c: false,
26+
},
27+
),
28+
).toEqual({
29+
onlyBase: 'base',
30+
a: 'bar',
31+
b: 43,
32+
c: false,
33+
onlyOverride: 'override',
34+
});
35+
});
36+
37+
it('merges object values one layer deep', () => {
38+
expect(
39+
mergeOptions(
40+
{
41+
obj: {
42+
nested: {
43+
fromBase: true,
44+
},
45+
fromBase: true,
46+
overridden: false,
47+
},
48+
},
49+
{
50+
obj: {
51+
nested: {
52+
fromOverride: true,
53+
},
54+
overridden: true,
55+
fromOverride: true,
56+
},
57+
},
58+
),
59+
).toEqual({
60+
obj: {
61+
nested: {
62+
fromOverride: true,
63+
},
64+
fromBase: true,
65+
overridden: true,
66+
fromOverride: true,
67+
},
68+
});
69+
});
70+
});

0 commit comments

Comments
 (0)