diff --git a/src/plugins/bundled-runtime-mirror.test.ts b/src/plugins/bundled-runtime-mirror.test.ts index 31986c47a4c..5f264769e0c 100644 --- a/src/plugins/bundled-runtime-mirror.test.ts +++ b/src/plugins/bundled-runtime-mirror.test.ts @@ -19,13 +19,13 @@ afterEach(() => { }); describe("refreshBundledPluginRuntimeMirrorRoot", () => { - it("refreshes stale mirrors without deleting the active target root", () => { + it("refreshes stale mirrors without leaving removed source files behind", () => { const root = makeTempRoot(); const sourceRoot = path.join(root, "source"); const targetRoot = path.join(root, "target"); fs.mkdirSync(sourceRoot, { recursive: true }); fs.mkdirSync(targetRoot, { recursive: true }); - fs.writeFileSync(path.join(sourceRoot, "index.js"), "export const value = 'v1';\n", "utf8"); + fs.writeFileSync(path.join(sourceRoot, "old.js"), "export const value = 'v1';\n", "utf8"); expect( refreshBundledPluginRuntimeMirrorRoot({ @@ -35,8 +35,8 @@ describe("refreshBundledPluginRuntimeMirrorRoot", () => { }), ).toBe(true); - fs.writeFileSync(path.join(sourceRoot, "index.js"), "export const value = 'v2';\n", "utf8"); - fs.writeFileSync(path.join(targetRoot, "inflight-import.js"), "still readable\n", "utf8"); + fs.rmSync(path.join(sourceRoot, "old.js")); + fs.writeFileSync(path.join(sourceRoot, "new.js"), "export const value = 'v2';\n", "utf8"); expect( refreshBundledPluginRuntimeMirrorRoot({ @@ -46,9 +46,47 @@ describe("refreshBundledPluginRuntimeMirrorRoot", () => { }), ).toBe(true); - expect(fs.readFileSync(path.join(targetRoot, "index.js"), "utf8")).toContain("v2"); - expect(fs.readFileSync(path.join(targetRoot, "inflight-import.js"), "utf8")).toBe( - "still readable\n", - ); + expect(fs.readdirSync(targetRoot).toSorted()).toEqual([ + ".openclaw-runtime-mirror.json", + "new.js", + ]); + expect(fs.readFileSync(path.join(targetRoot, "new.js"), "utf8")).toContain("v2"); + expect( + refreshBundledPluginRuntimeMirrorRoot({ + pluginId: "demo", + sourceRoot, + targetRoot, + }), + ).toBe(false); + }); + + it("replaces stale target entries when the source changes type", () => { + const root = makeTempRoot(); + const sourceRoot = path.join(root, "source"); + const targetRoot = path.join(root, "target"); + fs.mkdirSync(path.join(sourceRoot, "entry"), { recursive: true }); + fs.writeFileSync(path.join(sourceRoot, "entry", "index.js"), "export const value = 1;\n"); + + expect( + refreshBundledPluginRuntimeMirrorRoot({ + pluginId: "demo", + sourceRoot, + targetRoot, + }), + ).toBe(true); + + fs.rmSync(path.join(sourceRoot, "entry"), { recursive: true, force: true }); + fs.writeFileSync(path.join(sourceRoot, "entry"), "export const value = 2;\n"); + + expect( + refreshBundledPluginRuntimeMirrorRoot({ + pluginId: "demo", + sourceRoot, + targetRoot, + }), + ).toBe(true); + + expect(fs.lstatSync(path.join(targetRoot, "entry")).isFile()).toBe(true); + expect(fs.readFileSync(path.join(targetRoot, "entry"), "utf8")).toContain("2"); }); }); diff --git a/src/plugins/bundled-runtime-mirror.ts b/src/plugins/bundled-runtime-mirror.ts index 36506424e04..ffb8a1148be 100644 --- a/src/plugins/bundled-runtime-mirror.ts +++ b/src/plugins/bundled-runtime-mirror.ts @@ -74,7 +74,10 @@ export function copyBundledPluginRuntimeRoot(sourceRoot: string, targetRoot: str pruneStaleBundledRuntimeMirrorEntries(targetRoot, mirroredNames); } -function pruneStaleBundledRuntimeMirrorEntries(targetRoot: string, mirroredNames: Set): void { +function pruneStaleBundledRuntimeMirrorEntries( + targetRoot: string, + mirroredNames: Set, +): void { for (const entry of fs.readdirSync(targetRoot, { withFileTypes: true })) { if (shouldIgnoreBundledRuntimeMirrorEntry(entry.name)) { continue;