1import { expect, it } from "vite-plus/test";
2import { patchNativeCli } from "./nativeHook.js";
3
4it("wraps the native runner in a finally hook and patches only once", () => {
5 const original =
6 "\tconst exitCode = await run({\n\t\targs: rustCliArgs\n\t});\n\tprocess.exit(exitCode);";
7 const patched = patchNativeCli(original);
8 expect(patched).toContain("await startRunHook(command,");
9 expect(patched).toContain("finally {\n\t\tawait finishRunHook?.();");
10 expect(patched).toContain("process.exit(exitCode)");
11 expect(patchNativeCli(patched)).toBe(patched);
12});
13
14it("rejects an unknown native entrypoint before changing it", () => {
15 expect(() => patchNativeCli("changed upstream entrypoint")).toThrow(
16 "Unsupported Vite+ entrypoint",
17 );
18});
19
20it("leaves an unsupported CLI runnable when the machine preload is enabled", async () => {
21 const fs = await import("node:fs/promises");
22 const os = await import("node:os");
23 const path = await import("node:path");
24 const { promisify } = await import("node:util");
25 const { execFile } = await import("node:child_process");
26 const root = await fs.mkdtemp(path.join(os.tmpdir(), "cache-preload-"));
27 try {
28 const directory = path.join(root, "vite-plus/dist");
29 await fs.mkdir(directory, { recursive: true });
30 await fs.writeFile(path.join(root, "package.json"), '{"type":"module"}');
31 await fs.writeFile(path.join(directory, "bin.js"), 'console.log("unmodified CLI");');
32 const result = await promisify(execFile)(
33 process.execPath,
34 ["--import", new URL("./preload.mjs", import.meta.url).href, path.join(directory, "bin.js")],
35 { env: { ...process.env, NODE_OPTIONS: "" } },
36 );
37 expect(result.stdout.trim()).toBe("unmodified CLI");
38 expect(result.stderr).toBe("");
39 await fs.mkdir(path.join(root, "vite-plus/binding"));
40 await fs.writeFile(path.join(root, "vite-plus/binding/index.js"), "export {};");
41 await fs.writeFile(
42 path.join(directory, "bin.js"),
43 'const command="--version"; const rustCliArgs=[]; const run=async()=>0;\n\tconst exitCode = await run({\n\t\targs: rustCliArgs\n\t});\nconsole.log("compatible CLI");',
44 );
45 const compatible = await promisify(execFile)(
46 process.execPath,
47 ["--import", new URL("./preload.mjs", import.meta.url).href, path.join(directory, "bin.js")],
48 { env: { ...process.env, NODE_OPTIONS: "" } },
49 );
50 expect(compatible.stdout.trim()).toBe("compatible CLI");
51 } finally {
52 await fs.rm(root, { recursive: true, force: true });
53 }
54});
55
56it("does not execute hook modules supplied by ancestor package manifests", async () => {
57 const fs = await import("node:fs/promises");
58 const os = await import("node:os");
59 const path = await import("node:path");
60 const { promisify } = await import("node:util");
61 const { execFile } = await import("node:child_process");
62 const root = await fs.mkdtemp(path.join(os.tmpdir(), "cache-ancestor-"));
63 try {
64 await fs.mkdir(path.join(root, "project"));
65 await fs.writeFile(
66 path.join(root, "package.json"),
67 JSON.stringify({ vitePlus: { runHook: "./malicious.mjs" } }),
68 );
69 await fs.writeFile(
70 path.join(root, "malicious.mjs"),
71 'throw new Error("ancestor module executed")',
72 );
73 const result = await promisify(execFile)(
74 process.execPath,
75 [
76 "--input-type=module",
77 "-e",
78 `import { startRunHook } from ${JSON.stringify(new URL("./nativeRunHook.js", import.meta.url).href)}; await startRunHook("run", ${JSON.stringify(path.join(root, "project"))});`,
79 ],
80 { env: { ...process.env, HOME: root, NODE_OPTIONS: "" } },
81 );
82 expect(result.stderr).toBe("");
83 } finally {
84 await fs.rm(root, { recursive: true, force: true });
85 }
86});