@executablemd/cli 0.3.1 → 0.4.1
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/esm/deno.js +13 -9
- package/esm/src/cli.js +57 -30
- package/package.json +4 -3
- package/types/deno.d.ts +4 -0
package/esm/deno.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
"name": "@executablemd/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"exports": "./src/cli.ts",
|
|
5
5
|
"imports": {
|
|
6
6
|
"@types/node": "npm:@types/node@^24.5.2",
|
|
@@ -10,10 +10,13 @@ export default {
|
|
|
10
10
|
"@effectionx/test-adapter": "npm:@effectionx/test-adapter@0.7.4",
|
|
11
11
|
"effection": "npm:effection@4.1.0-alpha.7",
|
|
12
12
|
"effection/experimental": "npm:effection@4.1.0-alpha.7/experimental",
|
|
13
|
-
"@executablemd/durable-streams": "npm:@executablemd/durable-streams@^0.
|
|
14
|
-
"@executablemd/runtime": "npm:@executablemd/runtime@^0.
|
|
13
|
+
"@executablemd/durable-streams": "npm:@executablemd/durable-streams@^0.4.1",
|
|
14
|
+
"@executablemd/runtime": "npm:@executablemd/runtime@^0.4.1",
|
|
15
|
+
"@executablemd/testing": "npm:@executablemd/testing@^0.4.1",
|
|
16
|
+
"@std/assert": "jsr:@std/assert@^1.0.17",
|
|
17
|
+
"ajv": "npm:ajv@^8.17.1",
|
|
15
18
|
"@executablemd/runtime/test": "file:///home/runner/work/executable.md/executable.md/runtime/test/mod.ts",
|
|
16
|
-
"@executablemd/code-review-agent": "npm:@executablemd/code-review-agent@^0.
|
|
19
|
+
"@executablemd/code-review-agent": "npm:@executablemd/code-review-agent@^0.4.1",
|
|
17
20
|
"@effectionx/context-api": "npm:@effectionx/context-api@0.6.0",
|
|
18
21
|
"@effectionx/converge": "npm:@effectionx/converge@0.1.4",
|
|
19
22
|
"@effectionx/fetch": "npm:@effectionx/fetch@0.2.0",
|
|
@@ -32,10 +35,11 @@ export default {
|
|
|
32
35
|
"oxlint-tsgolint": "npm:oxlint-tsgolint@^0.17",
|
|
33
36
|
"unist-util-select": "npm:unist-util-select@^5",
|
|
34
37
|
"mdast-util-to-string": "npm:mdast-util-to-string@^4",
|
|
35
|
-
"@executablemd/core": "npm:@executablemd/core@^0.
|
|
36
|
-
"@executablemd/core/": "npm:@executablemd/core@^0.
|
|
37
|
-
"@executablemd/durable-streams/": "npm:@executablemd/durable-streams@^0.
|
|
38
|
-
"@executablemd/runtime/": "npm:@executablemd/runtime@^0.
|
|
39
|
-
"@executablemd/
|
|
38
|
+
"@executablemd/core": "npm:@executablemd/core@^0.4.1",
|
|
39
|
+
"@executablemd/core/": "npm:@executablemd/core@^0.4.1/",
|
|
40
|
+
"@executablemd/durable-streams/": "npm:@executablemd/durable-streams@^0.4.1/",
|
|
41
|
+
"@executablemd/runtime/": "npm:@executablemd/runtime@^0.4.1/",
|
|
42
|
+
"@executablemd/testing/": "npm:@executablemd/testing@^0.4.1/",
|
|
43
|
+
"@executablemd/code-review-agent/": "npm:@executablemd/code-review-agent@^0.4.1/"
|
|
40
44
|
}
|
|
41
45
|
};
|
package/esm/src/cli.js
CHANGED
|
@@ -19,16 +19,11 @@ import { inspect } from "node:util";
|
|
|
19
19
|
import process from "node:process";
|
|
20
20
|
import { program, object, field, cli, commands } from "configliere";
|
|
21
21
|
import { z } from "zod";
|
|
22
|
-
import {
|
|
22
|
+
import { execute, useNormalizedOutput, useTerminalOutput } from "@executablemd/core";
|
|
23
|
+
import { installTestingVocabulary, TestFailureError, useTesting } from "@executablemd/testing";
|
|
23
24
|
import { FileStream } from "./file-stream.js";
|
|
24
25
|
import denoJson from "../deno.js";
|
|
25
|
-
// ---------------------------------------------------------------------------
|
|
26
|
-
// Workaround: field.default exists at runtime but is missing from the .d.ts
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
26
|
const defaults = (value) => (mods) => ({ ...mods, default: value });
|
|
29
|
-
// ---------------------------------------------------------------------------
|
|
30
|
-
// Program schema
|
|
31
|
-
// ---------------------------------------------------------------------------
|
|
32
27
|
const runConfig = object({
|
|
33
28
|
docPath: {
|
|
34
29
|
description: "markdown document to execute",
|
|
@@ -53,14 +48,35 @@ const runConfig = object({
|
|
|
53
48
|
...field(z.boolean(), defaults(false)),
|
|
54
49
|
},
|
|
55
50
|
});
|
|
51
|
+
const testConfig = object({
|
|
52
|
+
docPath: {
|
|
53
|
+
description: "markdown document to test",
|
|
54
|
+
...field(z.string(), cli.argument()),
|
|
55
|
+
},
|
|
56
|
+
componentDir: {
|
|
57
|
+
description: "component search directory",
|
|
58
|
+
...field(z.array(z.string()), defaults(["components", "."]), field.array()),
|
|
59
|
+
},
|
|
60
|
+
verbose: {
|
|
61
|
+
description: "log journal entries to stderr",
|
|
62
|
+
aliases: ["-V"],
|
|
63
|
+
...field(z.boolean(), defaults(false)),
|
|
64
|
+
},
|
|
65
|
+
journal: {
|
|
66
|
+
description: "write a diagnostic JSONL trace (path must not exist)",
|
|
67
|
+
aliases: ["-j"],
|
|
68
|
+
...field(z.string().optional()),
|
|
69
|
+
},
|
|
70
|
+
raw: {
|
|
71
|
+
description: "output raw markdown without normalization or terminal formatting",
|
|
72
|
+
...field(z.boolean(), defaults(false)),
|
|
73
|
+
},
|
|
74
|
+
});
|
|
56
75
|
const xmd = program({
|
|
57
76
|
name: "xmd",
|
|
58
77
|
version: denoJson.version,
|
|
59
|
-
config: commands({ run: runConfig }, { default: "run" }),
|
|
78
|
+
config: commands({ run: runConfig, test: testConfig }, { default: "run" }),
|
|
60
79
|
});
|
|
61
|
-
// ---------------------------------------------------------------------------
|
|
62
|
-
// Journal entry formatting
|
|
63
|
-
// ---------------------------------------------------------------------------
|
|
64
80
|
const pretty = (value) => inspect(value, {
|
|
65
81
|
colors: true,
|
|
66
82
|
compact: true,
|
|
@@ -70,8 +86,9 @@ const pretty = (value) => inspect(value, {
|
|
|
70
86
|
});
|
|
71
87
|
function formatYieldResult(event) {
|
|
72
88
|
const { result, description } = event;
|
|
73
|
-
if (result.status !== "ok" || result.value === undefined)
|
|
89
|
+
if (result.status !== "ok" || result.value === undefined) {
|
|
74
90
|
return "";
|
|
91
|
+
}
|
|
75
92
|
const v = result.value;
|
|
76
93
|
switch (description.type) {
|
|
77
94
|
case "import_component":
|
|
@@ -97,9 +114,6 @@ function summarizeEvent(event) {
|
|
|
97
114
|
const detail = status === "err" && "error" in event.result ? ` (${event.result.error.message})` : "";
|
|
98
115
|
return `[close] ${event.coroutineId} → ${status}${detail}`;
|
|
99
116
|
}
|
|
100
|
-
// ---------------------------------------------------------------------------
|
|
101
|
-
// Document runner
|
|
102
|
-
// ---------------------------------------------------------------------------
|
|
103
117
|
function* createJournalFile(filePath) {
|
|
104
118
|
let handle;
|
|
105
119
|
try {
|
|
@@ -115,7 +129,7 @@ function* createJournalFile(filePath) {
|
|
|
115
129
|
}
|
|
116
130
|
yield* until(handle.close());
|
|
117
131
|
}
|
|
118
|
-
function* run(config) {
|
|
132
|
+
function* run(config, mode) {
|
|
119
133
|
const { docPath, componentDir, verbose, journal, raw } = config;
|
|
120
134
|
// Every CLI invocation starts from an empty stream. --journal writes
|
|
121
135
|
// current-run diagnostics only; existing traces are never loaded.
|
|
@@ -147,22 +161,23 @@ function* run(config) {
|
|
|
147
161
|
}
|
|
148
162
|
})
|
|
149
163
|
: spawn(function* () { });
|
|
150
|
-
// ---------------------------------------------------------------------------
|
|
151
|
-
// Output middleware (spec §9).
|
|
152
|
-
//
|
|
153
|
-
// Middleware is installed on the DocumentOutput Api via Api.around() before
|
|
154
|
-
// runDocument is called. runDocument owns the channel internally —
|
|
155
|
-
// the CLI just installs transformations and consumes the returned stream.
|
|
156
|
-
// ---------------------------------------------------------------------------
|
|
157
164
|
if (!raw) {
|
|
158
165
|
yield* useNormalizedOutput();
|
|
159
166
|
}
|
|
160
167
|
if (process.stdout.isTTY && !raw) {
|
|
161
168
|
yield* useTerminalOutput();
|
|
162
169
|
}
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
|
|
170
|
+
// Compose testing around the single core execution entrypoint: both
|
|
171
|
+
// commands register the vocabulary (assertions work in regular documents,
|
|
172
|
+
// explicit <Testing> boundaries affect the outcome), while `xmd test`
|
|
173
|
+
// additionally activates root testing through a useTesting() session.
|
|
174
|
+
if (mode.testing) {
|
|
175
|
+
yield* useTesting({ verbose });
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
yield* installTestingVocabulary({ verbose });
|
|
179
|
+
}
|
|
180
|
+
const execution = yield* execute({
|
|
166
181
|
docPath,
|
|
167
182
|
stream,
|
|
168
183
|
componentDirs: componentDir,
|
|
@@ -184,10 +199,19 @@ function* run(config) {
|
|
|
184
199
|
signal.close();
|
|
185
200
|
yield* writer;
|
|
186
201
|
}
|
|
202
|
+
// Inspect the completion Result AFTER the report finished streaming:
|
|
203
|
+
// test failures, assertion aborts, and any document abort exit nonzero.
|
|
204
|
+
const result = yield* execution;
|
|
205
|
+
if (!result.ok) {
|
|
206
|
+
if (result.error instanceof TestFailureError) {
|
|
207
|
+
console.error(`\ntests failed: ${result.error.message}`);
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
console.error(result.error.message);
|
|
211
|
+
}
|
|
212
|
+
yield* exit(1);
|
|
213
|
+
}
|
|
187
214
|
}
|
|
188
|
-
// ---------------------------------------------------------------------------
|
|
189
|
-
// Entry point
|
|
190
|
-
// ---------------------------------------------------------------------------
|
|
191
215
|
await main(function* (args) {
|
|
192
216
|
const parser = xmd.createParser({ args });
|
|
193
217
|
switch (parser.type) {
|
|
@@ -208,7 +232,10 @@ await main(function* (args) {
|
|
|
208
232
|
}
|
|
209
233
|
switch (parsed.value.name) {
|
|
210
234
|
case "run":
|
|
211
|
-
yield* run(parsed.value.config);
|
|
235
|
+
yield* run(parsed.value.config, { testing: false });
|
|
236
|
+
break;
|
|
237
|
+
case "test":
|
|
238
|
+
yield* run(parsed.value.config, { testing: true });
|
|
212
239
|
break;
|
|
213
240
|
}
|
|
214
241
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@executablemd/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "The xmd command-line interface for executable.md.",
|
|
5
5
|
"homepage": "https://executable.md",
|
|
6
6
|
"repository": {
|
|
@@ -17,8 +17,9 @@
|
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@effectionx/stream-helpers": "0.8.3",
|
|
20
|
-
"@executablemd/core": "^0.
|
|
21
|
-
"@executablemd/durable-streams": "^0.
|
|
20
|
+
"@executablemd/core": "^0.4.1",
|
|
21
|
+
"@executablemd/durable-streams": "^0.4.1",
|
|
22
|
+
"@executablemd/testing": "^0.4.1",
|
|
22
23
|
"configliere": "^0.2.3",
|
|
23
24
|
"effection": "4.1.0-alpha.7",
|
|
24
25
|
"zod": "^4.3.6"
|
package/types/deno.d.ts
CHANGED
|
@@ -12,6 +12,9 @@ declare namespace _default {
|
|
|
12
12
|
"effection/experimental": string;
|
|
13
13
|
"@executablemd/durable-streams": string;
|
|
14
14
|
"@executablemd/runtime": string;
|
|
15
|
+
"@executablemd/testing": string;
|
|
16
|
+
"@std/assert": string;
|
|
17
|
+
ajv: string;
|
|
15
18
|
"@executablemd/runtime/test": string;
|
|
16
19
|
"@executablemd/code-review-agent": string;
|
|
17
20
|
"@effectionx/context-api": string;
|
|
@@ -36,6 +39,7 @@ declare namespace _default {
|
|
|
36
39
|
"@executablemd/core/": string;
|
|
37
40
|
"@executablemd/durable-streams/": string;
|
|
38
41
|
"@executablemd/runtime/": string;
|
|
42
|
+
"@executablemd/testing/": string;
|
|
39
43
|
"@executablemd/code-review-agent/": string;
|
|
40
44
|
};
|
|
41
45
|
}
|