Files
paseo/packages/desktop/scripts/dev-build-contract.test.mjs
T
Mohamed Boudra 7c32fcda98 refactor(workspaces): finalize runtime package boundaries
Keep the daemon dependent only on the public runtime contract and helper, while optional runtime implementations remain external command registrations. Workspace setup continues asynchronously through the bound runtime without coupling production packages to an implementation.
2026-08-14 12:48:35 +02:00

129 lines
4.5 KiB
JavaScript

import { execFile } from "node:child_process";
import {
access,
cp,
mkdir,
mkdtemp,
readFile,
rm,
stat,
symlink,
utimes,
writeFile,
} from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { promisify } from "node:util";
import { describe, expect, test } from "vitest";
const desktopDir = path.resolve(import.meta.dirname, "..");
const repoRoot = path.resolve(desktopDir, "../..");
const execFileAsync = promisify(execFile);
const prerequisite = "npm run dev:prerequisites";
describe("desktop dev build contract", () => {
test("runs one prerequisite before either platform dev entry point", async () => {
const packageJson = JSON.parse(await readFile(path.join(desktopDir, "package.json"), "utf8"));
const devScript = await readFile(path.join(import.meta.dirname, "dev.sh"), "utf8");
expect(packageJson.scripts.predev).toBe(prerequisite);
expect(packageJson.scripts["predev:win"]).toBe(prerequisite);
expect(packageJson.scripts["dev:prerequisites"]).toBe(
"npm --prefix ../.. run build:server-deps && npm --prefix ../.. run build --workspace=@getpaseo/server",
);
expect(packageJson.scripts.dev).toBe("./scripts/dev.sh");
expect(packageJson.scripts["dev:win"]).toBe("powershell ./scripts/dev.ps1");
expect(devScript.indexOf("npm run build:main")).toBeLessThan(
devScript.indexOf('exec node "$SCRIPT_DIR/dev-runner.mjs"'),
);
});
test.each(["missing", "stale"])(
"the public prerequisite repairs %s server artifacts in an isolated checkout",
async (initialState) => {
const isolatedRoot = await createIsolatedBuildCheckout();
const serverExport = path.join(isolatedRoot, "packages/server/dist/server/server/exports.js");
const sourceExport = path.join(isolatedRoot, "packages/server/src/server/exports.ts");
if (initialState === "stale") {
await mkdir(path.dirname(serverExport), { recursive: true });
await writeFile(serverExport, "// stale isolated artifact\n");
const staleTime = new Date(0);
await utimes(serverExport, staleTime, staleTime);
}
try {
await runPrerequisite(isolatedRoot);
await expect(access(serverExport)).resolves.toBeUndefined();
expect(await readFile(serverExport, "utf8")).not.toContain("stale isolated artifact");
expect((await stat(serverExport)).mtimeMs).toBeGreaterThanOrEqual(
(await stat(sourceExport)).mtimeMs,
);
} finally {
await rm(isolatedRoot, { recursive: true, force: true });
}
},
70_000,
);
});
async function createIsolatedBuildCheckout() {
const isolatedRoot = await mkdtemp(path.join(os.tmpdir(), "paseo-desktop-build-contract-"));
const packageDirectories = [
"packages/client",
"packages/desktop",
"packages/highlight",
"packages/protocol",
"packages/relay",
"packages/server",
"packages/workspace-runtime-contract",
"packages/workspace-helper",
];
try {
await Promise.all([
...["package.json", "package-lock.json", "tsconfig.base.json", "tsconfig.json"].map((file) =>
cp(path.join(repoRoot, file), path.join(isolatedRoot, file)),
),
cp(path.join(repoRoot, "scripts"), path.join(isolatedRoot, "scripts"), {
recursive: true,
}),
...packageDirectories.map(async (relativeDirectory) => {
const target = path.join(isolatedRoot, relativeDirectory);
await mkdir(path.dirname(target), { recursive: true });
await cp(path.join(repoRoot, relativeDirectory), target, {
recursive: true,
filter: (source) =>
!source.endsWith(`${path.sep}dist`) &&
!source.endsWith(`${path.sep}node_modules`) &&
!source.endsWith(`${path.sep}test-results`),
});
}),
]);
await symlink(path.join(repoRoot, "node_modules"), path.join(isolatedRoot, "node_modules"));
await Promise.all(
packageDirectories.map((relativeDirectory) =>
symlink(
path.join(repoRoot, relativeDirectory, "node_modules"),
path.join(isolatedRoot, relativeDirectory, "node_modules"),
),
),
);
return isolatedRoot;
} catch (error) {
await rm(isolatedRoot, { recursive: true, force: true });
throw error;
}
}
async function runPrerequisite(isolatedRoot) {
try {
await execFileAsync("npm", ["run", "dev:prerequisites", "--workspace=@getpaseo/desktop"], {
cwd: isolatedRoot,
timeout: 60_000,
});
} catch (error) {
throw new Error(`${error.stdout ?? ""}\n${error.stderr ?? ""}`, { cause: error });
}
}