项目文件夹

文件
wehub-resource-sync 070959e133
landing-page-staging / Deploy landing page to staging (push) Has been skipped
landing-page-ci / Validate landing page (push) Failing after 4s
visual-baseline / Capture visual baselines (push) Has been cancelled
bake-plugin-previews / Bake plugin previews (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:00:47 +08:00

318 行
12 KiB
TypeScript

import { mkdir, mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { ToolPackCache } from "../src/cache.js";
import type { ToolPackConfig } from "../src/config.js";
import { ensureWorkspaceBuildArtifacts } from "../src/workspace-build.js";
const PACKAGE_DIRS = [
"packages/release",
"packages/components",
"packages/contracts",
"packages/registry-protocol",
"packages/sidecar-proto",
"packages/launcher-proto",
"packages/sidecar",
"packages/platform",
"packages/download",
"packages/host",
"packages/agui-adapter",
"packages/plugin-runtime",
"packages/diagnostics",
"apps/daemon",
"apps/web",
"apps/desktop",
"apps/packaged",
] as const;
const OUTPUT_FILES = [
"packages/release/dist/index.mjs",
"packages/release/dist/index.d.ts",
"packages/components/dist/index.mjs",
"packages/components/dist/index.d.ts",
"packages/contracts/dist/index.mjs",
"packages/contracts/dist/index.d.ts",
"packages/registry-protocol/dist/index.mjs",
"packages/registry-protocol/dist/index.d.ts",
"packages/sidecar-proto/dist/index.mjs",
"packages/sidecar-proto/dist/index.d.ts",
"packages/launcher-proto/dist/index.mjs",
"packages/launcher-proto/dist/index.d.ts",
"packages/sidecar/dist/index.mjs",
"packages/sidecar/dist/index.d.ts",
"packages/platform/dist/index.mjs",
"packages/platform/dist/index.d.ts",
"packages/download/dist/index.mjs",
"packages/download/dist/index.d.ts",
"packages/host/dist/index.mjs",
"packages/host/dist/index.d.ts",
"packages/agui-adapter/dist/index.mjs",
"packages/agui-adapter/dist/index.d.ts",
"packages/plugin-runtime/dist/index.mjs",
"packages/plugin-runtime/dist/index.d.ts",
"packages/diagnostics/dist/index.mjs",
"packages/diagnostics/dist/index.d.ts",
"apps/daemon/dist/cli.js",
"apps/daemon/dist/cli.d.ts",
"apps/daemon/dist/sidecar/index.js",
"apps/web/dist/sidecar/index.js",
"apps/web/dist/sidecar/index.d.ts",
"apps/web/.next/standalone/apps/web/server.js",
"apps/web/.next/static/chunk.js",
"apps/desktop/dist/main/index.js",
"apps/desktop/dist/main/index.d.ts",
"apps/packaged/dist/index.mjs",
"apps/packaged/dist/index.d.ts",
] as const;
async function writeWorkspace(root: string): Promise<void> {
await writeFile(join(root, "package.json"), `${JSON.stringify({ packageManager: "pnpm@10.33.2" }, null, 2)}\n`, "utf8");
await writeFile(join(root, "pnpm-lock.yaml"), "lockfileVersion: '9.0'\n", "utf8");
for (const directory of PACKAGE_DIRS) {
await mkdir(join(root, directory, "src"), { recursive: true });
await writeFile(join(root, directory, "package.json"), `${JSON.stringify({ name: directory }, null, 2)}\n`, "utf8");
await writeFile(join(root, directory, "src", "index.ts"), "export const value = 1;\n", "utf8");
}
}
async function writeOutputs(root: string, value: string): Promise<void> {
for (const file of OUTPUT_FILES) {
await mkdir(join(root, file, ".."), { recursive: true });
await writeFile(join(root, file), `${value}\n`, "utf8");
}
}
async function writeStandalonePeerDeps(root: string): Promise<void> {
const pnpmRoot = join(root, "apps/web/.next/standalone/node_modules/.pnpm");
for (const directory of ["react@18.3.1", "react-dom@18.3.1_react@18.3.1", "styled-jsx@5.1.6_react@18.3.1"]) {
const packageName = directory.split("@")[0]!;
const packageRoot = join(pnpmRoot, directory, "node_modules", packageName);
await mkdir(packageRoot, { recursive: true });
await writeFile(join(packageRoot, "package.json"), `${JSON.stringify({ name: packageName }, null, 2)}\n`, "utf8");
}
}
function createConfig(root: string, cacheRoot: string): ToolPackConfig {
return {
containerized: false,
electronBuilderCliPath: "electron-builder",
electronDistPath: "electron-dist",
electronVersion: "41.3.0",
macCompression: "normal",
namespace: "test",
platform: "win",
portable: false,
removeData: false,
removeLogs: false,
removeProductUserData: false,
removeSidecars: false,
requireVelaCli: false,
roots: {
cacheRoot,
output: {
appBuilderRoot: join(root, ".tmp", "builder"),
namespaceRoot: join(root, ".tmp", "out", "win", "namespaces", "test"),
platformRoot: join(root, ".tmp", "out", "win"),
root: join(root, ".tmp", "out"),
},
runtime: {
namespaceBaseRoot: join(root, ".tmp", "runtime", "win", "namespaces"),
namespaceRoot: join(root, ".tmp", "runtime", "win", "namespaces", "test"),
},
toolPackRoot: join(root, ".tmp", "tools-pack"),
},
signed: false,
silent: true,
to: "dir",
webOutputMode: "standalone",
workspaceRoot: root,
};
}
describe("ensureWorkspaceBuildArtifacts", () => {
it("builds once and skips when the key and outputs are still valid", async () => {
const root = await mkdtemp(join(tmpdir(), "open-design-workspace-build-"));
const cache = new ToolPackCache(join(root, ".cache"));
const config = createConfig(root, cache.root);
let builds = 0;
try {
await writeWorkspace(root);
await ensureWorkspaceBuildArtifacts(config, cache, async () => {
builds += 1;
await writeOutputs(root, `build-${builds}`);
});
await ensureWorkspaceBuildArtifacts(config, cache, async () => {
builds += 1;
await writeOutputs(root, `build-${builds}`);
});
expect(builds).toBe(1);
expect(cache.report().entries.map((entry) => entry.status)).toEqual(["miss", "hit"]);
expect(cache.report().entries.map((entry) => entry.nodeId)).toEqual([
"win.workspace-build",
"win.workspace-build",
]);
expect(await readFile(join(root, "apps/packaged/dist/index.mjs"), "utf8")).toBe("build-1\n");
} finally {
await rm(root, { force: true, recursive: true });
}
});
it("writes a Windows version-family alias after a successful build", async () => {
const root = await mkdtemp(join(tmpdir(), "open-design-workspace-build-alias-"));
const cache = new ToolPackCache(join(root, ".cache"));
const config: ToolPackConfig = { ...createConfig(root, cache.root), appVersion: "0.9.1-beta.1" };
try {
await writeWorkspace(root);
await ensureWorkspaceBuildArtifacts(config, cache, async () => {
await writeOutputs(root, "build");
});
const aliasesRoot = join(cache.root, "aliases", "win.workspace-build");
const aliasBuckets = await readdir(aliasesRoot);
expect(aliasBuckets).toHaveLength(1);
expect(await readFile(join(aliasesRoot, aliasBuckets[0]!, "alias.json"), "utf8")).toContain("win.workspace-build");
} finally {
await rm(root, { force: true, recursive: true });
}
});
it("hoists standalone web peer deps with Windows-compatible directory links", async () => {
const root = await mkdtemp(join(tmpdir(), "open-design-workspace-build-peer-deps-"));
const cache = new ToolPackCache(join(root, ".cache"));
const config = createConfig(root, cache.root);
try {
await writeWorkspace(root);
await ensureWorkspaceBuildArtifacts(config, cache, async () => {
await writeOutputs(root, "build");
await writeStandalonePeerDeps(root);
});
expect(await readFile(join(root, "apps/web/.next/standalone/apps/web/node_modules/react/package.json"), "utf8"))
.toContain('"name": "react"');
expect(await readFile(join(root, "apps/web/.next/standalone/apps/web/node_modules/react-dom/package.json"), "utf8"))
.toContain('"name": "react-dom"');
expect(await readFile(join(root, "apps/web/.next/standalone/apps/web/node_modules/styled-jsx/package.json"), "utf8"))
.toContain('"name": "styled-jsx"');
} finally {
await rm(root, { force: true, recursive: true });
}
});
it("does not write a version-family alias for mac workspace builds", async () => {
const root = await mkdtemp(join(tmpdir(), "open-design-workspace-build-mac-alias-"));
const cache = new ToolPackCache(join(root, ".cache"));
const config: ToolPackConfig = { ...createConfig(root, cache.root), appVersion: "0.9.1-beta.1", platform: "mac" };
try {
await writeWorkspace(root);
await ensureWorkspaceBuildArtifacts(config, cache, async () => {
await writeOutputs(root, "build");
});
await expect(readdir(join(cache.root, "aliases", "mac.workspace-build"))).rejects.toThrow();
} finally {
await rm(root, { force: true, recursive: true });
}
});
it("materializes cached outputs when an expected workspace output is missing", async () => {
const root = await mkdtemp(join(tmpdir(), "open-design-workspace-build-stale-"));
const cache = new ToolPackCache(join(root, ".cache"));
const config = createConfig(root, cache.root);
let builds = 0;
try {
await writeWorkspace(root);
await ensureWorkspaceBuildArtifacts(config, cache, async () => {
builds += 1;
await writeOutputs(root, `build-${builds}`);
});
await rm(join(root, "apps/web/dist/sidecar/index.js"), { force: true });
await ensureWorkspaceBuildArtifacts(config, cache, async () => {
builds += 1;
await writeOutputs(root, `build-${builds}`);
});
expect(builds).toBe(1);
expect(cache.report().entries.map((entry) => entry.status)).toEqual(["miss", "hit"]);
expect(await readFile(join(root, "apps/web/dist/sidecar/index.js"), "utf8")).toBe("build-1\n");
} finally {
await rm(root, { force: true, recursive: true });
}
});
it("materializes cached internal package outputs for pack tarballs", async () => {
const root = await mkdtemp(join(tmpdir(), "open-design-workspace-build-package-cache-"));
const cache = new ToolPackCache(join(root, ".cache"));
const config = createConfig(root, cache.root);
let builds = 0;
try {
await writeWorkspace(root);
await ensureWorkspaceBuildArtifacts(config, cache, async () => {
builds += 1;
await writeOutputs(root, `build-${builds}`);
});
await rm(join(root, "packages/host/dist/index.mjs"), { force: true });
await ensureWorkspaceBuildArtifacts(config, cache, async () => {
builds += 1;
await writeOutputs(root, `build-${builds}`);
});
expect(builds).toBe(1);
expect(cache.report().entries.map((entry) => entry.status)).toEqual(["miss", "hit"]);
expect(await readFile(join(root, "packages/host/dist/index.mjs"), "utf8")).toBe("build-1\n");
} finally {
await rm(root, { force: true, recursive: true });
}
});
it("keeps platform-specific workspace build cache nodes separate", async () => {
const root = await mkdtemp(join(tmpdir(), "open-design-workspace-build-platform-"));
const cache = new ToolPackCache(join(root, ".cache"));
const winConfig = createConfig(root, cache.root);
const macConfig: ToolPackConfig = {
...winConfig,
platform: "mac",
roots: {
...winConfig.roots,
output: {
...winConfig.roots.output,
namespaceRoot: join(root, ".tmp", "out", "mac", "namespaces", "test"),
platformRoot: join(root, ".tmp", "out", "mac"),
},
runtime: {
namespaceBaseRoot: join(root, ".tmp", "runtime", "mac", "namespaces"),
namespaceRoot: join(root, ".tmp", "runtime", "mac", "namespaces", "test"),
},
},
};
try {
await writeWorkspace(root);
await ensureWorkspaceBuildArtifacts(winConfig, cache, async () => {
await writeOutputs(root, "win-build");
});
await ensureWorkspaceBuildArtifacts(macConfig, cache, async () => {
await writeOutputs(root, "mac-build");
});
expect(cache.report().entries.map((entry) => entry.nodeId)).toEqual([
"win.workspace-build",
"mac.workspace-build",
]);
expect(cache.report().entries.map((entry) => entry.status)).toEqual(["miss", "miss"]);
expect(await readFile(join(root, "apps/packaged/dist/index.mjs"), "utf8")).toBe("mac-build\n");
} finally {
await rm(root, { force: true, recursive: true });
}
});
});