diff --git a/packages/coding-agent/package.json b/packages/coding-agent/package.json index c76738d21..55b414430 100644 --- a/packages/coding-agent/package.json +++ b/packages/coding-agent/package.json @@ -107,6 +107,10 @@ "dist/cli.js", "dist/CHANGELOG-*.md", "dist/*.node", + "dist/template-*.css", + "dist/template-*.html", + "dist/template-*.js", + "dist/tool-views.generated-*.js", "scripts", "examples", "README.md", diff --git a/packages/coding-agent/scripts/bundle-dist.ts b/packages/coding-agent/scripts/bundle-dist.ts index 3d54530ee..22738a75b 100755 --- a/packages/coding-agent/scripts/bundle-dist.ts +++ b/packages/coding-agent/scripts/bundle-dist.ts @@ -9,6 +9,7 @@ const packageDir = path.join(import.meta.dir, ".."); const outDir = path.join(packageDir, "dist"); const cliPath = path.join(outDir, "cli.js"); const shebang = "#!/usr/bin/env bun\n"; +const legacyHtmlExportAssetPattern = /^(?:template-[^.]+\.(?:css|html|js)|tool-views\.generated-[^.]+\.js)$/; // Native / optional / platform-specific deps are loaded from installed files. // `omp-legacy-pi-modules` exists only in compiled binaries via the build plugin; @@ -80,7 +81,8 @@ async function cleanBundleOutputs(): Promise { entry === "cli.js" || entry.endsWith(".node") || entry.endsWith(".js.map") || - (entry.startsWith("CHANGELOG-") && entry.endsWith(".md")), + (entry.startsWith("CHANGELOG-") && entry.endsWith(".md")) || + legacyHtmlExportAssetPattern.test(entry), ) .map(entry => fs.rm(path.join(outDir, entry), { force: true })), ); diff --git a/packages/coding-agent/test/export-html-template.test.ts b/packages/coding-agent/test/export-html-template.test.ts index a4ba96977..92d48434d 100644 --- a/packages/coding-agent/test/export-html-template.test.ts +++ b/packages/coding-agent/test/export-html-template.test.ts @@ -2,6 +2,7 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import * as fs from "node:fs"; import * as os from "node:os"; import * as path from "node:path"; +import type { BunPlugin } from "bun"; import { getTemplate } from "../src/export/html/index"; interface HeapProbeResult { @@ -15,6 +16,9 @@ interface TemplateProbeResult { sha256: string; stableCache: boolean; } +interface NpmPackResult { + files: Array<{ path: string }>; +} const expectedTemplate: TemplateProbeResult = { chars: 377_268, @@ -25,9 +29,11 @@ const expectedTemplate: TemplateProbeResult = { const assetDir = new URL("../src/export/html/", import.meta.url); const templateProbePath = path.resolve(import.meta.dir, "fixtures", "html-export-template-probe.ts"); const heapProbePath = path.resolve(import.meta.dir, "fixtures", "html-export-static-import-heap-probe.ts"); +const packageDir = path.resolve(import.meta.dir, ".."); const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "omp-html-template-")); const unrelatedCwd = path.join(tempRoot, "unrelated-cwd"); -const bundleDir = path.join(tempRoot, "bundle"); +const packDir = path.join(tempRoot, "package"); +const bundleDir = path.join(packDir, "dist"); const compiledPath = path.join(tempRoot, "compiled-template-probe"); let bundlePath: string; const bundledDependencyStubs: Record = { @@ -88,11 +94,19 @@ async function runProbe(command: string[]): Promise { beforeAll(async () => { fs.mkdirSync(unrelatedCwd); + fs.mkdirSync(packDir); + const packageManifest = JSON.parse(fs.readFileSync(path.join(packageDir, "package.json"), "utf8")) as Record< + string, + unknown + >; + packageManifest.scripts = {}; + fs.writeFileSync(path.join(packDir, "package.json"), JSON.stringify(packageManifest)); const bundle = await Bun.build({ entrypoints: [templateProbePath], outdir: bundleDir, target: "bun", plugins: [focusedBundlePlugin], + naming: { entry: "cli.js" }, }); expect(bundle.success, bundle.logs.map(log => log.message).join("\n")).toBe(true); const entrypoint = bundle.outputs.find(output => output.kind === "entry-point"); @@ -133,6 +147,29 @@ describe("HTML export template", () => { expect(await runProbe([process.execPath, bundlePath])).toEqual(expectedTemplate); }); + test("packs every normal-bundle HTML export asset", async () => { + const proc = Bun.spawn(["npm", "pack", "--dry-run", "--ignore-scripts", "--json"], { + cwd: packDir, + stderr: "pipe", + stdout: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([ + new Response(proc.stdout).text(), + new Response(proc.stderr).text(), + proc.exited, + ]); + expect(exitCode, stderr).toBe(0); + const [packResult] = JSON.parse(stdout) as NpmPackResult[]; + const packedAssets = packResult!.files.map(file => file.path).filter(filePath => + /^dist\/(?:template-[^.]+\.(?:css|html|js)|tool-views\.generated-[^.]+\.js)$/.test(filePath), + ); + expect(packedAssets).toHaveLength(4); + expect(packedAssets.filter(filePath => /^dist\/template-[^.]+\.css$/.test(filePath))).toHaveLength(1); + expect(packedAssets.filter(filePath => /^dist\/template-[^.]+\.html$/.test(filePath))).toHaveLength(1); + expect(packedAssets.filter(filePath => /^dist\/template-[^.]+\.js$/.test(filePath))).toHaveLength(1); + expect(packedAssets.filter(filePath => /^dist\/tool-views\.generated-[^.]+\.js$/.test(filePath))).toHaveLength(1); + }); + test("preserves exact bytes in a compiled bundle launched from an unrelated directory", async () => { expect(await runProbe([compiledPath])).toEqual(expectedTemplate); });