@rune-cli/rune 0.0.9 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/test.mjs CHANGED
@@ -1,42 +1,91 @@
1
- import { r as executeCommand, t as captureProcessOutput } from "./dist-DuisScgY.mjs";
2
- //#region src/test.ts
3
- /**
4
- * Runs a command definition directly in-process for testing.
5
- *
6
- * This helper bypasses Rune's CLI parser and validation layers. Callers
7
- * provide already-normalized `options` and `args` values, and the command's
8
- * `run` function is executed with those values injected into the context.
9
- *
10
- * All output written to `process.stdout`, `process.stderr`, and `console` is
11
- * captured and returned as strings so tests can assert on them.
12
- *
13
- * @param command - A command created with {@link defineCommand}.
14
- * @param options - Pre-validated options, args, cwd, and rawArgs to inject.
15
- * @returns The exit code, captured stdout/stderr, and an optional error message.
16
- *
17
- * @example
18
- * ```ts
19
- * import { defineCommand } from "rune";
20
- * import { runCommand } from "rune/test";
21
- * import { expect, test } from "vitest";
22
- *
23
- * const hello = defineCommand({
24
- * options: [{ name: "name", type: "string", required: true }],
25
- * run(ctx) {
26
- * console.log(`Hello, ${ctx.options.name}!`);
27
- * },
28
- * });
29
- *
30
- * test("hello command", async () => {
31
- * const result = await runCommand(hello, {
32
- * options: { name: "Rune" },
33
- * });
34
- *
35
- * expect(result.exitCode).toBe(0);
36
- * expect(result.stdout).toBe("Hello, Rune!\n");
37
- * });
38
- * ```
39
- */
1
+ import { format } from "node:util";
2
+ //#region ../test-utils/dist/index.mjs
3
+ async function captureProcessOutput(action) {
4
+ const stdoutChunks = [];
5
+ const stderrChunks = [];
6
+ const originalStdoutWrite = process.stdout.write.bind(process.stdout);
7
+ const originalStderrWrite = process.stderr.write.bind(process.stderr);
8
+ const originalConsoleMethods = {
9
+ log: console.log,
10
+ info: console.info,
11
+ debug: console.debug,
12
+ warn: console.warn,
13
+ error: console.error
14
+ };
15
+ const captureChunk = (chunks, chunk, encoding) => {
16
+ if (typeof chunk === "string") {
17
+ chunks.push(chunk);
18
+ return;
19
+ }
20
+ chunks.push(Buffer.from(chunk).toString(encoding));
21
+ };
22
+ const captureConsole = (chunks, args) => {
23
+ chunks.push(`${format(...args)}\n`);
24
+ };
25
+ const createWriteCapture = (chunks) => ((chunk, encoding, cb) => {
26
+ captureChunk(chunks, chunk, typeof encoding === "string" ? encoding : void 0);
27
+ if (typeof encoding === "function") encoding(null);
28
+ else cb?.(null);
29
+ return true;
30
+ });
31
+ process.stdout.write = createWriteCapture(stdoutChunks);
32
+ process.stderr.write = createWriteCapture(stderrChunks);
33
+ for (const method of [
34
+ "log",
35
+ "info",
36
+ "debug"
37
+ ]) console[method] = (...args) => captureConsole(stdoutChunks, args);
38
+ for (const method of ["warn", "error"]) console[method] = (...args) => captureConsole(stderrChunks, args);
39
+ try {
40
+ return {
41
+ ok: true,
42
+ value: await action(),
43
+ stdout: stdoutChunks.join(""),
44
+ stderr: stderrChunks.join("")
45
+ };
46
+ } catch (error) {
47
+ return {
48
+ ok: false,
49
+ error,
50
+ stdout: stdoutChunks.join(""),
51
+ stderr: stderrChunks.join("")
52
+ };
53
+ } finally {
54
+ process.stdout.write = originalStdoutWrite;
55
+ process.stderr.write = originalStderrWrite;
56
+ Object.assign(console, originalConsoleMethods);
57
+ }
58
+ }
59
+ function isSchemaField(field) {
60
+ return "schema" in field && field.schema !== void 0;
61
+ }
62
+ function formatExecutionError(error) {
63
+ if (error instanceof Error) return error.message === "" ? "" : error.message || error.name || "Unknown error";
64
+ if (typeof error === "string") return error;
65
+ return "Unknown error";
66
+ }
67
+ function createExecutionOptions(command, input) {
68
+ const options = { ...input.options };
69
+ for (const field of command.options) if (options[field.name] === void 0 && !isSchemaField(field) && field.type === "boolean") options[field.name] = false;
70
+ return options;
71
+ }
72
+ async function executeCommand(command, input = {}) {
73
+ try {
74
+ await command.run({
75
+ options: createExecutionOptions(command, input),
76
+ args: input.args ?? {},
77
+ cwd: input.cwd ?? process.cwd(),
78
+ rawArgs: input.rawArgs ?? []
79
+ });
80
+ return { exitCode: 0 };
81
+ } catch (error) {
82
+ const message = formatExecutionError(error);
83
+ return message ? {
84
+ exitCode: 1,
85
+ errorMessage: message
86
+ } : { exitCode: 1 };
87
+ }
88
+ }
40
89
  async function runCommand(command, options = {}) {
41
90
  const captured = await captureProcessOutput(() => executeCommand(command, options));
42
91
  if (!captured.ok) throw captured.error;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rune-cli/rune",
3
- "version": "0.0.9",
3
+ "version": "0.0.11",
4
4
  "description": "Rune is a CLI framework built around the concept of file-based command routing.",
5
5
  "homepage": "https://github.com/morinokami/rune#readme",
6
6
  "bugs": {
@@ -46,7 +46,8 @@
46
46
  "@typescript/native-preview": "7.0.0-dev.20260322.1",
47
47
  "typescript": "5.9.3",
48
48
  "vite-plus": "v0.1.13",
49
- "@rune-cli/core": "0.0.0"
49
+ "@rune-cli/core": "0.0.0",
50
+ "@rune-cli/test-utils": "0.0.0"
50
51
  },
51
52
  "peerDependencies": {
52
53
  "typescript": ">=5.0.0"