Files
PaperClipAI/server/scripts/bundle-runtime.test.mjs
T
Devin FoleyandPaperclip e30550ef39 refactor: remove code comments added by recent PRs (#16681)
CONTRIBUTING.md says PRs add no code comments, but #16485, #16586, #16621 and #16672 added some. This removes the 215 comment-only lines git blame on master attributes to those merge commits, across 12 code files, plus one blank line the removal left doubled. Rewritten (not added) comments in scripts/assert-cloud-image-sentry.mjs and comments in .github workflows are left alone. No code changes.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-17 18:55:39 -07:00

111 lines
6.3 KiB
JavaScript

import { execFile } from "node:child_process";
import { mkdirSync, mkdtempSync, realpathSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { promisify } from "node:util";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { bundleRuntime, preserveModuleLocation } from "./bundle-runtime.mjs";
const execFileAsync = promisify(execFile);
function write(file, content) {
mkdirSync(path.dirname(file), { recursive: true });
writeFileSync(file, content);
}
describe("bundleRuntime", () => {
let root;
beforeEach(() => {
root = realpathSync(mkdtempSync(path.join(tmpdir(), "paperclip-bundle-runtime-")));
const pkg = path.join(root, "packages", "fixture");
write(path.join(pkg, "package.json"), JSON.stringify({ name: "@paperclipai/fixture", type: "module", exports: { ".": "./src/index.ts" } }));
write(path.join(pkg, "src", "asset.txt"), "asset beside its source\n");
write(path.join(pkg, "src", "helper.ts"), "export const double = (value: number): number => value * 2;\n");
write(path.join(pkg, "src", "index.ts"), [
"import { readFileSync } from \"node:fs\";",
"import { double } from \"./helper.js\";",
"import { onlyForPackage } from \"package-private-dep\";",
"import { nodeBuild } from \"dual-build-dep\";",
"export const asset = readFileSync(new URL(\"./asset.txt\", import.meta.url), \"utf8\").trim();",
"export const location = import.meta.dirname;",
"export const values = { doubled: double(21), onlyForPackage, nodeBuild };",
"",
].join("\n"));
write(path.join(pkg, "node_modules", "package-private-dep", "package.json"), JSON.stringify({ name: "package-private-dep", type: "module", main: "index.js" }));
write(path.join(pkg, "node_modules", "package-private-dep", "index.js"), "export const onlyForPackage = \"resolved from the package\";\n");
write(path.join(pkg, "node_modules", "dual-build-dep", "package.json"), JSON.stringify({
name: "dual-build-dep", exports: { ".": { module: "./bundler/index.js", default: "./node.cjs" } },
}));
write(path.join(pkg, "node_modules", "dual-build-dep", "node.cjs"), "exports.nodeBuild = \"node build\";\n");
write(path.join(pkg, "node_modules", "dual-build-dep", "bundler", "index.js"), "export * from \"./extensionless\";\n");
const server = path.join(root, "server");
mkdirSync(path.join(server, "node_modules", "@paperclipai"), { recursive: true });
symlinkSync(pkg, path.join(server, "node_modules", "@paperclipai", "fixture"), "dir");
write(path.join(server, "package.json"), JSON.stringify({ name: "server", type: "module" }));
for (const [version, flavor] of [["1", "installed at build"], ["2", "replaced after build"]]) {
const store = path.join(root, "node_modules", ".pnpm", `server-dep@${version}`);
write(path.join(store, "package.json"), JSON.stringify({ name: "server-dep", type: "module", main: "index.js" }));
write(path.join(store, "index.js"), `export const flavor = ${JSON.stringify(flavor)};\n`);
}
symlinkSync(path.join(root, "node_modules", ".pnpm", "server-dep@1"), path.join(server, "node_modules", "server-dep"), "dir");
write(path.join(server, "dist", "late.js"), "import { values } from \"@paperclipai/fixture\";\nexport const late = () => values.doubled + 1;\n");
write(path.join(server, "dist", "index.js"), [
"import { asset, location, values } from \"@paperclipai/fixture\";",
"import { flavor } from \"server-dep\";",
"import { pathToFileURL } from \"node:url\";",
"const { late } = await import(\"./late.js\");",
"// The server only starts when it is the main module.",
"if (pathToFileURL(process.argv[1]).href === import.meta.url) {",
" console.log(JSON.stringify({ asset, location, values, flavor, late: late(), entryDir: import.meta.dirname }));",
"}",
"",
].join("\n"));
});
afterEach(() => {
rmSync(root, { recursive: true, force: true });
});
it("bundles workspace TypeScript and runs with plain node", async () => {
const outdir = path.join(root, "server", "dist-runtime");
const summary = await bundleRuntime({ entry: path.join(root, "server", "dist", "index.js"), outdir });
expect(summary.outfile).toBe(path.join(outdir, "index.mjs"));
const { stdout } = await execFileAsync(process.execPath, [summary.outfile], { cwd: path.join(root, "server"), timeout: 30_000 });
const output = JSON.parse(stdout);
expect(output.values).toEqual({ doubled: 42, onlyForPackage: "resolved from the package", nodeBuild: "node build" });
expect(output.late).toBe(43);
expect(output.asset).toBe("asset beside its source");
expect(output.location).toBe(path.join(root, "packages", "fixture", "src"));
expect(output.entryDir).toBe(path.join(root, "server", "dist"));
});
it("leaves the server's own dependencies to runtime resolution", async () => {
const outdir = path.join(root, "server", "dist-runtime");
const summary = await bundleRuntime({ entry: path.join(root, "server", "dist", "index.js"), outdir });
rmSync(path.join(root, "server", "node_modules", "server-dep"));
symlinkSync(path.join(root, "node_modules", ".pnpm", "server-dep@2"), path.join(root, "server", "node_modules", "server-dep"), "dir");
const { stdout } = await execFileAsync(process.execPath, [summary.outfile], { cwd: path.join(root, "server"), timeout: 30_000 });
expect(JSON.parse(stdout).flavor).toBe("replaced after build");
});
it("keeps third-party packages external", async () => {
const outdir = path.join(root, "server", "dist-runtime");
await bundleRuntime({ entry: path.join(root, "server", "dist", "index.js"), outdir });
const { stdout } = await execFileAsync("grep", ["-rl", "resolved from the package", outdir], { encoding: "utf8" }).catch(() => ({ stdout: "" }));
expect(stdout.trim()).toBe("");
});
});
describe("preserveModuleLocation", () => {
it("replaces import.meta location properties with the original path", () => {
const file = "/srv/app/packages/x/src/mod.ts";
const out = preserveModuleLocation("a(import.meta.url); b(import.meta.dirname); c(import.meta.filename); d(import.meta.resolve);", file);
expect(out).toBe(`a("file:///srv/app/packages/x/src/mod.ts"); b("/srv/app/packages/x/src"); c("/srv/app/packages/x/src/mod.ts"); d(import.meta.resolve);`);
});
});