Skip to content

Commit 815b857

Browse files
committed
Fix bug where defaults sometimes wouldn't apply
1 parent 0af4e15 commit 815b857

2 files changed

Lines changed: 24 additions & 14 deletions

File tree

packages/stack-shared/src/config/schema.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -670,21 +670,25 @@ function applyDefaults<D extends object | ((key: string) => unknown), C extends
670670
const res: any = deepReplaceFunctionsWithObjects(defaults, paths);
671671

672672
outer: for (const [key, mergeValue] of Object.entries(config)) {
673-
if (mergeValue == null) continue;
674-
if (!isObjectLike(mergeValue)) {
673+
if (!isObjectLike(mergeValue) && mergeValue !== null) {
675674
set(res, key, mergeValue);
676675
} else {
677676
const keyParts = key.split(".");
678677
let baseValue: any = defaults;
679678
for (const [index, part] of keyParts.entries()) {
680-
baseValue = has(baseValue, part) ? get(baseValue, part) : (typeof baseValue === 'function' ? (baseValue as any)(part) : undefined);
681-
if (baseValue === undefined || !isObjectLike(baseValue)) {
682-
set(res, key, mergeValue);
679+
if (!isObjectLike(baseValue)) {
680+
set(res, key, mergeValue ?? null);
683681
continue outer;
684682
}
683+
baseValue = has(baseValue, part) ? get(baseValue, part) : (typeof baseValue === 'function' ? (baseValue as any)(part) : undefined);
684+
}
685+
if (!isObjectLike(baseValue)) {
686+
set(res, key, mergeValue ?? baseValue ?? null);
687+
continue outer;
688+
} else {
689+
const newPaths = paths.filter(p => p.startsWith(key + ".")).map(p => p.slice(key.length + 1));
690+
set(res, key, applyDefaults(baseValue, mergeValue ?? {}, newPaths));
685691
}
686-
const newPaths = paths.filter(p => p.startsWith(key + ".")).map(p => p.slice(key.length + 1));
687-
set(res, key, applyDefaults(baseValue, mergeValue, newPaths));
688692
}
689693
}
690694
return res as any;
@@ -711,20 +715,26 @@ import.meta.vitest?.test("applyDefaults", ({ expect }) => {
711715

712716
// Dot notation
713717
expect(applyDefaults({ a: { b: 1 } }, { "a.c": 2 })).toEqual({ a: { b: 1 }, "a.c": 2 });
714-
expect(applyDefaults({ a: { b: 1 } }, { "a.c": null })).toEqual({ a: { b: 1 } });
718+
expect(applyDefaults({ a: { b: 1 } }, { "a.c": null })).toEqual({ a: { b: 1 }, "a.c": null });
719+
expect(applyDefaults({ a: { b: 1 } }, { "a.b": null })).toEqual({ a: { b: 1 }, "a.b": 1 });
720+
expect(applyDefaults({ a: { b: { c: 1 } } }, { "a.b": null })).toEqual({ a: { b: { c: 1 } }, "a.b": { c: 1 } });
721+
expect(applyDefaults({ a: {} }, { "a.b": null })).toEqual({ a: {}, "a.b": null });
722+
expect(applyDefaults({ a: {} }, { "a": { b: 1 }, "a.b": null })).toEqual({ a: { b: 1 }, "a.b": null });
723+
expect(applyDefaults({ a: 1 }, { "a.b": null })).toEqual({ a: 1, "a.b": null });
715724
expect(applyDefaults({ a: 1 }, { "a.b": 2 })).toEqual({ a: 1, "a.b": 2 });
716725
expect(applyDefaults({ a: null }, { "a.b": 2 })).toEqual({ a: null, "a.b": 2 });
717726
expect(applyDefaults({ a: { b: { c: 1 } } }, { "a.b": { d: 2 } })).toEqual({ a: { b: { c: 1 } }, "a.b": { c: 1, d: 2 } });
718-
expect(applyDefaults({ a: { b: { c: 1 } } }, { "a.b": null })).toEqual({ a: { b: { c: 1 } } });
727+
expect(applyDefaults({ a: { b: { c: 1 } } }, { "a.b": null })).toEqual({ a: { b: { c: 1 } }, "a.b": { c: 1 } });
719728
expect(applyDefaults({ a: { b: { c: { d: 1 } } } }, { "a.b.c": {} })).toEqual({ a: { b: { c: { d: 1 } } }, "a.b.c": { d: 1 } });
720729
expect(applyDefaults({ a: () => ({ c: 1 }) }, { "a.b": { d: 2 } })).toEqual({ a: { b: { c: 1 } }, "a.b": { c: 1, d: 2 } });
721-
expect(applyDefaults({ a: () => () => ({ d: 1 }) }, { "a.b": null })).toEqual({ a: { b: {} } });
730+
expect(applyDefaults({ a: () => () => ({ d: 1 }) }, { "a.b": null })).toEqual({ a: { b: {} }, "a.b": {} });
722731
expect(applyDefaults({ a: () => () => ({ d: 1 }) }, { "a.b.c": {} })).toEqual({ a: { b: { c: { d: 1 } } }, "a.b.c": { d: 1 } });
723732
expect(applyDefaults({ a: { b: () => ({ c: 1, d: 2 }) } }, { "a.b.x-y.c": 3 })).toEqual({ a: { b: { "x-y": { c: 1, d: 2 } } }, "a.b.x-y.c": 3 });
724733
expect(applyDefaults({ a: () => ({ c: 1 }) }, { "a.b.d": 2 })).toEqual({ a: { b: { c: 1 } }, "a.b.d": 2 });
725734
expect(applyDefaults({ a: () => ({ c: 1 }) }, { "a.b.d": 2, "a.e.d": 3 })).toEqual({ a: { b: { c: 1 }, e: { c: 1 } }, "a.b.d": 2, "a.e.d": 3 });
726735
expect(applyDefaults({ a: () => ({ c: 1 }) }, { "a.b.d": 2, a: { b: { d: 3 } } })).toEqual({ a: { b: { c: 1, d: 3 } }, "a.b.d": 2 });
727736
expect(applyDefaults({ a: () => ({ c: 1 }) }, { "a.b.d": 2, a: { e: { d: 3 } } })).toEqual({ a: { b: { c: 1 }, e: { c: 1, d: 3 } }, "a.b.d": 2 });
737+
expect(applyDefaults({ a: () => ({ c: 1 }) }, { "a.b": { d: { e: 2 } }, "a.b.d": null })).toEqual({ a: { b: { c: 1 } }, "a.b": { c: 1, d: { e: 2 } }, "a.b.d": null });
728738
});
729739

730740
export function applyProjectDefaults<T extends ProjectRenderedConfigBeforeDefaults>(config: T) {

packages/stack-shared/src/utils/objects.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -530,24 +530,24 @@ import.meta.vitest?.test("deepSortKeys", ({ expect }) => {
530530
]);
531531
});
532532

533-
export function set<T extends object, K extends keyof T>(obj: T, key: K, value: T[K]) {
533+
export function set<T extends object, K extends PropertyKey = keyof T>(obj: T, key: K, value: T[K & keyof T]) {
534534
if (!isObjectLike(obj)) throw new StackAssertionError(`set: obj is not an object (found: ${(obj as any) === null ? "null" : typeof obj})`, { obj, key, value });
535535
Object.defineProperty(obj, key, { value, writable: true, configurable: true, enumerable: true });
536536
}
537537

538-
export function get<T extends object, K extends keyof T>(obj: T, key: K): T[K] {
538+
export function get<T extends object, K extends PropertyKey = keyof T>(obj: T, key: K): T[K & keyof T] {
539539
if ((obj as any) == null) throw new StackAssertionError("get: obj is null or undefined", { obj, key });
540540
const descriptor = Object.getOwnPropertyDescriptor(obj, key);
541541
if (!descriptor) throw new StackAssertionError(`get: key ${String(key)} does not exist`, { obj, key });
542542
return descriptor.value;
543543
}
544544

545-
export function getOrUndefined<T extends object, K extends keyof T>(obj: T, key: K): T[K] | undefined {
545+
export function getOrUndefined<T extends object, K extends PropertyKey = keyof T>(obj: T, key: K): T[K & keyof T] | undefined {
546546
if ((obj as any) == null) throw new StackAssertionError("getOrUndefined: obj is null or undefined", { obj, key });
547547
return has(obj, key) ? get(obj, key) : undefined;
548548
}
549549

550-
export function has<T extends object, K extends keyof T>(obj: T, key: K): obj is T & { [k in K]: unknown } {
550+
export function has<T extends object, K extends PropertyKey = keyof T>(obj: T, key: K): obj is T & { [k in K & keyof T]: unknown } {
551551
if ((obj as any) == null) throw new StackAssertionError("has: obj is null or undefined", { obj, key });
552552
return Object.prototype.hasOwnProperty.call(obj, key);
553553
}

0 commit comments

Comments
 (0)