From 645822c24eeae0d41c5d83c2756b56b338fa5a43 Mon Sep 17 00:00:00 2001 From: Vadim Velicodnii Date: Tue, 23 Jun 2026 11:46:03 +0100 Subject: [PATCH 1/2] fix(config): preserve other deploy fields when writing org/app --- config.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/config.ts b/config.ts index b885639..601fba6 100644 --- a/config.ts +++ b/config.ts @@ -297,15 +297,25 @@ async function writeConfig( const content = configContent.config?.content ?? "{}\n"; - const newConfig: Record = { org }; + const config = parseJSONC(content); + const deployObj = config.asObjectOrForce().getIfObjectOrForce("deploy"); + + const upsertKey = (key: string, value: string) => { + const prop = deployObj.get(key); + if (prop) { + prop.setValue(value); + } else { + deployObj.append(key, value); + } + }; + upsertKey("org", org); if (app) { - newConfig.app = app; + upsertKey("app", app); + } else { + deployObj.get("app")?.remove(); } - const config = parseJSONC(content); - const deployObj = config.asObjectOrForce().getIfObjectOrForce("deploy"); - deployObj.replaceWith(newConfig); deployObj.ensureMultiline(); await Deno.writeTextFile( From b79338c8b123abf48200f1c0ae60c95e751b49a2 Mon Sep 17 00:00:00 2001 From: Vadim Velicodnii Date: Tue, 23 Jun 2026 13:19:38 +0100 Subject: [PATCH 2/2] test(config): test writeConfig via the deploy action handler --- tests/config.test.ts | 96 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 tests/config.test.ts diff --git a/tests/config.test.ts b/tests/config.test.ts new file mode 100644 index 0000000..a68c78b --- /dev/null +++ b/tests/config.test.ts @@ -0,0 +1,96 @@ +import { assertEquals, assertStringIncludes } from "@std/assert"; +import { join } from "@std/path"; +import { actionHandler } from "../config.ts"; +import type { GlobalContext } from "../main.ts"; + +// Runs the deploy action handler on a temporary config +async function runDeploy( + content: string, + resolved: { org: string | undefined; app: string | undefined }, +): Promise { + const dir = await Deno.makeTempDir(); + const path = join(dir, "deno.json"); + try { + await Deno.writeTextFile(path, content); + const context = { + config: path, + ignore: [], + allowNodeModules: false, + debug: false, + } as unknown as GlobalContext; + + await actionHandler((config) => { + config.org = resolved.org; + config.app = resolved.app; + })(context); + + return await Deno.readTextFile(path); + } finally { + await Deno.remove(dir, { recursive: true }); + } +} + +Deno.test("deploy preserves other deploy fields when persisting org/app", async () => { + const input = `{ + "deploy": { + "org": "old-org", + "app": "old-app", + "exclude": ["!dist"], + "framework": "fresh" + } +} +`; + const outputConfigJson = await runDeploy(input, { + org: "my-org", + app: "my-app", + }); + const outputConfig = JSON.parse(outputConfigJson); + + assertEquals(outputConfig.deploy.org, "my-org"); + assertEquals(outputConfig.deploy.app, "my-app"); + assertEquals(outputConfig.deploy.exclude, ["!dist"]); + assertEquals(outputConfig.deploy.framework, "fresh"); +}); + +Deno.test("deploy creates the deploy block when the config has none", async () => { + const outputConfigJson = await runDeploy("{}\n", { + org: "my-org", + app: "my-app", + }); + const outputConfig = JSON.parse(outputConfigJson); + + assertEquals(outputConfig.deploy.org, "my-org"); + assertEquals(outputConfig.deploy.app, "my-app"); +}); + +Deno.test("deploy clears app when it doesn't resolve but keeps siblings", async () => { + const input = `{ + "deploy": { "org": "old-org", "app": "stale", "exclude": ["!dist"] } +} +`; + + const outputConfigJson = await runDeploy(input, { + org: "my-org", + app: undefined, + }); + const out = JSON.parse(outputConfigJson); + + assertEquals(out.deploy.app, undefined); + assertEquals(out.deploy.exclude, ["!dist"]); +}); + +Deno.test("deploy preserves comments and formatting (jsonc)", async () => { + const input = `{ + // keep this comment + "deploy": { + "org": "old-org", // and this one + "exclude": ["!dist"] + } +} +`; + + const out = await runDeploy(input, { org: "my-org", app: undefined }); + + assertStringIncludes(out, "// keep this comment"); + assertStringIncludes(out, "// and this one"); +});