@flux-lang/cli 0.1.3 → 0.1.4-canary.305338c5e
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/README.md +43 -4
- package/dist/bin/flux.js +1214 -200
- package/dist/ui-routing.js +5 -0
- package/dist/version.js +1 -1
- package/dist/view/runViewer.js +115 -0
- package/package.json +8 -3
package/dist/bin/flux.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import path from "node:path";
|
|
2
3
|
import fs from "node:fs/promises";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { parseCommand, checkCommand, renderCommand, tickCommand, stepCommand, viewCommand, pdfCommand, configCommand, newCommand, addCommand, updateRecents, resolveConfig, formatCommand, } from "@flux-lang/cli-core";
|
|
6
|
+
import { runViewer } from "../view/runViewer.js";
|
|
7
|
+
import { createRuntime, parseDocument } from "@flux-lang/core";
|
|
8
|
+
import { shouldLaunchUi } from "../ui-routing.js";
|
|
9
|
+
import { computeBuildId, defaultEmbeddedDir, VIEWER_VERSION as VIEWER_PACKAGE_VERSION, } from "@flux-lang/viewer";
|
|
10
|
+
import { FLUX_CLI_VERSION } from "../version.js";
|
|
11
|
+
const CLI_VERSION = FLUX_CLI_VERSION;
|
|
7
12
|
void (async () => {
|
|
8
13
|
try {
|
|
9
14
|
const code = await main(process.argv.slice(2));
|
|
10
|
-
if (code !== 0)
|
|
15
|
+
if (code !== 0)
|
|
11
16
|
process.exit(code);
|
|
12
|
-
}
|
|
13
17
|
}
|
|
14
18
|
catch (error) {
|
|
15
19
|
const msg = error?.message ?? String(error);
|
|
@@ -18,64 +22,227 @@ void (async () => {
|
|
|
18
22
|
}
|
|
19
23
|
})();
|
|
20
24
|
async function main(argv) {
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
const parsed = parseGlobalArgs(argv);
|
|
26
|
+
const args = parsed.args;
|
|
27
|
+
const uiEnabled = shouldLaunchUi({
|
|
28
|
+
stdoutIsTTY: Boolean(process.stdout.isTTY),
|
|
29
|
+
stdinIsTTY: process.stdin.isTTY,
|
|
30
|
+
json: parsed.json,
|
|
31
|
+
noUi: parsed.noUi,
|
|
32
|
+
env: process.env,
|
|
33
|
+
});
|
|
34
|
+
if (uiEnabled) {
|
|
35
|
+
return launchUi({
|
|
36
|
+
cwd: process.cwd(),
|
|
37
|
+
initialArgs: args,
|
|
38
|
+
detach: parsed.detach,
|
|
39
|
+
helpCommand: parsed.help ? args[0] : undefined,
|
|
40
|
+
version: parsed.version ? `flux v${CLI_VERSION}` : undefined,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
if (parsed.version) {
|
|
44
|
+
await printVersion(parsed.json);
|
|
23
45
|
return 0;
|
|
24
46
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const idx = argv.findIndex((a) => a === "-h" || a === "--help");
|
|
28
|
-
// No subcommand yet → global
|
|
29
|
-
if (idx === 0) {
|
|
47
|
+
if (parsed.help) {
|
|
48
|
+
if (args.length === 0) {
|
|
30
49
|
printGlobalHelp();
|
|
31
50
|
return 0;
|
|
32
51
|
}
|
|
33
|
-
|
|
34
|
-
if (cmd === "parse") {
|
|
35
|
-
printParseHelp();
|
|
36
|
-
}
|
|
37
|
-
else if (cmd === "check") {
|
|
38
|
-
printCheckHelp();
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
printGlobalHelp();
|
|
42
|
-
}
|
|
52
|
+
printCommandHelp(args[0]);
|
|
43
53
|
return 0;
|
|
44
54
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
console.log(`flux v${VERSION}`);
|
|
55
|
+
if (args.length === 0) {
|
|
56
|
+
printGlobalHelp();
|
|
48
57
|
return 0;
|
|
49
58
|
}
|
|
50
|
-
const [cmd, ...rest] =
|
|
51
|
-
|
|
52
|
-
|
|
59
|
+
const [cmd, ...rest] = args;
|
|
60
|
+
switch (cmd) {
|
|
61
|
+
case "parse":
|
|
62
|
+
return runParse(rest);
|
|
63
|
+
case "check":
|
|
64
|
+
return runCheck(rest, parsed);
|
|
65
|
+
case "render":
|
|
66
|
+
return runRender(rest);
|
|
67
|
+
case "fmt":
|
|
68
|
+
return runFormat(rest, parsed);
|
|
69
|
+
case "tick":
|
|
70
|
+
return runTick(rest);
|
|
71
|
+
case "step":
|
|
72
|
+
return runStep(rest);
|
|
73
|
+
case "view":
|
|
74
|
+
return runView(rest, parsed);
|
|
75
|
+
case "edit":
|
|
76
|
+
return runEdit(rest, parsed);
|
|
77
|
+
case "pdf":
|
|
78
|
+
return runPdf(rest, parsed);
|
|
79
|
+
case "config":
|
|
80
|
+
return runConfig(rest, parsed);
|
|
81
|
+
case "new":
|
|
82
|
+
return runNew(rest, parsed);
|
|
83
|
+
case "add":
|
|
84
|
+
return runAdd(rest, parsed);
|
|
85
|
+
default:
|
|
86
|
+
console.error(`Unknown command '${cmd}'.`);
|
|
87
|
+
printGlobalHelp();
|
|
88
|
+
return 1;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function isInteractive() {
|
|
92
|
+
return Boolean(process.stdout.isTTY && process.stdin.isTTY);
|
|
93
|
+
}
|
|
94
|
+
function parseGlobalArgs(argv) {
|
|
95
|
+
const flags = {
|
|
96
|
+
help: false,
|
|
97
|
+
version: false,
|
|
98
|
+
noUi: false,
|
|
99
|
+
ui: false,
|
|
100
|
+
detach: false,
|
|
101
|
+
json: false,
|
|
102
|
+
quiet: false,
|
|
103
|
+
verbose: false,
|
|
104
|
+
};
|
|
105
|
+
const args = [];
|
|
106
|
+
let passthrough = false;
|
|
107
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
108
|
+
const arg = argv[i];
|
|
109
|
+
if (arg === "--") {
|
|
110
|
+
passthrough = true;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (!passthrough) {
|
|
114
|
+
if (arg === "-h" || arg === "--help") {
|
|
115
|
+
flags.help = true;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (arg === "-v" || arg === "--version") {
|
|
119
|
+
flags.version = true;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (arg === "--no-ui") {
|
|
123
|
+
flags.noUi = true;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (arg === "--ui") {
|
|
127
|
+
flags.ui = true;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (arg === "--detach") {
|
|
131
|
+
flags.detach = true;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (arg === "--json") {
|
|
135
|
+
flags.json = true;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (arg === "--quiet" || arg === "-q") {
|
|
139
|
+
flags.quiet = true;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (arg === "--verbose" || arg === "-V") {
|
|
143
|
+
flags.verbose = true;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
args.push(arg);
|
|
148
|
+
}
|
|
149
|
+
return { ...flags, args };
|
|
150
|
+
}
|
|
151
|
+
async function collectComponentVersions() {
|
|
152
|
+
let editorBuildId = null;
|
|
153
|
+
try {
|
|
154
|
+
const embedded = defaultEmbeddedDir();
|
|
155
|
+
const indexPath = path.join(embedded, "index.html");
|
|
156
|
+
editorBuildId = await computeBuildId(embedded, indexPath);
|
|
53
157
|
}
|
|
54
|
-
|
|
55
|
-
|
|
158
|
+
catch {
|
|
159
|
+
editorBuildId = null;
|
|
56
160
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
161
|
+
return {
|
|
162
|
+
cli: CLI_VERSION,
|
|
163
|
+
viewer: VIEWER_PACKAGE_VERSION,
|
|
164
|
+
editorBuildId,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
async function printVersion(asJson) {
|
|
168
|
+
const info = await collectComponentVersions();
|
|
169
|
+
if (asJson) {
|
|
170
|
+
console.log(JSON.stringify(info));
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
console.log(`cli ${info.cli}`);
|
|
174
|
+
console.log(`viewer ${info.viewer}`);
|
|
175
|
+
console.log(`editor ${info.editorBuildId ?? "unknown"}`);
|
|
176
|
+
}
|
|
177
|
+
function printCommandHelp(cmd) {
|
|
178
|
+
if (cmd === "parse")
|
|
179
|
+
return printParseHelp();
|
|
180
|
+
if (cmd === "check")
|
|
181
|
+
return printCheckHelp();
|
|
182
|
+
if (cmd === "render")
|
|
183
|
+
return printRenderHelp();
|
|
184
|
+
if (cmd === "fmt")
|
|
185
|
+
return printFormatHelp();
|
|
186
|
+
if (cmd === "tick")
|
|
187
|
+
return printTickHelp();
|
|
188
|
+
if (cmd === "step")
|
|
189
|
+
return printStepHelp();
|
|
190
|
+
if (cmd === "view")
|
|
191
|
+
return printViewHelp();
|
|
192
|
+
if (cmd === "edit")
|
|
193
|
+
return printEditHelp();
|
|
194
|
+
if (cmd === "pdf")
|
|
195
|
+
return printPdfHelp();
|
|
196
|
+
if (cmd === "config")
|
|
197
|
+
return printConfigHelp();
|
|
198
|
+
if (cmd === "new")
|
|
199
|
+
return printNewHelp();
|
|
200
|
+
if (cmd === "add")
|
|
201
|
+
return printAddHelp();
|
|
202
|
+
return printGlobalHelp();
|
|
60
203
|
}
|
|
61
|
-
/* -------------------------------------------------------------------------- */
|
|
62
|
-
/* Help text */
|
|
63
|
-
/* -------------------------------------------------------------------------- */
|
|
64
204
|
function printGlobalHelp() {
|
|
65
205
|
console.log([
|
|
66
|
-
`Flux CLI v${
|
|
206
|
+
`Flux CLI v${CLI_VERSION}`,
|
|
67
207
|
"",
|
|
68
208
|
"Usage:",
|
|
209
|
+
" flux (launch UI in TTY)",
|
|
69
210
|
" flux parse [options] <files...>",
|
|
70
211
|
" flux check [options] <files...>",
|
|
212
|
+
" flux render [options] <file>",
|
|
213
|
+
" flux fmt <file>",
|
|
214
|
+
" flux tick [options] <file>",
|
|
215
|
+
" flux step [options] <file>",
|
|
216
|
+
" flux view <file>",
|
|
217
|
+
" flux edit <file>",
|
|
218
|
+
" flux pdf <file> --out <file.pdf>",
|
|
219
|
+
" flux config [set <key> <value>]",
|
|
220
|
+
" flux new <template> [options]",
|
|
221
|
+
" flux add <kind> [options]",
|
|
71
222
|
"",
|
|
72
223
|
"Commands:",
|
|
73
224
|
" parse Parse Flux source files and print their IR as JSON.",
|
|
74
225
|
" check Parse and run basic static checks.",
|
|
226
|
+
" render Render a Flux document to canonical Render IR JSON.",
|
|
227
|
+
" fmt Format a Flux document in-place.",
|
|
228
|
+
" tick Advance time and render the updated document.",
|
|
229
|
+
" step Advance docsteps and render the updated document.",
|
|
230
|
+
" view View a Flux document in a local web preview.",
|
|
231
|
+
" edit Edit a Flux document in the local web editor.",
|
|
232
|
+
" pdf Export a Flux document snapshot to PDF.",
|
|
233
|
+
" config View or edit configuration.",
|
|
234
|
+
" new Create a new Flux document.",
|
|
235
|
+
" add Apply structured edits to a Flux document.",
|
|
75
236
|
"",
|
|
76
237
|
"Global options:",
|
|
77
238
|
" -h, --help Show this help message.",
|
|
78
239
|
" -v, --version Show CLI version.",
|
|
240
|
+
" --no-ui Disable Ink UI launch.",
|
|
241
|
+
" --ui Force Ink UI launch (TTY only).",
|
|
242
|
+
" --detach Keep viewer running on UI exit.",
|
|
243
|
+
" --json Emit machine-readable JSON where applicable.",
|
|
244
|
+
" -q, --quiet Reduce non-essential output.",
|
|
245
|
+
" -V, --verbose Show verbose logs.",
|
|
79
246
|
"",
|
|
80
247
|
].join("\n"));
|
|
81
248
|
}
|
|
@@ -110,214 +277,1061 @@ function printCheckHelp() {
|
|
|
110
277
|
"",
|
|
111
278
|
].join("\n"));
|
|
112
279
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
280
|
+
function printRenderHelp() {
|
|
281
|
+
console.log([
|
|
282
|
+
"Usage:",
|
|
283
|
+
" flux render [options] <file>",
|
|
284
|
+
"",
|
|
285
|
+
"Description:",
|
|
286
|
+
" Render a Flux document to canonical Render IR JSON.",
|
|
287
|
+
"",
|
|
288
|
+
"Options:",
|
|
289
|
+
" --format ir Output format. (required; currently only 'ir')",
|
|
290
|
+
" --seed N Deterministic RNG seed (default: 0).",
|
|
291
|
+
" --time T Render time in seconds (default: 0).",
|
|
292
|
+
" --docstep D Render at docstep D (default: 0).",
|
|
293
|
+
" -h, --help Show this message.",
|
|
294
|
+
"",
|
|
295
|
+
].join("\n"));
|
|
296
|
+
}
|
|
297
|
+
function printFormatHelp() {
|
|
298
|
+
console.log([
|
|
299
|
+
"Usage:",
|
|
300
|
+
" flux fmt <file>",
|
|
301
|
+
"",
|
|
302
|
+
"Description:",
|
|
303
|
+
" Apply a minimal formatter to a Flux document.",
|
|
304
|
+
"",
|
|
305
|
+
"Options:",
|
|
306
|
+
" -h, --help Show this message.",
|
|
307
|
+
"",
|
|
308
|
+
].join("\n"));
|
|
309
|
+
}
|
|
310
|
+
function printTickHelp() {
|
|
311
|
+
console.log([
|
|
312
|
+
"Usage:",
|
|
313
|
+
" flux tick [options] <file>",
|
|
314
|
+
"",
|
|
315
|
+
"Description:",
|
|
316
|
+
" Advance time by a number of seconds and render the updated IR.",
|
|
317
|
+
"",
|
|
318
|
+
"Options:",
|
|
319
|
+
" --seconds S Seconds to advance time by.",
|
|
320
|
+
" --seed N Deterministic RNG seed (default: 0).",
|
|
321
|
+
" -h, --help Show this message.",
|
|
322
|
+
"",
|
|
323
|
+
].join("\n"));
|
|
324
|
+
}
|
|
325
|
+
function printStepHelp() {
|
|
326
|
+
console.log([
|
|
327
|
+
"Usage:",
|
|
328
|
+
" flux step [options] <file>",
|
|
329
|
+
"",
|
|
330
|
+
"Description:",
|
|
331
|
+
" Advance docsteps and render the updated IR.",
|
|
332
|
+
"",
|
|
333
|
+
"Options:",
|
|
334
|
+
" --n N Docsteps to advance by (default: 1).",
|
|
335
|
+
" --seed N Deterministic RNG seed (default: 0).",
|
|
336
|
+
" -h, --help Show this message.",
|
|
337
|
+
"",
|
|
338
|
+
].join("\n"));
|
|
339
|
+
}
|
|
340
|
+
function printViewHelp() {
|
|
341
|
+
console.log([
|
|
342
|
+
"Usage:",
|
|
343
|
+
" flux view [options] <file>",
|
|
344
|
+
"",
|
|
345
|
+
"Description:",
|
|
346
|
+
" Open a local web viewer for a Flux document.",
|
|
347
|
+
"",
|
|
348
|
+
"Options:",
|
|
349
|
+
" --port <n> Port for the local server (default: auto).",
|
|
350
|
+
" --docstep-ms <n> Docstep interval in milliseconds.",
|
|
351
|
+
" --time-rate <n> Time multiplier for viewer ticks (default: 1).",
|
|
352
|
+
" --seed <n> Seed for deterministic rendering.",
|
|
353
|
+
" --allow-net <orig> Allow remote assets for origin (repeatable or comma-separated).",
|
|
354
|
+
" --editor-dist <p> Serve editor assets from this dist folder.",
|
|
355
|
+
" --no-time Disable automatic time advancement.",
|
|
356
|
+
" --tty Use the legacy TTY grid viewer.",
|
|
357
|
+
" -h, --help Show this message.",
|
|
358
|
+
"",
|
|
359
|
+
].join("\n"));
|
|
360
|
+
}
|
|
361
|
+
function printEditHelp() {
|
|
362
|
+
console.log([
|
|
363
|
+
"Usage:",
|
|
364
|
+
" flux edit [options] <file>",
|
|
365
|
+
"",
|
|
366
|
+
"Description:",
|
|
367
|
+
" Open the local web editor for a Flux document.",
|
|
368
|
+
"",
|
|
369
|
+
"Options:",
|
|
370
|
+
" --port <n> Port for the local server (default: auto).",
|
|
371
|
+
" --docstep-ms <n> Docstep interval in milliseconds.",
|
|
372
|
+
" --time-rate <n> Time multiplier for viewer ticks (default: 1).",
|
|
373
|
+
" --seed <n> Seed for deterministic rendering.",
|
|
374
|
+
" --allow-net <orig> Allow remote assets for origin (repeatable or comma-separated).",
|
|
375
|
+
" --editor-dist <p> Serve editor assets from this dist folder.",
|
|
376
|
+
" --no-time Disable automatic time advancement.",
|
|
377
|
+
" -h, --help Show this message.",
|
|
378
|
+
"",
|
|
379
|
+
].join("\n"));
|
|
380
|
+
}
|
|
381
|
+
function printPdfHelp() {
|
|
382
|
+
console.log([
|
|
383
|
+
"Usage:",
|
|
384
|
+
" flux pdf [options] <file> --out <file.pdf>",
|
|
385
|
+
"",
|
|
386
|
+
"Description:",
|
|
387
|
+
" Render a Flux document snapshot to PDF.",
|
|
388
|
+
"",
|
|
389
|
+
"Options:",
|
|
390
|
+
" --out <file> Output PDF path. (required)",
|
|
391
|
+
" --seed <n> Seed for deterministic rendering.",
|
|
392
|
+
" --docstep <n> Docstep to render.",
|
|
393
|
+
" -h, --help Show this message.",
|
|
394
|
+
"",
|
|
395
|
+
].join("\n"));
|
|
396
|
+
}
|
|
397
|
+
function printConfigHelp() {
|
|
398
|
+
console.log([
|
|
399
|
+
"Usage:",
|
|
400
|
+
" flux config",
|
|
401
|
+
" flux config set <key> <value> [--init]",
|
|
402
|
+
"",
|
|
403
|
+
"Options:",
|
|
404
|
+
" --json Emit JSON.",
|
|
405
|
+
" --init Create flux.config.json if missing.",
|
|
406
|
+
"",
|
|
407
|
+
].join("\n"));
|
|
408
|
+
}
|
|
409
|
+
function printNewHelp() {
|
|
410
|
+
console.log([
|
|
411
|
+
"Usage:",
|
|
412
|
+
" flux new (launch wizard)",
|
|
413
|
+
" flux new <template> --out <dir> --page Letter|A4 --theme print|screen|both --fonts tech|bookish",
|
|
414
|
+
" --fallback system|none --assets yes|no --chapters N --live yes|no",
|
|
415
|
+
"",
|
|
416
|
+
"Templates:",
|
|
417
|
+
" demo, article, spec, zine, paper, blank",
|
|
418
|
+
"",
|
|
419
|
+
].join("\n"));
|
|
420
|
+
}
|
|
421
|
+
function printAddHelp() {
|
|
422
|
+
console.log([
|
|
423
|
+
"Usage:",
|
|
424
|
+
" flux add <kind> [options] <file>",
|
|
425
|
+
"",
|
|
426
|
+
"Kinds:",
|
|
427
|
+
" title, page, section, figure, callout, table, slot, inline-slot, bibliography-stub",
|
|
428
|
+
"",
|
|
429
|
+
"Options:",
|
|
430
|
+
" --text <value> Text value for title/callout.",
|
|
431
|
+
" --heading <value> Heading text for sections.",
|
|
432
|
+
" --label <value> Optional label for figure/callout.",
|
|
433
|
+
" --no-heading Omit section heading.",
|
|
434
|
+
" --no-check Skip check after editing.",
|
|
435
|
+
"",
|
|
436
|
+
].join("\n"));
|
|
437
|
+
}
|
|
116
438
|
async function runParse(args) {
|
|
117
|
-
const opts = {
|
|
118
|
-
ndjson: false,
|
|
119
|
-
pretty: false,
|
|
120
|
-
compact: false,
|
|
121
|
-
};
|
|
122
|
-
const files = [];
|
|
439
|
+
const opts = { ndjson: false, pretty: false, compact: false, files: [] };
|
|
123
440
|
for (const arg of args) {
|
|
124
|
-
if (arg === "--ndjson")
|
|
441
|
+
if (arg === "--ndjson")
|
|
125
442
|
opts.ndjson = true;
|
|
126
|
-
|
|
127
|
-
else if (arg === "--pretty") {
|
|
443
|
+
else if (arg === "--pretty")
|
|
128
444
|
opts.pretty = true;
|
|
129
|
-
|
|
130
|
-
else if (arg === "--compact") {
|
|
445
|
+
else if (arg === "--compact")
|
|
131
446
|
opts.compact = true;
|
|
447
|
+
else
|
|
448
|
+
opts.files.push(arg);
|
|
449
|
+
}
|
|
450
|
+
const result = await parseCommand(opts);
|
|
451
|
+
if (!result.ok || !result.data) {
|
|
452
|
+
console.error(result.error?.message ?? "flux parse failed");
|
|
453
|
+
if (result.error?.code === "NO_INPUT")
|
|
454
|
+
printParseHelp();
|
|
455
|
+
return 1;
|
|
456
|
+
}
|
|
457
|
+
if (result.data.ndjson) {
|
|
458
|
+
for (const item of result.data.docs) {
|
|
459
|
+
process.stdout.write(JSON.stringify({ file: item.file, doc: item.doc }) + "\n");
|
|
132
460
|
}
|
|
133
|
-
|
|
134
|
-
|
|
461
|
+
return 0;
|
|
462
|
+
}
|
|
463
|
+
const doc = result.data.docs[0].doc;
|
|
464
|
+
const space = result.data.compact ? 0 : 2;
|
|
465
|
+
process.stdout.write(JSON.stringify(doc, null, space) + "\n");
|
|
466
|
+
return 0;
|
|
467
|
+
}
|
|
468
|
+
async function runCheck(args, globals) {
|
|
469
|
+
const files = args.filter((arg) => !arg.startsWith("-"));
|
|
470
|
+
const result = await checkCommand({ files, json: globals.json });
|
|
471
|
+
if (!result.ok || !result.data) {
|
|
472
|
+
console.error(result.error?.message ?? "flux check failed");
|
|
473
|
+
printCheckHelp();
|
|
474
|
+
return 1;
|
|
475
|
+
}
|
|
476
|
+
const results = result.data.results;
|
|
477
|
+
const failed = results.filter((r) => !r.ok);
|
|
478
|
+
if (globals.json) {
|
|
479
|
+
for (const r of results) {
|
|
480
|
+
const payload = {
|
|
481
|
+
file: r.file,
|
|
482
|
+
ok: r.ok,
|
|
483
|
+
};
|
|
484
|
+
if (r.errors)
|
|
485
|
+
payload.errors = r.errors.map((message) => ({ message }));
|
|
486
|
+
process.stdout.write(JSON.stringify(payload) + "\n");
|
|
135
487
|
}
|
|
488
|
+
return failed.length ? 1 : 0;
|
|
489
|
+
}
|
|
490
|
+
for (const r of results) {
|
|
491
|
+
if (!r.errors)
|
|
492
|
+
continue;
|
|
493
|
+
for (const msg of r.errors)
|
|
494
|
+
console.error(msg);
|
|
136
495
|
}
|
|
137
|
-
if (
|
|
138
|
-
|
|
139
|
-
|
|
496
|
+
if (failed.length) {
|
|
497
|
+
if (!globals.quiet)
|
|
498
|
+
console.log(`✗ ${failed.length} of ${results.length} files failed checks`);
|
|
140
499
|
return 1;
|
|
141
500
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
501
|
+
if (!globals.quiet)
|
|
502
|
+
console.log(`✓ ${results.length} files OK`);
|
|
503
|
+
return 0;
|
|
504
|
+
}
|
|
505
|
+
async function runRender(args) {
|
|
506
|
+
let format = "ir";
|
|
507
|
+
let seed;
|
|
508
|
+
let time;
|
|
509
|
+
let docstep;
|
|
510
|
+
let file;
|
|
511
|
+
try {
|
|
512
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
513
|
+
const arg = args[i];
|
|
514
|
+
if (arg === "--format") {
|
|
515
|
+
format = args[i + 1] ?? "";
|
|
516
|
+
i += 1;
|
|
517
|
+
}
|
|
518
|
+
else if (arg.startsWith("--format=")) {
|
|
519
|
+
format = arg.slice("--format=".length);
|
|
520
|
+
}
|
|
521
|
+
else if (arg === "--seed") {
|
|
522
|
+
seed = parseNumberFlag("--seed", args[i + 1]);
|
|
523
|
+
i += 1;
|
|
524
|
+
}
|
|
525
|
+
else if (arg.startsWith("--seed=")) {
|
|
526
|
+
seed = parseNumberFlag("--seed", arg.slice("--seed=".length));
|
|
527
|
+
}
|
|
528
|
+
else if (arg === "--time") {
|
|
529
|
+
time = parseNumberFlag("--time", args[i + 1]);
|
|
530
|
+
i += 1;
|
|
531
|
+
}
|
|
532
|
+
else if (arg.startsWith("--time=")) {
|
|
533
|
+
time = parseNumberFlag("--time", arg.slice("--time=".length));
|
|
534
|
+
}
|
|
535
|
+
else if (arg === "--docstep") {
|
|
536
|
+
docstep = parseNumberFlag("--docstep", args[i + 1]);
|
|
537
|
+
i += 1;
|
|
538
|
+
}
|
|
539
|
+
else if (arg.startsWith("--docstep=")) {
|
|
540
|
+
docstep = parseNumberFlag("--docstep", arg.slice("--docstep=".length));
|
|
541
|
+
}
|
|
542
|
+
else if (!arg.startsWith("-")) {
|
|
543
|
+
file = arg;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
catch (error) {
|
|
548
|
+
console.error(`flux render: ${error?.message ?? error}`);
|
|
145
549
|
return 1;
|
|
146
550
|
}
|
|
147
|
-
if (
|
|
148
|
-
console.error("flux
|
|
551
|
+
if (!file) {
|
|
552
|
+
console.error("flux render: No input file specified.");
|
|
553
|
+
printRenderHelp();
|
|
149
554
|
return 1;
|
|
150
555
|
}
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
556
|
+
const result = await renderCommand({ file, format: format, seed, time, docstep });
|
|
557
|
+
if (!result.ok || !result.data) {
|
|
558
|
+
console.error(result.error?.message ?? "flux render failed");
|
|
559
|
+
return 1;
|
|
560
|
+
}
|
|
561
|
+
process.stdout.write(JSON.stringify(result.data.rendered, null, 2) + "\n");
|
|
562
|
+
return 0;
|
|
563
|
+
}
|
|
564
|
+
async function runFormat(args, globals) {
|
|
565
|
+
const file = args.find((arg) => !arg.startsWith("-"));
|
|
566
|
+
if (!file) {
|
|
567
|
+
console.error("flux fmt: No input file specified.");
|
|
568
|
+
printFormatHelp();
|
|
569
|
+
return 1;
|
|
570
|
+
}
|
|
571
|
+
const result = await formatCommand({ file });
|
|
572
|
+
if (!result.ok || !result.data) {
|
|
573
|
+
console.error(result.error?.message ?? "flux fmt failed");
|
|
574
|
+
return 1;
|
|
575
|
+
}
|
|
576
|
+
if (globals.json) {
|
|
577
|
+
process.stdout.write(JSON.stringify(result.data) + "\n");
|
|
578
|
+
}
|
|
579
|
+
else if (!globals.quiet) {
|
|
580
|
+
console.log(`Formatted ${file}`);
|
|
581
|
+
}
|
|
582
|
+
return 0;
|
|
583
|
+
}
|
|
584
|
+
async function runTick(args) {
|
|
585
|
+
let seconds;
|
|
586
|
+
let seed;
|
|
587
|
+
let file;
|
|
588
|
+
try {
|
|
589
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
590
|
+
const arg = args[i];
|
|
591
|
+
if (arg === "--seconds") {
|
|
592
|
+
seconds = parseNumberFlag("--seconds", args[i + 1]);
|
|
593
|
+
i += 1;
|
|
594
|
+
}
|
|
595
|
+
else if (arg.startsWith("--seconds=")) {
|
|
596
|
+
seconds = parseNumberFlag("--seconds", arg.slice("--seconds=".length));
|
|
597
|
+
}
|
|
598
|
+
else if (arg === "--seed") {
|
|
599
|
+
seed = parseNumberFlag("--seed", args[i + 1]);
|
|
600
|
+
i += 1;
|
|
601
|
+
}
|
|
602
|
+
else if (arg.startsWith("--seed=")) {
|
|
603
|
+
seed = parseNumberFlag("--seed", arg.slice("--seed=".length));
|
|
604
|
+
}
|
|
605
|
+
else if (!arg.startsWith("-")) {
|
|
606
|
+
file = arg;
|
|
607
|
+
}
|
|
156
608
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
609
|
+
}
|
|
610
|
+
catch (error) {
|
|
611
|
+
console.error(`flux tick: ${error?.message ?? error}`);
|
|
612
|
+
return 1;
|
|
613
|
+
}
|
|
614
|
+
if (!file) {
|
|
615
|
+
console.error("flux tick: No input file specified.");
|
|
616
|
+
printTickHelp();
|
|
617
|
+
return 1;
|
|
618
|
+
}
|
|
619
|
+
if (seconds === undefined) {
|
|
620
|
+
console.error("flux tick: --seconds is required.");
|
|
621
|
+
printTickHelp();
|
|
622
|
+
return 1;
|
|
623
|
+
}
|
|
624
|
+
const result = await tickCommand({ file, seconds, seed });
|
|
625
|
+
if (!result.ok || !result.data) {
|
|
626
|
+
console.error(result.error?.message ?? "flux tick failed");
|
|
627
|
+
return 1;
|
|
628
|
+
}
|
|
629
|
+
process.stdout.write(JSON.stringify(result.data.rendered, null, 2) + "\n");
|
|
630
|
+
return 0;
|
|
631
|
+
}
|
|
632
|
+
async function runStep(args) {
|
|
633
|
+
let count;
|
|
634
|
+
let seed;
|
|
635
|
+
let file;
|
|
636
|
+
try {
|
|
637
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
638
|
+
const arg = args[i];
|
|
639
|
+
if (arg === "--n") {
|
|
640
|
+
count = parseNumberFlag("--n", args[i + 1]);
|
|
641
|
+
i += 1;
|
|
642
|
+
}
|
|
643
|
+
else if (arg.startsWith("--n=")) {
|
|
644
|
+
count = parseNumberFlag("--n", arg.slice("--n=".length));
|
|
645
|
+
}
|
|
646
|
+
else if (arg === "--seed") {
|
|
647
|
+
seed = parseNumberFlag("--seed", args[i + 1]);
|
|
648
|
+
i += 1;
|
|
649
|
+
}
|
|
650
|
+
else if (arg.startsWith("--seed=")) {
|
|
651
|
+
seed = parseNumberFlag("--seed", arg.slice("--seed=".length));
|
|
652
|
+
}
|
|
653
|
+
else if (!arg.startsWith("-")) {
|
|
654
|
+
file = arg;
|
|
655
|
+
}
|
|
161
656
|
}
|
|
657
|
+
}
|
|
658
|
+
catch (error) {
|
|
659
|
+
console.error(`flux step: ${error?.message ?? error}`);
|
|
660
|
+
return 1;
|
|
661
|
+
}
|
|
662
|
+
if (!file) {
|
|
663
|
+
console.error("flux step: No input file specified.");
|
|
664
|
+
printStepHelp();
|
|
665
|
+
return 1;
|
|
666
|
+
}
|
|
667
|
+
const result = await stepCommand({ file, steps: count, seed });
|
|
668
|
+
if (!result.ok || !result.data) {
|
|
669
|
+
console.error(result.error?.message ?? "flux step failed");
|
|
670
|
+
return 1;
|
|
671
|
+
}
|
|
672
|
+
process.stdout.write(JSON.stringify(result.data.rendered, null, 2) + "\n");
|
|
673
|
+
return 0;
|
|
674
|
+
}
|
|
675
|
+
async function runView(args, globals) {
|
|
676
|
+
let port;
|
|
677
|
+
let docstepMs;
|
|
678
|
+
let timeRate;
|
|
679
|
+
let seed;
|
|
680
|
+
let useTty = false;
|
|
681
|
+
let editorDist;
|
|
682
|
+
const allowNet = [];
|
|
683
|
+
let file;
|
|
684
|
+
let advanceTime = true;
|
|
685
|
+
let advanceTimeExplicit = false;
|
|
686
|
+
try {
|
|
687
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
688
|
+
const arg = args[i];
|
|
689
|
+
if (arg === "--tty") {
|
|
690
|
+
useTty = true;
|
|
691
|
+
}
|
|
692
|
+
else if (arg === "--no-time") {
|
|
693
|
+
advanceTime = false;
|
|
694
|
+
advanceTimeExplicit = true;
|
|
695
|
+
}
|
|
696
|
+
else if (arg === "--port") {
|
|
697
|
+
port = parseNumberFlag("--port", args[i + 1]);
|
|
698
|
+
i += 1;
|
|
699
|
+
}
|
|
700
|
+
else if (arg.startsWith("--port=")) {
|
|
701
|
+
port = parseNumberFlag("--port", arg.slice("--port=".length));
|
|
702
|
+
}
|
|
703
|
+
else if (arg === "--docstep-ms") {
|
|
704
|
+
docstepMs = parseNumberFlag("--docstep-ms", args[i + 1]);
|
|
705
|
+
i += 1;
|
|
706
|
+
}
|
|
707
|
+
else if (arg.startsWith("--docstep-ms=")) {
|
|
708
|
+
docstepMs = parseNumberFlag("--docstep-ms", arg.slice("--docstep-ms=".length));
|
|
709
|
+
}
|
|
710
|
+
else if (arg === "--time-rate") {
|
|
711
|
+
timeRate = parseNumberFlag("--time-rate", args[i + 1]);
|
|
712
|
+
i += 1;
|
|
713
|
+
}
|
|
714
|
+
else if (arg.startsWith("--time-rate=")) {
|
|
715
|
+
timeRate = parseNumberFlag("--time-rate", arg.slice("--time-rate=".length));
|
|
716
|
+
}
|
|
717
|
+
else if (arg === "--seed") {
|
|
718
|
+
seed = parseNumberFlag("--seed", args[i + 1]);
|
|
719
|
+
i += 1;
|
|
720
|
+
}
|
|
721
|
+
else if (arg.startsWith("--seed=")) {
|
|
722
|
+
seed = parseNumberFlag("--seed", arg.slice("--seed=".length));
|
|
723
|
+
}
|
|
724
|
+
else if (arg === "--allow-net") {
|
|
725
|
+
const raw = args[i + 1] ?? "";
|
|
726
|
+
allowNet.push(...raw.split(",").map((item) => item.trim()).filter(Boolean));
|
|
727
|
+
i += 1;
|
|
728
|
+
}
|
|
729
|
+
else if (arg.startsWith("--allow-net=")) {
|
|
730
|
+
const raw = arg.slice("--allow-net=".length);
|
|
731
|
+
allowNet.push(...raw.split(",").map((item) => item.trim()).filter(Boolean));
|
|
732
|
+
}
|
|
733
|
+
else if (arg === "--editor-dist") {
|
|
734
|
+
editorDist = args[i + 1];
|
|
735
|
+
i += 1;
|
|
736
|
+
}
|
|
737
|
+
else if (arg.startsWith("--editor-dist=")) {
|
|
738
|
+
editorDist = arg.slice("--editor-dist=".length);
|
|
739
|
+
}
|
|
740
|
+
else if (!arg.startsWith("-")) {
|
|
741
|
+
file = arg;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
catch (error) {
|
|
746
|
+
console.error(`flux view: ${error?.message ?? error}`);
|
|
747
|
+
return 1;
|
|
748
|
+
}
|
|
749
|
+
const resolved = await resolveConfig({ cwd: process.cwd(), env: process.env });
|
|
750
|
+
if (docstepMs === undefined) {
|
|
751
|
+
docstepMs = resolved.config.docstepMs;
|
|
752
|
+
}
|
|
753
|
+
if (!advanceTimeExplicit) {
|
|
754
|
+
advanceTime = resolved.config.advanceTime;
|
|
755
|
+
}
|
|
756
|
+
if (!file) {
|
|
757
|
+
console.error("flux view: No input file specified.");
|
|
758
|
+
printViewHelp();
|
|
759
|
+
return 1;
|
|
760
|
+
}
|
|
761
|
+
if (file === "-") {
|
|
762
|
+
console.error("flux view: stdin input is not supported for the web viewer.");
|
|
763
|
+
return 1;
|
|
764
|
+
}
|
|
765
|
+
if (useTty) {
|
|
162
766
|
try {
|
|
163
|
-
const
|
|
164
|
-
|
|
767
|
+
const source = await fs.readFile(file, "utf8");
|
|
768
|
+
const doc = parseFlux(source, file);
|
|
769
|
+
const runtime = createRuntime(doc, { clock: "manual" });
|
|
770
|
+
await runViewer(runtime, { docPath: file, title: doc.meta.title, materialLabels: new Map() });
|
|
771
|
+
return 0;
|
|
165
772
|
}
|
|
166
773
|
catch (error) {
|
|
167
|
-
|
|
168
|
-
console.error(msg);
|
|
774
|
+
console.error(`flux view: ${String(error?.message ?? error)}`);
|
|
169
775
|
return 1;
|
|
170
776
|
}
|
|
171
777
|
}
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
778
|
+
const result = await viewCommand({
|
|
779
|
+
cwd: process.cwd(),
|
|
780
|
+
docPath: file,
|
|
781
|
+
port,
|
|
782
|
+
docstepMs,
|
|
783
|
+
seed,
|
|
784
|
+
allowNet,
|
|
785
|
+
advanceTime,
|
|
786
|
+
timeRate,
|
|
787
|
+
editorDist,
|
|
788
|
+
});
|
|
789
|
+
if (!result.ok || !result.data) {
|
|
790
|
+
console.error(result.error?.message ?? "flux view failed");
|
|
791
|
+
return 1;
|
|
792
|
+
}
|
|
793
|
+
await updateRecents(process.cwd(), path.resolve(file));
|
|
794
|
+
const session = result.data.session;
|
|
795
|
+
if (globals.json) {
|
|
796
|
+
process.stdout.write(JSON.stringify(session) + "\n");
|
|
797
|
+
}
|
|
798
|
+
else if (!globals.quiet) {
|
|
799
|
+
console.log(`Flux viewer running at ${session.url}`);
|
|
800
|
+
if (session.attached) {
|
|
801
|
+
console.log("Attached to existing viewer.");
|
|
177
802
|
}
|
|
178
|
-
|
|
803
|
+
console.log("Press Ctrl+C to stop.");
|
|
804
|
+
}
|
|
805
|
+
if (!globals.quiet) {
|
|
806
|
+
openBrowser(session.url);
|
|
807
|
+
}
|
|
808
|
+
if (session.close) {
|
|
809
|
+
await new Promise((resolve) => {
|
|
810
|
+
const shutdown = async () => {
|
|
811
|
+
await session.close?.();
|
|
812
|
+
resolve();
|
|
813
|
+
};
|
|
814
|
+
process.on("SIGINT", shutdown);
|
|
815
|
+
process.on("SIGTERM", shutdown);
|
|
816
|
+
});
|
|
179
817
|
}
|
|
180
|
-
// Single file → pretty by default unless compact explicitly requested
|
|
181
|
-
const doc = docs[0].doc;
|
|
182
|
-
const space = opts.compact ? 0 : 2;
|
|
183
|
-
const json = JSON.stringify(doc, null, space);
|
|
184
|
-
process.stdout.write(json + "\n");
|
|
185
818
|
return 0;
|
|
186
819
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
820
|
+
async function runEdit(args, globals) {
|
|
821
|
+
let port;
|
|
822
|
+
let docstepMs;
|
|
823
|
+
let timeRate;
|
|
824
|
+
let seed;
|
|
825
|
+
let editorDist;
|
|
826
|
+
const allowNet = [];
|
|
827
|
+
let file;
|
|
828
|
+
let advanceTime = true;
|
|
829
|
+
let advanceTimeExplicit = false;
|
|
830
|
+
try {
|
|
831
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
832
|
+
const arg = args[i];
|
|
833
|
+
if (arg === "--no-time") {
|
|
834
|
+
advanceTime = false;
|
|
835
|
+
advanceTimeExplicit = true;
|
|
836
|
+
}
|
|
837
|
+
else if (arg === "--port") {
|
|
838
|
+
port = parseNumberFlag("--port", args[i + 1]);
|
|
839
|
+
i += 1;
|
|
840
|
+
}
|
|
841
|
+
else if (arg.startsWith("--port=")) {
|
|
842
|
+
port = parseNumberFlag("--port", arg.slice("--port=".length));
|
|
843
|
+
}
|
|
844
|
+
else if (arg === "--docstep-ms") {
|
|
845
|
+
docstepMs = parseNumberFlag("--docstep-ms", args[i + 1]);
|
|
846
|
+
i += 1;
|
|
847
|
+
}
|
|
848
|
+
else if (arg.startsWith("--docstep-ms=")) {
|
|
849
|
+
docstepMs = parseNumberFlag("--docstep-ms", arg.slice("--docstep-ms=".length));
|
|
850
|
+
}
|
|
851
|
+
else if (arg === "--time-rate") {
|
|
852
|
+
timeRate = parseNumberFlag("--time-rate", args[i + 1]);
|
|
853
|
+
i += 1;
|
|
854
|
+
}
|
|
855
|
+
else if (arg.startsWith("--time-rate=")) {
|
|
856
|
+
timeRate = parseNumberFlag("--time-rate", arg.slice("--time-rate=".length));
|
|
857
|
+
}
|
|
858
|
+
else if (arg === "--seed") {
|
|
859
|
+
seed = parseNumberFlag("--seed", args[i + 1]);
|
|
860
|
+
i += 1;
|
|
861
|
+
}
|
|
862
|
+
else if (arg.startsWith("--seed=")) {
|
|
863
|
+
seed = parseNumberFlag("--seed", arg.slice("--seed=".length));
|
|
864
|
+
}
|
|
865
|
+
else if (arg === "--allow-net") {
|
|
866
|
+
const raw = args[i + 1] ?? "";
|
|
867
|
+
allowNet.push(...raw.split(",").map((item) => item.trim()).filter(Boolean));
|
|
868
|
+
i += 1;
|
|
869
|
+
}
|
|
870
|
+
else if (arg.startsWith("--allow-net=")) {
|
|
871
|
+
const raw = arg.slice("--allow-net=".length);
|
|
872
|
+
allowNet.push(...raw.split(",").map((item) => item.trim()).filter(Boolean));
|
|
873
|
+
}
|
|
874
|
+
else if (arg === "--editor-dist") {
|
|
875
|
+
editorDist = args[i + 1];
|
|
876
|
+
i += 1;
|
|
877
|
+
}
|
|
878
|
+
else if (arg.startsWith("--editor-dist=")) {
|
|
879
|
+
editorDist = arg.slice("--editor-dist=".length);
|
|
880
|
+
}
|
|
881
|
+
else if (!arg.startsWith("-")) {
|
|
882
|
+
file = arg;
|
|
883
|
+
}
|
|
201
884
|
}
|
|
202
885
|
}
|
|
203
|
-
|
|
204
|
-
console.error(
|
|
205
|
-
printCheckHelp();
|
|
886
|
+
catch (error) {
|
|
887
|
+
console.error(`flux edit: ${error?.message ?? error}`);
|
|
206
888
|
return 1;
|
|
207
889
|
}
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
890
|
+
const resolved = await resolveConfig({ cwd: process.cwd(), env: process.env });
|
|
891
|
+
if (docstepMs === undefined) {
|
|
892
|
+
docstepMs = resolved.config.docstepMs;
|
|
893
|
+
}
|
|
894
|
+
if (!advanceTimeExplicit) {
|
|
895
|
+
advanceTime = resolved.config.advanceTime;
|
|
896
|
+
}
|
|
897
|
+
if (!file) {
|
|
898
|
+
console.error("flux edit: No input file specified.");
|
|
899
|
+
printEditHelp();
|
|
900
|
+
return 1;
|
|
901
|
+
}
|
|
902
|
+
if (file === "-") {
|
|
903
|
+
console.error("flux edit: stdin input is not supported for the web editor.");
|
|
904
|
+
return 1;
|
|
905
|
+
}
|
|
906
|
+
const result = await viewCommand({
|
|
907
|
+
cwd: process.cwd(),
|
|
908
|
+
docPath: file,
|
|
909
|
+
port,
|
|
910
|
+
docstepMs,
|
|
911
|
+
seed,
|
|
912
|
+
allowNet,
|
|
913
|
+
advanceTime,
|
|
914
|
+
timeRate,
|
|
915
|
+
editorDist,
|
|
916
|
+
});
|
|
917
|
+
if (!result.ok || !result.data) {
|
|
918
|
+
console.error(result.error?.message ?? "flux edit failed");
|
|
919
|
+
return 1;
|
|
920
|
+
}
|
|
921
|
+
await updateRecents(process.cwd(), path.resolve(file));
|
|
922
|
+
const session = result.data.session;
|
|
923
|
+
const absolutePath = path.resolve(file);
|
|
924
|
+
const editorUrl = `${session.url}/edit?file=${encodeURIComponent(absolutePath)}`;
|
|
925
|
+
let buildId = session.buildId;
|
|
926
|
+
let editorDistPath = session.editorDist;
|
|
927
|
+
if (!buildId) {
|
|
928
|
+
const fetchImpl = globalThis?.fetch;
|
|
929
|
+
if (fetchImpl) {
|
|
930
|
+
try {
|
|
931
|
+
const res = await fetchImpl(`${session.url}/edit/build-id.json`, { cache: "no-store" });
|
|
932
|
+
if (res.ok) {
|
|
933
|
+
const json = await res.json();
|
|
934
|
+
buildId = json?.buildId ?? null;
|
|
935
|
+
editorDistPath = json?.editorDist ?? editorDistPath;
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
catch {
|
|
939
|
+
// ignore
|
|
940
|
+
}
|
|
226
941
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
});
|
|
234
|
-
continue;
|
|
942
|
+
}
|
|
943
|
+
if (globals.json) {
|
|
944
|
+
process.stdout.write(JSON.stringify({ ...session, editorUrl }) + "\n");
|
|
945
|
+
}
|
|
946
|
+
else if (!globals.quiet) {
|
|
947
|
+
if (buildId || editorDistPath) {
|
|
948
|
+
console.log(`[flux] editor build ${buildId ?? "unknown"} (${editorDistPath ?? "unknown"})`);
|
|
235
949
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
initRuntimeState(doc);
|
|
950
|
+
console.log(`Flux editor running at ${editorUrl}`);
|
|
951
|
+
if (session.attached) {
|
|
952
|
+
console.log("Attached to existing editor.");
|
|
240
953
|
}
|
|
241
|
-
|
|
242
|
-
const detail = error?.message ?? String(error);
|
|
243
|
-
errors.push(`${file}:0:0: Check error: initRuntimeState failed: ${detail}`);
|
|
244
|
-
}
|
|
245
|
-
// Static checks (grids, neighbors, timers, etc.)
|
|
246
|
-
errors.push(...checkDocument(file, doc));
|
|
247
|
-
results.push({
|
|
248
|
-
file,
|
|
249
|
-
ok: errors.length === 0,
|
|
250
|
-
errors: errors.length ? errors : undefined,
|
|
251
|
-
});
|
|
954
|
+
console.log("Press Ctrl+C to stop.");
|
|
252
955
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if (
|
|
257
|
-
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
956
|
+
if (!globals.quiet) {
|
|
957
|
+
openBrowser(editorUrl);
|
|
958
|
+
}
|
|
959
|
+
if (session.close) {
|
|
960
|
+
await new Promise((resolve) => {
|
|
961
|
+
const shutdown = async () => {
|
|
962
|
+
await session.close?.();
|
|
963
|
+
resolve();
|
|
261
964
|
};
|
|
262
|
-
|
|
263
|
-
|
|
965
|
+
process.on("SIGINT", shutdown);
|
|
966
|
+
process.on("SIGTERM", shutdown);
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
return 0;
|
|
970
|
+
}
|
|
971
|
+
async function runPdf(args, globals) {
|
|
972
|
+
let seed;
|
|
973
|
+
let docstep;
|
|
974
|
+
let outPath;
|
|
975
|
+
let file;
|
|
976
|
+
try {
|
|
977
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
978
|
+
const arg = args[i];
|
|
979
|
+
if (arg === "--out") {
|
|
980
|
+
outPath = args[i + 1];
|
|
981
|
+
i += 1;
|
|
982
|
+
}
|
|
983
|
+
else if (arg.startsWith("--out=")) {
|
|
984
|
+
outPath = arg.slice("--out=".length);
|
|
985
|
+
}
|
|
986
|
+
else if (arg === "--seed") {
|
|
987
|
+
seed = parseNumberFlag("--seed", args[i + 1]);
|
|
988
|
+
i += 1;
|
|
989
|
+
}
|
|
990
|
+
else if (arg.startsWith("--seed=")) {
|
|
991
|
+
seed = parseNumberFlag("--seed", arg.slice("--seed=".length));
|
|
992
|
+
}
|
|
993
|
+
else if (arg === "--docstep") {
|
|
994
|
+
docstep = parseNumberFlag("--docstep", args[i + 1]);
|
|
995
|
+
i += 1;
|
|
996
|
+
}
|
|
997
|
+
else if (arg.startsWith("--docstep=")) {
|
|
998
|
+
docstep = parseNumberFlag("--docstep", arg.slice("--docstep=".length));
|
|
999
|
+
}
|
|
1000
|
+
else if (!arg.startsWith("-")) {
|
|
1001
|
+
file = arg;
|
|
264
1002
|
}
|
|
265
|
-
process.stdout.write(JSON.stringify(payload) + "\n");
|
|
266
1003
|
}
|
|
267
|
-
return hasFailure ? 1 : 0;
|
|
268
1004
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
1005
|
+
catch (error) {
|
|
1006
|
+
console.error(`flux pdf: ${error?.message ?? error}`);
|
|
1007
|
+
return 1;
|
|
1008
|
+
}
|
|
1009
|
+
if (!file || !outPath) {
|
|
1010
|
+
console.error("flux pdf: --out <file.pdf> is required.");
|
|
1011
|
+
printPdfHelp();
|
|
1012
|
+
return 1;
|
|
1013
|
+
}
|
|
1014
|
+
const result = await pdfCommand({ file, outPath, seed, docstep });
|
|
1015
|
+
if (!result.ok || !result.data) {
|
|
1016
|
+
console.error(result.error?.message ?? "flux pdf failed");
|
|
1017
|
+
return 1;
|
|
1018
|
+
}
|
|
1019
|
+
if (globals.json) {
|
|
1020
|
+
process.stdout.write(JSON.stringify(result.data) + "\n");
|
|
1021
|
+
}
|
|
1022
|
+
else if (!globals.quiet) {
|
|
1023
|
+
console.log(`Wrote PDF to ${outPath}`);
|
|
1024
|
+
}
|
|
1025
|
+
return 0;
|
|
1026
|
+
}
|
|
1027
|
+
async function runConfig(args, globals) {
|
|
1028
|
+
if (args.length === 0) {
|
|
1029
|
+
const result = await configCommand({
|
|
1030
|
+
cwd: process.cwd(),
|
|
1031
|
+
action: "view",
|
|
1032
|
+
env: process.env,
|
|
1033
|
+
});
|
|
1034
|
+
if (!result.ok || !result.data) {
|
|
1035
|
+
console.error(result.error?.message ?? "flux config failed");
|
|
1036
|
+
return 1;
|
|
1037
|
+
}
|
|
1038
|
+
return printConfig(result.data.config, globals);
|
|
1039
|
+
}
|
|
1040
|
+
if (args[0] === "set") {
|
|
1041
|
+
const key = args[1];
|
|
1042
|
+
const value = args[2];
|
|
1043
|
+
const init = args.includes("--init");
|
|
1044
|
+
if (!key || value === undefined) {
|
|
1045
|
+
printConfigHelp();
|
|
1046
|
+
return 1;
|
|
1047
|
+
}
|
|
1048
|
+
const parsedValue = parseConfigValue(key, value);
|
|
1049
|
+
const result = await configCommand({
|
|
1050
|
+
cwd: process.cwd(),
|
|
1051
|
+
action: "set",
|
|
1052
|
+
key: parsedValue.key,
|
|
1053
|
+
value: parsedValue.value,
|
|
1054
|
+
init,
|
|
1055
|
+
env: process.env,
|
|
1056
|
+
});
|
|
1057
|
+
if (!result.ok || !result.data) {
|
|
1058
|
+
console.error(result.error?.message ?? "flux config set failed");
|
|
1059
|
+
return 1;
|
|
1060
|
+
}
|
|
1061
|
+
return printConfig(result.data.config, globals);
|
|
1062
|
+
}
|
|
1063
|
+
printConfigHelp();
|
|
1064
|
+
return 1;
|
|
1065
|
+
}
|
|
1066
|
+
async function runNew(args, globals) {
|
|
1067
|
+
const [template, ...rest] = args;
|
|
1068
|
+
if (!template) {
|
|
1069
|
+
printNewHelp();
|
|
1070
|
+
return 1;
|
|
1071
|
+
}
|
|
1072
|
+
const opts = parseNewArgs(rest);
|
|
1073
|
+
if (!opts.out) {
|
|
1074
|
+
const resolved = await resolveConfig({ cwd: process.cwd(), env: process.env });
|
|
1075
|
+
if (resolved.config.defaultOutputDir && resolved.config.defaultOutputDir !== ".") {
|
|
1076
|
+
opts.out = resolved.config.defaultOutputDir;
|
|
275
1077
|
}
|
|
276
1078
|
}
|
|
277
|
-
|
|
278
|
-
|
|
1079
|
+
const result = await newCommand({
|
|
1080
|
+
cwd: process.cwd(),
|
|
1081
|
+
template: template,
|
|
1082
|
+
out: opts.out,
|
|
1083
|
+
page: opts.page,
|
|
1084
|
+
theme: opts.theme,
|
|
1085
|
+
fonts: opts.fonts,
|
|
1086
|
+
fontFallback: opts.fontFallback,
|
|
1087
|
+
assets: opts.assets,
|
|
1088
|
+
chapters: opts.chapters,
|
|
1089
|
+
live: opts.live,
|
|
1090
|
+
});
|
|
1091
|
+
if (!result.ok || !result.data) {
|
|
1092
|
+
console.error(result.error?.message ?? "flux new failed");
|
|
279
1093
|
return 1;
|
|
280
1094
|
}
|
|
281
|
-
|
|
1095
|
+
await updateRecents(process.cwd(), result.data.docPath);
|
|
1096
|
+
if (globals.json) {
|
|
1097
|
+
process.stdout.write(JSON.stringify(result.data) + "\n");
|
|
1098
|
+
}
|
|
1099
|
+
else if (!globals.quiet) {
|
|
1100
|
+
console.log(`Created ${result.data.docPath}`);
|
|
1101
|
+
printNewNextSteps(result.data.docPath);
|
|
1102
|
+
}
|
|
282
1103
|
return 0;
|
|
283
1104
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
1105
|
+
async function runAdd(args, globals) {
|
|
1106
|
+
const [kind, ...rest] = args;
|
|
1107
|
+
if (!kind) {
|
|
1108
|
+
printAddHelp();
|
|
1109
|
+
return 1;
|
|
1110
|
+
}
|
|
1111
|
+
const parsed = parseAddArgs(rest);
|
|
1112
|
+
if (!parsed.file) {
|
|
1113
|
+
console.error("flux add: missing <file>");
|
|
1114
|
+
printAddHelp();
|
|
1115
|
+
return 1;
|
|
290
1116
|
}
|
|
291
|
-
|
|
1117
|
+
const result = await addCommand({
|
|
1118
|
+
cwd: process.cwd(),
|
|
1119
|
+
file: parsed.file,
|
|
1120
|
+
kind: kind,
|
|
1121
|
+
text: parsed.text,
|
|
1122
|
+
heading: parsed.heading,
|
|
1123
|
+
label: parsed.label,
|
|
1124
|
+
noHeading: parsed.noHeading,
|
|
1125
|
+
noCheck: parsed.noCheck,
|
|
1126
|
+
});
|
|
1127
|
+
if (!result.ok || !result.data) {
|
|
1128
|
+
console.error(result.error?.message ?? "flux add failed");
|
|
1129
|
+
return 1;
|
|
1130
|
+
}
|
|
1131
|
+
if (globals.json) {
|
|
1132
|
+
process.stdout.write(JSON.stringify(result.data) + "\n");
|
|
1133
|
+
}
|
|
1134
|
+
else if (!globals.quiet) {
|
|
1135
|
+
console.log(`Updated ${result.data.file}`);
|
|
1136
|
+
}
|
|
1137
|
+
return 0;
|
|
292
1138
|
}
|
|
293
|
-
function
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
1139
|
+
function parseNewArgs(args) {
|
|
1140
|
+
const opts = {};
|
|
1141
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
1142
|
+
const arg = args[i];
|
|
1143
|
+
if (arg === "--out") {
|
|
1144
|
+
opts.out = args[i + 1];
|
|
1145
|
+
i += 1;
|
|
1146
|
+
}
|
|
1147
|
+
else if (arg.startsWith("--out=")) {
|
|
1148
|
+
opts.out = arg.slice("--out=".length);
|
|
1149
|
+
}
|
|
1150
|
+
else if (arg === "--page") {
|
|
1151
|
+
opts.page = args[i + 1];
|
|
1152
|
+
i += 1;
|
|
1153
|
+
}
|
|
1154
|
+
else if (arg.startsWith("--page=")) {
|
|
1155
|
+
opts.page = arg.slice("--page=".length);
|
|
1156
|
+
}
|
|
1157
|
+
else if (arg === "--theme") {
|
|
1158
|
+
opts.theme = args[i + 1];
|
|
1159
|
+
i += 1;
|
|
1160
|
+
}
|
|
1161
|
+
else if (arg.startsWith("--theme=")) {
|
|
1162
|
+
opts.theme = arg.slice("--theme=".length);
|
|
1163
|
+
}
|
|
1164
|
+
else if (arg === "--fonts") {
|
|
1165
|
+
opts.fonts = args[i + 1];
|
|
1166
|
+
i += 1;
|
|
1167
|
+
}
|
|
1168
|
+
else if (arg.startsWith("--fonts=")) {
|
|
1169
|
+
opts.fonts = arg.slice("--fonts=".length);
|
|
1170
|
+
}
|
|
1171
|
+
else if (arg === "--fallback" || arg === "--font-fallback") {
|
|
1172
|
+
opts.fontFallback = parseFontFallback(args[i + 1]);
|
|
1173
|
+
i += 1;
|
|
1174
|
+
}
|
|
1175
|
+
else if (arg.startsWith("--fallback=")) {
|
|
1176
|
+
opts.fontFallback = parseFontFallback(arg.slice("--fallback=".length));
|
|
1177
|
+
}
|
|
1178
|
+
else if (arg.startsWith("--font-fallback=")) {
|
|
1179
|
+
opts.fontFallback = parseFontFallback(arg.slice("--font-fallback=".length));
|
|
1180
|
+
}
|
|
1181
|
+
else if (arg === "--assets") {
|
|
1182
|
+
opts.assets = parseYesNo(args[i + 1]);
|
|
1183
|
+
i += 1;
|
|
1184
|
+
}
|
|
1185
|
+
else if (arg.startsWith("--assets=")) {
|
|
1186
|
+
opts.assets = parseYesNo(arg.slice("--assets=".length));
|
|
1187
|
+
}
|
|
1188
|
+
else if (arg === "--chapters") {
|
|
1189
|
+
opts.chapters = parseNumberFlag("--chapters", args[i + 1]);
|
|
1190
|
+
i += 1;
|
|
1191
|
+
}
|
|
1192
|
+
else if (arg.startsWith("--chapters=")) {
|
|
1193
|
+
opts.chapters = parseNumberFlag("--chapters", arg.slice("--chapters=".length));
|
|
1194
|
+
}
|
|
1195
|
+
else if (arg === "--live") {
|
|
1196
|
+
opts.live = parseYesNo(args[i + 1]);
|
|
1197
|
+
i += 1;
|
|
1198
|
+
}
|
|
1199
|
+
else if (arg.startsWith("--live=")) {
|
|
1200
|
+
opts.live = parseYesNo(arg.slice("--live=".length));
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
return opts;
|
|
1204
|
+
}
|
|
1205
|
+
function parseAddArgs(args) {
|
|
1206
|
+
const opts = {};
|
|
1207
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
1208
|
+
const arg = args[i];
|
|
1209
|
+
if (arg === "--text") {
|
|
1210
|
+
opts.text = args[i + 1];
|
|
1211
|
+
i += 1;
|
|
1212
|
+
}
|
|
1213
|
+
else if (arg.startsWith("--text=")) {
|
|
1214
|
+
opts.text = arg.slice("--text=".length);
|
|
1215
|
+
}
|
|
1216
|
+
else if (arg === "--heading") {
|
|
1217
|
+
opts.heading = args[i + 1];
|
|
1218
|
+
i += 1;
|
|
1219
|
+
}
|
|
1220
|
+
else if (arg.startsWith("--heading=")) {
|
|
1221
|
+
opts.heading = arg.slice("--heading=".length);
|
|
1222
|
+
}
|
|
1223
|
+
else if (arg === "--label") {
|
|
1224
|
+
opts.label = args[i + 1];
|
|
1225
|
+
i += 1;
|
|
1226
|
+
}
|
|
1227
|
+
else if (arg.startsWith("--label=")) {
|
|
1228
|
+
opts.label = arg.slice("--label=".length);
|
|
1229
|
+
}
|
|
1230
|
+
else if (arg === "--no-heading") {
|
|
1231
|
+
opts.noHeading = true;
|
|
1232
|
+
}
|
|
1233
|
+
else if (arg === "--no-check") {
|
|
1234
|
+
opts.noCheck = true;
|
|
1235
|
+
}
|
|
1236
|
+
else if (!arg.startsWith("-")) {
|
|
1237
|
+
opts.file = arg;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
return opts;
|
|
1241
|
+
}
|
|
1242
|
+
function parseYesNo(raw) {
|
|
1243
|
+
if (!raw)
|
|
1244
|
+
return true;
|
|
1245
|
+
return !(raw === "no" || raw === "false" || raw === "0");
|
|
1246
|
+
}
|
|
1247
|
+
function parseFontFallback(raw) {
|
|
1248
|
+
if (!raw)
|
|
1249
|
+
return "system";
|
|
1250
|
+
if (raw === "none" || raw === "off" || raw === "false" || raw === "0")
|
|
1251
|
+
return "none";
|
|
1252
|
+
return "system";
|
|
1253
|
+
}
|
|
1254
|
+
function parseConfigValue(key, raw) {
|
|
1255
|
+
switch (key) {
|
|
1256
|
+
case "docstepMs":
|
|
1257
|
+
case "docstep-ms":
|
|
1258
|
+
return { key: "docstepMs", value: Number(raw) };
|
|
1259
|
+
case "advanceTime":
|
|
1260
|
+
case "advance-time":
|
|
1261
|
+
return { key: "advanceTime", value: raw !== "0" && raw !== "false" };
|
|
1262
|
+
case "defaultPageSize":
|
|
1263
|
+
case "page":
|
|
1264
|
+
return { key: "defaultPageSize", value: (raw === "A4" ? "A4" : "Letter") };
|
|
1265
|
+
case "defaultTheme":
|
|
1266
|
+
case "theme":
|
|
1267
|
+
return { key: "defaultTheme", value: raw };
|
|
1268
|
+
case "defaultFonts":
|
|
1269
|
+
case "fonts":
|
|
1270
|
+
return { key: "defaultFonts", value: raw };
|
|
1271
|
+
case "defaultOutputDir":
|
|
1272
|
+
case "output":
|
|
1273
|
+
return { key: "defaultOutputDir", value: raw };
|
|
1274
|
+
default:
|
|
1275
|
+
return { key: key, value: raw };
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
async function printConfig(config, globals) {
|
|
1279
|
+
if (globals.json) {
|
|
1280
|
+
process.stdout.write(JSON.stringify(config, null, 2) + "\n");
|
|
1281
|
+
return 0;
|
|
1282
|
+
}
|
|
1283
|
+
if (!globals.quiet) {
|
|
1284
|
+
console.log(JSON.stringify(config, null, 2));
|
|
1285
|
+
}
|
|
1286
|
+
return 0;
|
|
1287
|
+
}
|
|
1288
|
+
function printNewNextSteps(docPath) {
|
|
1289
|
+
const pdfPath = docPath.replace(/\.flux$/i, ".pdf");
|
|
1290
|
+
console.log([
|
|
1291
|
+
"",
|
|
1292
|
+
"Next steps:",
|
|
1293
|
+
` flux view ${docPath}`,
|
|
1294
|
+
` flux check ${docPath}`,
|
|
1295
|
+
` flux pdf ${docPath} --out ${pdfPath}`,
|
|
1296
|
+
"",
|
|
1297
|
+
].join("\n"));
|
|
1298
|
+
}
|
|
1299
|
+
function parseNumberFlag(flag, raw) {
|
|
1300
|
+
const value = Number(raw);
|
|
1301
|
+
if (!Number.isFinite(value)) {
|
|
1302
|
+
throw new Error(`${flag} expects a finite number`);
|
|
1303
|
+
}
|
|
1304
|
+
return value;
|
|
1305
|
+
}
|
|
1306
|
+
function openBrowser(url) {
|
|
1307
|
+
const command = process.platform === "darwin"
|
|
1308
|
+
? "open"
|
|
1309
|
+
: process.platform === "win32"
|
|
1310
|
+
? "cmd"
|
|
1311
|
+
: "xdg-open";
|
|
1312
|
+
const args = process.platform === "win32" ? ["/c", "start", url.replace(/&/g, "^&")] : [url];
|
|
1313
|
+
spawn(command, args, { stdio: "ignore", detached: true });
|
|
1314
|
+
}
|
|
1315
|
+
async function launchUi(options) {
|
|
1316
|
+
const { runCliUi } = await import("@flux-lang/cli-ui");
|
|
1317
|
+
await runCliUi({
|
|
1318
|
+
cwd: options.cwd,
|
|
1319
|
+
mode: options.mode,
|
|
1320
|
+
initialArgs: options.initialArgs,
|
|
1321
|
+
detach: options.detach,
|
|
1322
|
+
helpCommand: options.helpCommand,
|
|
1323
|
+
version: options.version,
|
|
302
1324
|
});
|
|
1325
|
+
return 0;
|
|
303
1326
|
}
|
|
304
|
-
function
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
const [, line, column, near, detail] = parseMatch;
|
|
315
|
-
return `${file}:${line}:${column}: Parse error near '${near}': ${detail}`;
|
|
316
|
-
}
|
|
317
|
-
const lexMatch = /Lexer error at (\d+):(\d+)\s*-\s*(.*)/.exec(message);
|
|
318
|
-
if (lexMatch) {
|
|
319
|
-
const [, line, column, detail] = lexMatch;
|
|
320
|
-
return `${file}:${line}:${column}: Lexer error: ${detail}`;
|
|
321
|
-
}
|
|
322
|
-
return `${file}:0:0: ${message}`;
|
|
1327
|
+
function parseFlux(source, filePath) {
|
|
1328
|
+
if (!filePath || filePath === "-") {
|
|
1329
|
+
return parseDocument(source);
|
|
1330
|
+
}
|
|
1331
|
+
const resolved = path.resolve(filePath);
|
|
1332
|
+
return parseDocument(source, {
|
|
1333
|
+
sourcePath: resolved,
|
|
1334
|
+
docRoot: path.dirname(resolved),
|
|
1335
|
+
resolveIncludes: true,
|
|
1336
|
+
});
|
|
323
1337
|
}
|