diff --git a/scripts/build-diffs-viewer-runtime.mjs b/scripts/build-diffs-viewer-runtime.mjs index 5d4f739f1fa..d569e4a8ef2 100644 --- a/scripts/build-diffs-viewer-runtime.mjs +++ b/scripts/build-diffs-viewer-runtime.mjs @@ -7,6 +7,8 @@ import { build } from "esbuild"; const modulePath = fileURLToPath(import.meta.url); const repoRoot = path.resolve(path.dirname(modulePath), ".."); +const pierreDiffsEmptySideEffectNamespace = "openclaw-diffs-empty-side-effect"; +const pierreDiffsEmptySideEffectPath = "pierre-diffs-parse-decorations-side-effect"; const targets = { curated: { @@ -20,6 +22,39 @@ const targets = { }, }; +function toPosixPath(value) { + return String(value ?? "").replaceAll("\\", "/"); +} + +export function createPierreDiffsSideEffectImportPlugin() { + return { + name: "openclaw-diffs-pierre-side-effect-imports", + setup(buildContext) { + buildContext.onResolve({ filter: /^diff$/ }, (args) => { + const importer = toPosixPath(args.importer); + if (!importer.endsWith("/@pierre/diffs/dist/utils/parseDiffDecorations.js")) { + return undefined; + } + return { + path: pierreDiffsEmptySideEffectPath, + namespace: pierreDiffsEmptySideEffectNamespace, + sideEffects: true, + }; + }); + buildContext.onLoad( + { + filter: /^pierre-diffs-parse-decorations-side-effect$/, + namespace: pierreDiffsEmptySideEffectNamespace, + }, + () => ({ + contents: "export {};\n", + loader: "js", + }), + ); + }, + }; +} + export async function buildDiffsViewerRuntime(targetName) { const target = targets[targetName]; if (!target) { @@ -44,18 +79,21 @@ export async function buildDiffsViewerRuntime(targetName) { legalComments: "none", outfile: outputPath, write: false, - plugins: target.shikiAlias - ? [ - { - name: "openclaw-diffs-curated-shiki", - setup(buildContext) { - buildContext.onResolve({ filter: /^shiki$/ }, () => ({ - path: path.join(repoRoot, target.shikiAlias), - })); + plugins: [ + createPierreDiffsSideEffectImportPlugin(), + ...(target.shikiAlias + ? [ + { + name: "openclaw-diffs-curated-shiki", + setup(buildContext) { + buildContext.onResolve({ filter: /^shiki$/ }, () => ({ + path: path.join(repoRoot, target.shikiAlias), + })); + }, }, - }, - ] - : [], + ] + : []), + ], }); const outputFile = result.outputFiles?.[0]; diff --git a/scripts/test-projects.test-support.mjs b/scripts/test-projects.test-support.mjs index 82d986c5337..9d48e13cdf1 100644 --- a/scripts/test-projects.test-support.mjs +++ b/scripts/test-projects.test-support.mjs @@ -423,6 +423,7 @@ const TOOLING_SOURCE_TEST_TARGETS = new Map([ ["scripts/test-projects.test-support.mjs", ["test/scripts/test-projects.test.ts"]], ["scripts/bundled-plugin-assets.mjs", ["test/scripts/bundled-plugin-assets.test.ts"]], ["scripts/bundle-a2ui.mjs", ["test/scripts/bundled-plugin-assets.test.ts"]], + ["scripts/build-diffs-viewer-runtime.mjs", ["test/scripts/build-diffs-viewer-runtime.test.ts"]], ["extensions/canvas/scripts/bundle-a2ui.mjs", ["extensions/canvas/scripts/bundle-a2ui.test.ts"]], ["extensions/canvas/scripts/copy-a2ui.mjs", ["extensions/canvas/scripts/copy-a2ui.test.ts"]], ]); diff --git a/test/scripts/build-diffs-viewer-runtime.test.ts b/test/scripts/build-diffs-viewer-runtime.test.ts new file mode 100644 index 00000000000..d0a2ba51270 --- /dev/null +++ b/test/scripts/build-diffs-viewer-runtime.test.ts @@ -0,0 +1,49 @@ +import { describe, expect, it } from "vitest"; +import { createPierreDiffsSideEffectImportPlugin } from "../../scripts/build-diffs-viewer-runtime.mjs"; + +type ResolveCallback = (args: { importer: string; path: string }) => unknown; +type LoadCallback = () => unknown; + +function collectPluginCallbacks() { + const resolveCallbacks: ResolveCallback[] = []; + const loadCallbacks: LoadCallback[] = []; + const plugin = createPierreDiffsSideEffectImportPlugin(); + plugin.setup({ + onResolve(_options: unknown, callback: ResolveCallback) { + resolveCallbacks.push(callback); + }, + onLoad(_options: unknown, callback: LoadCallback) { + loadCallbacks.push(callback); + }, + }); + return { loadCallbacks, resolveCallbacks }; +} + +describe("build diffs viewer runtime", () => { + it("replaces Pierre Diffs' empty side-effect import without touching real diff imports", () => { + const { loadCallbacks, resolveCallbacks } = collectPluginCallbacks(); + expect(resolveCallbacks).toHaveLength(1); + expect(loadCallbacks).toHaveLength(1); + + expect( + resolveCallbacks[0]({ + path: "diff", + importer: "/repo/node_modules/@pierre/diffs/dist/utils/parseDiffDecorations.js", + }), + ).toEqual({ + path: "pierre-diffs-parse-decorations-side-effect", + namespace: "openclaw-diffs-empty-side-effect", + sideEffects: true, + }); + expect( + resolveCallbacks[0]({ + path: "diff", + importer: "/repo/node_modules/@pierre/diffs/dist/utils/renderDiffWithHighlighter.js", + }), + ).toBeUndefined(); + expect(loadCallbacks[0]()).toEqual({ + contents: "export {};\n", + loader: "js", + }); + }); +});