mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-23 17:52:49 +00:00
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.
131 lines
4.3 KiB
JavaScript
131 lines
4.3 KiB
JavaScript
import { execFile } from "node:child_process";
|
|
import { access, cp, mkdir, mkdtemp, readFile, readdir, rm, symlink } from "node:fs/promises";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { promisify } from "node:util";
|
|
|
|
const run = promisify(execFile);
|
|
const repoRoot = path.resolve(import.meta.dirname, "..");
|
|
const packageDirectories = [
|
|
"packages/highlight",
|
|
"packages/relay",
|
|
"packages/protocol",
|
|
"packages/client",
|
|
"packages/workspace-runtime-contract",
|
|
"packages/workspace-helper",
|
|
"packages/server",
|
|
"packages/cli",
|
|
];
|
|
|
|
test("build:server:clean constructs public prerequisites without runtime implementations", async (t) => {
|
|
const isolatedRoot = await mkdtemp(path.join(os.tmpdir(), "paseo-server-clean-build-"));
|
|
t.after(() => rm(isolatedRoot, { recursive: true, force: true }));
|
|
|
|
await copyBuildCheckout(isolatedRoot);
|
|
await createNodeModulesOverlay(isolatedRoot);
|
|
await createPackageNodeModulesOverlays(isolatedRoot);
|
|
|
|
for (const relativePath of [
|
|
"packages/workspace-runtime-contract/dist",
|
|
"packages/workspace-helper/dist",
|
|
"packages/server/dist",
|
|
]) {
|
|
await assert.rejects(access(path.join(isolatedRoot, relativePath)), relativePath);
|
|
}
|
|
await assert.rejects(access(path.join(isolatedRoot, "runtimes")));
|
|
|
|
try {
|
|
await run("npm", ["run", "build:server:clean"], {
|
|
cwd: isolatedRoot,
|
|
timeout: 120_000,
|
|
});
|
|
} catch (error) {
|
|
assert.fail(`${error.stdout ?? ""}\n${error.stderr ?? ""}`);
|
|
}
|
|
|
|
for (const relativePath of [
|
|
"packages/workspace-runtime-contract/dist/index.js",
|
|
"packages/workspace-helper/dist/index.js",
|
|
"packages/server/dist/server/server/exports.js",
|
|
"packages/cli/dist/index.js",
|
|
]) {
|
|
await access(path.join(isolatedRoot, relativePath));
|
|
}
|
|
await assert.rejects(access(path.join(isolatedRoot, "runtimes")));
|
|
});
|
|
|
|
async function copyBuildCheckout(isolatedRoot) {
|
|
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,
|
|
filter: excludesBuildOutput,
|
|
}),
|
|
...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: excludesBuildOutput,
|
|
});
|
|
}),
|
|
]);
|
|
}
|
|
|
|
function excludesBuildOutput(source) {
|
|
return (
|
|
!source.endsWith(`${path.sep}dist`) &&
|
|
!source.endsWith(`${path.sep}node_modules`) &&
|
|
!source.endsWith(`${path.sep}test-results`)
|
|
);
|
|
}
|
|
|
|
async function createNodeModulesOverlay(isolatedRoot) {
|
|
const sourceModules = path.join(repoRoot, "node_modules");
|
|
const targetModules = path.join(isolatedRoot, "node_modules");
|
|
await mkdir(targetModules);
|
|
await overlayModules(sourceModules, targetModules);
|
|
|
|
const internalScope = path.join(targetModules, "@getpaseo");
|
|
await mkdir(internalScope);
|
|
for (const relativeDirectory of packageDirectories) {
|
|
const manifest = JSON.parse(
|
|
await readFile(path.join(isolatedRoot, relativeDirectory, "package.json"), "utf8"),
|
|
);
|
|
await symlink(
|
|
path.join(isolatedRoot, relativeDirectory),
|
|
path.join(internalScope, manifest.name.slice("@getpaseo/".length)),
|
|
"dir",
|
|
);
|
|
}
|
|
}
|
|
|
|
async function createPackageNodeModulesOverlays(isolatedRoot) {
|
|
for (const relativeDirectory of packageDirectories) {
|
|
const sourceModules = path.join(repoRoot, relativeDirectory, "node_modules");
|
|
try {
|
|
await access(sourceModules);
|
|
} catch {
|
|
continue;
|
|
}
|
|
const targetModules = path.join(isolatedRoot, relativeDirectory, "node_modules");
|
|
await mkdir(targetModules);
|
|
await overlayModules(sourceModules, targetModules);
|
|
}
|
|
}
|
|
|
|
async function overlayModules(sourceModules, targetModules) {
|
|
for (const entry of await readdir(sourceModules, { withFileTypes: true })) {
|
|
if (entry.name === "@getpaseo") continue;
|
|
await symlink(
|
|
path.join(sourceModules, entry.name),
|
|
path.join(targetModules, entry.name),
|
|
entry.isDirectory() ? "dir" : "file",
|
|
);
|
|
}
|
|
}
|