mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-22 09:12:44 +00:00
198 lines
6.5 KiB
JavaScript
198 lines
6.5 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/plugin",
|
|
"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")));
|
|
});
|
|
|
|
test("public runtime packages construct complete tarballs from source", async (t) => {
|
|
const isolatedRoot = await mkdtemp(path.join(os.tmpdir(), "paseo-runtime-packages-"));
|
|
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",
|
|
]) {
|
|
await assert.rejects(access(path.join(isolatedRoot, relativePath)), relativePath);
|
|
}
|
|
|
|
const packDirectory = path.join(isolatedRoot, "packs");
|
|
await mkdir(packDirectory);
|
|
const packages = [
|
|
{
|
|
workspace: "@getpaseo/workspace-runtime-contract",
|
|
files: ["dist/index.js", "dist/index.d.ts"],
|
|
},
|
|
{
|
|
workspace: "@getpaseo/workspace-helper",
|
|
files: ["dist/index.js", "dist/index.d.ts", "dist/executable.mjs"],
|
|
executable: "dist/executable.mjs",
|
|
},
|
|
];
|
|
|
|
for (const expected of packages) {
|
|
const packed = JSON.parse(
|
|
(
|
|
await run(
|
|
"npm",
|
|
[
|
|
"pack",
|
|
"--json",
|
|
`--workspace=${expected.workspace}`,
|
|
"--pack-destination",
|
|
packDirectory,
|
|
],
|
|
{ cwd: isolatedRoot, timeout: 120_000 },
|
|
)
|
|
).stdout,
|
|
)[0];
|
|
const files = new Map(packed.files.map((file) => [file.path, file]));
|
|
for (const expectedFile of expected.files) assert.ok(files.has(expectedFile), expectedFile);
|
|
if (expected.executable) assert.equal(files.get(expected.executable)?.mode, 0o755);
|
|
await access(path.join(packDirectory, packed.filename));
|
|
}
|
|
});
|
|
|
|
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");
|
|
const workspaces = await isolatedWorkspaces(isolatedRoot);
|
|
const internalScopes = new Set(workspaces.map(({ name }) => name.split("/")[0]));
|
|
await mkdir(targetModules);
|
|
await overlayModules(sourceModules, targetModules, internalScopes);
|
|
|
|
for (const { relativeDirectory, name } of workspaces) {
|
|
const [scope, packageName] = name.split("/");
|
|
const internalScope = path.join(targetModules, scope);
|
|
await mkdir(internalScope, { recursive: true });
|
|
await symlink(
|
|
path.join(isolatedRoot, relativeDirectory),
|
|
path.join(internalScope, packageName),
|
|
"dir",
|
|
);
|
|
}
|
|
}
|
|
|
|
async function createPackageNodeModulesOverlays(isolatedRoot) {
|
|
const internalScopes = new Set(
|
|
(await isolatedWorkspaces(isolatedRoot)).map(({ name }) => name.split("/")[0]),
|
|
);
|
|
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, internalScopes);
|
|
}
|
|
}
|
|
|
|
async function isolatedWorkspaces(isolatedRoot) {
|
|
return Promise.all(
|
|
packageDirectories.map(async (relativeDirectory) => ({
|
|
relativeDirectory,
|
|
name: JSON.parse(
|
|
await readFile(path.join(isolatedRoot, relativeDirectory, "package.json"), "utf8"),
|
|
).name,
|
|
})),
|
|
);
|
|
}
|
|
|
|
async function overlayModules(sourceModules, targetModules, internalScopes) {
|
|
for (const entry of await readdir(sourceModules, { withFileTypes: true })) {
|
|
if (internalScopes.has(entry.name)) continue;
|
|
await symlink(
|
|
path.join(sourceModules, entry.name),
|
|
path.join(targetModules, entry.name),
|
|
entry.isDirectory() ? "dir" : "file",
|
|
);
|
|
}
|
|
}
|