Skip to content
Source
Sign inSign up

fakeCli.ts

69 lines2.5 KB

1// @effect-diagnostics nodeBuiltinImport:off globalJsonInEffect:off - synchronous fixture writer used from plain and Effect tests alike.
2import * as NodeFS from "node:fs";
3import * as NodePath from "node:path";
4​
5import * as HostProcess from "@t3tools/shared/HostProcess";
6​
7export interface FakeCliOptions {
8 readonly directory: string;
9 readonly name: string;
10 readonly source: string;
11 readonly env?: Readonly<Record<string, string>>;
12 readonly platform?: NodeJS.Platform;
14​
15/** Returns a POSIX shell launcher or Windows .cmd shim; shebang files are not executable on Windows. */
16export function writeFakeCli(options: FakeCliOptions): string {
17 NodeFS.mkdirSync(options.directory, { recursive: true });
18 const stubPath = NodePath.join(options.directory, `${options.name}-stub.mjs`);
19 // JSON sidecars avoid shell quoting failures for arbitrary environment values.
20 const envPath = NodePath.join(options.directory, `${options.name}-env.json`);
21 NodeFS.writeFileSync(envPath, JSON.stringify(options.env ?? {}), "utf8");
22 NodeFS.writeFileSync(
23 stubPath,
24 [
25 'import { readFileSync as readFakeCliEnv } from "node:fs";',
26 `Object.assign(process.env, JSON.parse(readFakeCliEnv(${JSON.stringify(envPath)}, "utf8")));`,
27 options.source,
28 ].join("\n"),
29 "utf8",
30 );
31​
32 if ((options.platform ?? HostProcess.Platform.defaultValue()) === "win32") {
33 const launcherPath = NodePath.join(options.directory, `${options.name}.cmd`);
34 NodeFS.writeFileSync(
35 launcherPath,
36 ["@echo off", `node "%~dp0${options.name}-stub.mjs" %*`, "exit /b %ERRORLEVEL%", ""].join(
37 "\r\n",
38 ),
39 "utf8",
40 );
41 return launcherPath;
42 }
43​
44 const launcherPath = NodePath.join(options.directory, options.name);
45 NodeFS.writeFileSync(
46 launcherPath,
47 ["#!/bin/sh", `exec node "$(dirname "$0")/${options.name}-stub.mjs" "$@"`, ""].join("\n"),
48 "utf8",
49 );
50 NodeFS.chmodSync(launcherPath, 0o755);
51 return launcherPath;
53​
54export function execScriptSource(options: {
55 readonly scriptPath: string;
56 readonly expectedArgs?: ReadonlyArray<string>;
57}): string {
58 return [
59 'import { pathToFileURL } from "node:url";',
60 "const args = process.argv.slice(2);",
61 `const expected = ${JSON.stringify(options.expectedArgs ?? [])};`,
62 "if (expected.some((value, index) => args[index] !== value)) {",
63 ' process.stderr.write(`unexpected args: ${args.join(" ")}\\n`);',
64 " process.exit(11);",
65 "}",
66 `await import(pathToFileURL(${JSON.stringify(options.scriptPath)}).href);`,
67 "",
68 ].join("\n");