Skip to content

Commit ad04d68

Browse files
committed
fix(@angular-devkit/core): only invoke smart default provider when value is undefined
Previously, `CoreSchemaRegistry._applySmartDefaults` invoked the smart default provider unconditionally before `CoreSchemaRegistry._set` checked whether the target property already had a defined value. This caused smart default providers to be evaluated even when an option was explicitly provided. (cherry picked from commit c44762f)
1 parent 982c3dc commit ad04d68

2 files changed

Lines changed: 34 additions & 11 deletions

File tree

‎packages/angular_devkit/core/src/json/schema/registry.ts‎

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -585,11 +585,11 @@ export class CoreSchemaRegistry implements SchemaRegistry {
585585
for (const path in answers) {
586586
const pathFragments = path.split('/').slice(1);
587587

588-
CoreSchemaRegistry._set(data, pathFragments, answers[path], null, undefined, true);
588+
await CoreSchemaRegistry._set(data, pathFragments, answers[path], null, undefined, true);
589589
}
590590
}
591591

592-
private static _set(
592+
private static async _set(
593593
// eslint-disable-next-line @typescript-eslint/no-explicit-any
594594
data: any,
595595
fragments: string[],
@@ -598,7 +598,7 @@ export class CoreSchemaRegistry implements SchemaRegistry {
598598
parent: any = null,
599599
parentProperty?: string,
600600
force?: boolean,
601-
): void {
601+
): Promise<void> {
602602
for (let index = 0; index < fragments.length; index++) {
603603
const fragment = fragments[index];
604604
if (/^i\d+$/.test(fragment)) {
@@ -607,7 +607,7 @@ export class CoreSchemaRegistry implements SchemaRegistry {
607607
}
608608

609609
for (let dataIndex = 0; dataIndex < data.length; dataIndex++) {
610-
CoreSchemaRegistry._set(
610+
await CoreSchemaRegistry._set(
611611
data[dataIndex],
612612
fragments.slice(index + 1),
613613
value,
@@ -629,7 +629,12 @@ export class CoreSchemaRegistry implements SchemaRegistry {
629629
}
630630

631631
if (parent && parentProperty && (force || parent[parentProperty] === undefined)) {
632-
parent[parentProperty] = value;
632+
let resolvedValue = typeof value === 'function' ? value() : value;
633+
if (isObservable(resolvedValue)) {
634+
resolvedValue = await lastValueFrom(resolvedValue);
635+
}
636+
637+
parent[parentProperty] = resolvedValue;
633638
}
634639
}
635640

@@ -644,12 +649,7 @@ export class CoreSchemaRegistry implements SchemaRegistry {
644649
continue;
645650
}
646651

647-
let value = source(schema);
648-
if (isObservable(value)) {
649-
value = (await lastValueFrom(value)) as {};
650-
}
651-
652-
CoreSchemaRegistry._set(data, fragments, value);
652+
await CoreSchemaRegistry._set(data, fragments, () => source(schema));
653653
}
654654
}
655655

‎packages/angular_devkit/core/src/json/schema/registry_spec.ts‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,29 @@ describe('CoreSchemaRegistry', () => {
305305
expect(data.obj.deep.arr).toEqual([1, 2, 3]);
306306
});
307307

308+
it('does not call smart default provider when value is already defined', async () => {
309+
const registry = new CoreSchemaRegistry();
310+
const provider = jasmine.createSpy('provider').and.returnValue('default-val');
311+
registry.addSmartDefaultProvider('test', provider);
312+
313+
const validator = await registry.compile({
314+
properties: {
315+
prop: {
316+
type: 'string',
317+
$default: {
318+
$source: 'test',
319+
},
320+
},
321+
},
322+
});
323+
324+
const data = { prop: 'explicit-val' };
325+
const result = await validator(data);
326+
expect(result.success).toBe(true);
327+
expect(data.prop).toBe('explicit-val');
328+
expect(provider).not.toHaveBeenCalled();
329+
});
330+
308331
it('works with true as a schema and post-transforms', async () => {
309332
const registry = new CoreSchemaRegistry();
310333
registry.addPostTransform(addUndefinedDefaults);

0 commit comments

Comments
 (0)