diff --git a/src/configWatcher.ts b/src/configWatcher.ts index 562668ec..ee63f448 100644 --- a/src/configWatcher.ts +++ b/src/configWatcher.ts @@ -26,7 +26,7 @@ export function watchConfigurationChanges( const newValue = getValue(); - if (!isDeepStrictEqual(newValue, appliedValues.get(setting))) { + if (!configValuesEqual(newValue, appliedValues.get(setting))) { changedSettings.push(setting); appliedValues.set(setting, newValue); } @@ -37,3 +37,22 @@ export function watchConfigurationChanges( } }); } + +function configValuesEqual(a: unknown, b: unknown): boolean { + return isDeepStrictEqual(normalizeEmptyValue(a), normalizeEmptyValue(b)); +} + +/** + * Normalize empty values (undefined, null, "", []) to a canonical form for comparison. + */ +function normalizeEmptyValue(value: unknown): unknown { + if ( + value === undefined || + value === null || + value === "" || + (Array.isArray(value) && value.length === 0) + ) { + return undefined; + } + return value; +} diff --git a/test/unit/configWatcher.test.ts b/test/unit/configWatcher.test.ts index 0ce6862c..2327b2df 100644 --- a/test/unit/configWatcher.test.ts +++ b/test/unit/configWatcher.test.ts @@ -94,4 +94,44 @@ describe("watchConfigurationChanges", () => { expect(changes).toEqual([["test.setting"]]); dispose(); }); + + it("treats undefined, null, empty string, and empty array as equivalent", () => { + const config = new MockConfigurationProvider(); + config.set("test.setting", undefined); + const { changes, dispose } = createWatcher("test.setting"); + + config.set("test.setting", null); + config.set("test.setting", ""); + config.set("test.setting", []); + config.set("test.setting", undefined); + + expect(changes).toEqual([]); + dispose(); + }); + + interface ValueChangeTestCase { + name: string; + from: unknown; + to: unknown; + } + + it.each([ + { name: "undefined to value", from: undefined, to: "value" }, + { name: "value to empty string", from: "value", to: "" }, + { name: "undefined to false", from: undefined, to: false }, + { name: "undefined to zero", from: undefined, to: 0 }, + { name: "null to value", from: null, to: "value" }, + { name: "empty string to value", from: "", to: "value" }, + { name: "empty array to non-empty array", from: [], to: ["item"] }, + { name: "value to different value", from: "old", to: "new" }, + ])("detects change: $name", ({ from, to }) => { + const config = new MockConfigurationProvider(); + config.set("test.setting", from); + const { changes, dispose } = createWatcher("test.setting"); + + config.set("test.setting", to); + + expect(changes).toEqual([["test.setting"]]); + dispose(); + }); });