mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
Plugin discovery, esbuild builds (optional peer dependency), vm evaluation with pluginFile and mapStack, dependency installs, a watcher with dirty marks and rollout across hosts; host.load and host.reload per plugin with the closure rule; examples 35-39 as folders with real plugin directories.
62 lines
2.9 KiB
TypeScript
62 lines
2.9 KiB
TypeScript
// Tests of installing a plugin's dependencies (slice 7c §5): the npm arguments, run in the plugin's real directory,
|
|
// the command overridable; a failure and an abort reject.
|
|
|
|
import { readFile, realpath, symlink } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
import { BACKGROUND_CONTEXT as context, withCancel } from "../../../src/context/index.ts";
|
|
import { discoverPlugins, installDependencies } from "../../../src/next-node/plugins/index.ts";
|
|
import { rejection } from "../../next/support.ts";
|
|
import { KINDS, tempRoot, writeFiles, writePlugin } from "./support.ts";
|
|
|
|
/** A fake npm (a Node script) that records its arguments and directory, and exits with `code` after `delayMs`. */
|
|
async function fakeNpm(dir: string, code: number, delayMs = 0): Promise<readonly string[]> {
|
|
const script = join(dir, "fake-npm.mjs");
|
|
await writeFiles(dir, {
|
|
"fake-npm.mjs": [
|
|
'import { writeFileSync } from "node:fs";',
|
|
`writeFileSync(${JSON.stringify(join(dir, "record.json"))}, JSON.stringify({ args: process.argv.slice(2), cwd: process.cwd() }));`,
|
|
`setTimeout(() => { if (${code} !== 0) console.error("npm said no"); process.exit(${code}); }, ${delayMs});`,
|
|
].join("\n"),
|
|
});
|
|
return [process.execPath, script];
|
|
}
|
|
|
|
describe("installDependencies", () => {
|
|
it("runs npm install without dev dependencies, peers, scripts, audit or funding, in the plugin's real directory", async () => {
|
|
const tools = await tempRoot();
|
|
const elsewhere = await tempRoot();
|
|
const root = await tempRoot();
|
|
const dir = await writePlugin(
|
|
elsewhere,
|
|
"deps",
|
|
"@test/deps",
|
|
{ "worker.ts": "" },
|
|
{ dependencies: { a: "1.0.0" } },
|
|
);
|
|
await symlink(dir, join(root, "deps"));
|
|
const { plugins } = await discoverPlugins([root], { kinds: KINDS }, context);
|
|
await installDependencies(plugins[0]!, context, { npmCommand: await fakeNpm(tools, 0) });
|
|
expect(JSON.parse(await readFile(join(tools, "record.json"), "utf8"))).toEqual({
|
|
args: ["install", "--omit=dev", "--legacy-peer-deps", "--ignore-scripts", "--no-audit", "--no-fund"],
|
|
cwd: await realpath(dir),
|
|
});
|
|
});
|
|
|
|
it("rejects with the command's output when it fails, and when the context aborts", async () => {
|
|
const tools = await tempRoot();
|
|
const root = await tempRoot();
|
|
await writePlugin(root, "deps", "@test/deps", {});
|
|
const { plugins } = await discoverPlugins([root], { kinds: KINDS }, context);
|
|
const plugin = plugins[0]!;
|
|
expect(
|
|
await rejection(installDependencies(plugin, context, { npmCommand: await fakeNpm(tools, 1) })),
|
|
).toMatchObject({
|
|
message: expect.stringMatching(/^plugin "@test\/deps": .* install failed \(exit 1\): npm said no$/),
|
|
});
|
|
const { context: aborted, cancel } = withCancel(context);
|
|
const running = installDependencies(plugin, aborted, { npmCommand: await fakeNpm(tools, 0, 5_000) });
|
|
setTimeout(() => cancel(new Error("stop")), 50);
|
|
expect(await rejection(running)).toMatchObject({ name: "AbortError" });
|
|
});
|
|
});
|