@markdy/cli 0.8.20 → 0.8.21
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 +14 -10
- package/dist/index.js +140 -4
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -35,16 +35,20 @@ npx markdy render examples/showcase/bullet-reveal.markdy --out examples/xscene.h
|
|
|
35
35
|
## Commands
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
markdy
|
|
39
|
-
markdy lint scene.markdy
|
|
40
|
-
markdy
|
|
41
|
-
markdy fmt scene.markdy --write
|
|
42
|
-
markdy
|
|
43
|
-
markdy
|
|
44
|
-
markdy
|
|
45
|
-
markdy
|
|
46
|
-
markdy
|
|
47
|
-
markdy
|
|
38
|
+
markdy # launch a local browser playground on http://127.0.0.1:4242
|
|
39
|
+
markdy lint scene.markdy # syntax validation
|
|
40
|
+
markdy lint scene.markdy --arch-rules # run Well-Architected governance & cycle rules
|
|
41
|
+
markdy fmt scene.markdy --write # format MarkdyScript file
|
|
42
|
+
markdy import flow.mmd --out scene.markdy # import Mermaid, draw.io, Compose, K8s, or Terraform
|
|
43
|
+
markdy diff v1.markdy v2.markdy # semantic AST diff summary table
|
|
44
|
+
markdy diff v1.markdy v2.markdy --evolution # generate animated migration scene
|
|
45
|
+
markdy share scene.markdy # create compressed playground link
|
|
46
|
+
markdy render scene.markdy --out dist/scene.html # export self-contained HTML preview
|
|
47
|
+
markdy explain scene.markdy # display AST structure and stats
|
|
48
|
+
markdy new demo.markdy # scaffold fresh starter scene
|
|
49
|
+
markdy docs # display docs and tutorial links
|
|
50
|
+
markdy ai # generate prompt context for LLMs
|
|
51
|
+
markdy check-all . --arch-rules # batch lint entire workspace
|
|
48
52
|
```
|
|
49
53
|
|
|
50
54
|
## Notes
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
parse,
|
|
6
|
+
validateArchitecture,
|
|
7
|
+
resolveArchitectureConfig,
|
|
8
|
+
diffDiagramASTs,
|
|
9
|
+
compressMarkdyToUrlHash
|
|
10
|
+
} from "@markdy/core";
|
|
11
|
+
import {
|
|
12
|
+
transpileMermaidToMarkdy,
|
|
13
|
+
transpileDockerComposeToMarkdy,
|
|
14
|
+
transpileKubernetesManifestsToMarkdy,
|
|
15
|
+
transpileTerraformStateToMarkdy,
|
|
16
|
+
transpileDrawioToMarkdy
|
|
17
|
+
} from "@markdy/compat";
|
|
5
18
|
import { createRequire } from "module";
|
|
6
19
|
import { basename, dirname, extname, join, resolve, sep } from "path";
|
|
7
20
|
import { fileURLToPath, pathToFileURL } from "url";
|
|
@@ -159,6 +172,12 @@ async function runCli(argv, io = defaultIo(), runtime = defaultRuntime()) {
|
|
|
159
172
|
return renderCommand(parsed, io, runtime);
|
|
160
173
|
case "explain":
|
|
161
174
|
return explainCommand(parsed, io);
|
|
175
|
+
case "import":
|
|
176
|
+
return importCommand(parsed, io);
|
|
177
|
+
case "diff":
|
|
178
|
+
return diffCommand(parsed, io);
|
|
179
|
+
case "share":
|
|
180
|
+
return shareCommand(parsed, io);
|
|
162
181
|
case "new":
|
|
163
182
|
return newCommand(parsed, io);
|
|
164
183
|
case "docs":
|
|
@@ -181,14 +200,50 @@ async function lintCommand(parsed, io) {
|
|
|
181
200
|
return { exitCode: 1 };
|
|
182
201
|
}
|
|
183
202
|
const strict = hasFlag(parsed, "strict");
|
|
203
|
+
const checkArchRules = hasFlag(parsed, "arch-rules");
|
|
204
|
+
const configPath = getStringFlag(parsed, "config");
|
|
184
205
|
const cache = /* @__PURE__ */ new Map();
|
|
185
206
|
let warningCount = 0;
|
|
186
207
|
let errorCount = 0;
|
|
208
|
+
let customRules;
|
|
209
|
+
if (configPath) {
|
|
210
|
+
try {
|
|
211
|
+
const raw = await readFile(resolve(process.cwd(), configPath), "utf-8");
|
|
212
|
+
const json = JSON.parse(raw);
|
|
213
|
+
customRules = resolveArchitectureConfig(json);
|
|
214
|
+
} catch (err) {
|
|
215
|
+
io.stderr(`markdy lint: failed to read config "${configPath}": ${err.message}`);
|
|
216
|
+
return { exitCode: 1 };
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
for (const defaultName of [".markdyrc.json", "markdy.config.json", ".markdyrc"]) {
|
|
220
|
+
try {
|
|
221
|
+
const candidate = resolve(process.cwd(), defaultName);
|
|
222
|
+
const raw = await readFile(candidate, "utf-8");
|
|
223
|
+
const json = JSON.parse(raw);
|
|
224
|
+
customRules = resolveArchitectureConfig(json);
|
|
225
|
+
break;
|
|
226
|
+
} catch {
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
187
230
|
for (const file of files) {
|
|
188
231
|
try {
|
|
189
232
|
const scene = await loadSceneFromFile(file, cache);
|
|
190
233
|
io.stdout(`OK ${file}`);
|
|
191
234
|
warningCount += printWarnings(scene.ast.diagnostics, file, io);
|
|
235
|
+
if (checkArchRules || customRules) {
|
|
236
|
+
const violations = validateArchitecture(scene.ast, customRules);
|
|
237
|
+
for (const v of violations) {
|
|
238
|
+
if (v.severity === "error") {
|
|
239
|
+
errorCount++;
|
|
240
|
+
io.stderr(`ARCH_FAIL ${file}:${v.line ?? 1} [${v.ruleName}] ${v.message}`);
|
|
241
|
+
} else {
|
|
242
|
+
warningCount++;
|
|
243
|
+
io.stderr(`ARCH_WARN ${file}:${v.line ?? 1} [${v.ruleName}] ${v.message}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
192
247
|
} catch (error) {
|
|
193
248
|
errorCount++;
|
|
194
249
|
io.stderr(`FAIL ${file}`);
|
|
@@ -310,6 +365,84 @@ async function newCommand(parsed, io) {
|
|
|
310
365
|
io.stdout(`Created ${resolvedTarget}`);
|
|
311
366
|
return { exitCode: 0 };
|
|
312
367
|
}
|
|
368
|
+
async function importCommand(parsed, io) {
|
|
369
|
+
const inputFile = parsed.positionals[0];
|
|
370
|
+
if (!inputFile) {
|
|
371
|
+
io.stderr("markdy import: expected an input file (e.g. diagram.mmd, docker-compose.yml, manifests.yaml, terraform.tfstate)");
|
|
372
|
+
return { exitCode: 1 };
|
|
373
|
+
}
|
|
374
|
+
const content = await readFile(resolve(inputFile), "utf8").catch((err) => {
|
|
375
|
+
io.stderr(`markdy import: failed to read ${inputFile}: ${describeError(err)}`);
|
|
376
|
+
return null;
|
|
377
|
+
});
|
|
378
|
+
if (content === null) return { exitCode: 1 };
|
|
379
|
+
const formatFlag = getStringFlag(parsed, "from")?.toLowerCase();
|
|
380
|
+
const ext = extname(inputFile).toLowerCase();
|
|
381
|
+
const title = basename(inputFile, extname(inputFile));
|
|
382
|
+
let markdyCode;
|
|
383
|
+
if (formatFlag === "mermaid" || ext === ".mmd" || ext === ".mermaid") {
|
|
384
|
+
markdyCode = transpileMermaidToMarkdy(content, title).code;
|
|
385
|
+
} else if (formatFlag === "compose" || (ext === ".yml" || ext === ".yaml") && (inputFile.includes("compose") || content.includes("services:"))) {
|
|
386
|
+
markdyCode = transpileDockerComposeToMarkdy(content, title);
|
|
387
|
+
} else if (formatFlag === "k8s" || content.includes("apiVersion:") && content.includes("kind:")) {
|
|
388
|
+
markdyCode = transpileKubernetesManifestsToMarkdy(content, title);
|
|
389
|
+
} else if (formatFlag === "terraform" || ext === ".tfstate" || (content.includes("terraform_version") || content.includes('"resources":'))) {
|
|
390
|
+
markdyCode = transpileTerraformStateToMarkdy(content, title);
|
|
391
|
+
} else if (formatFlag === "drawio" || ext === ".drawio" || ext === ".xml" && content.includes("<mxCell")) {
|
|
392
|
+
markdyCode = transpileDrawioToMarkdy(content, title).code;
|
|
393
|
+
} else {
|
|
394
|
+
markdyCode = transpileMermaidToMarkdy(content, title).code;
|
|
395
|
+
}
|
|
396
|
+
const outPath = getStringFlag(parsed, "out");
|
|
397
|
+
if (outPath) {
|
|
398
|
+
const resolvedOut = resolve(outPath);
|
|
399
|
+
await writeFile(resolvedOut, markdyCode, "utf8");
|
|
400
|
+
io.stdout(`Wrote ${resolvedOut}`);
|
|
401
|
+
} else {
|
|
402
|
+
io.stdout(markdyCode);
|
|
403
|
+
}
|
|
404
|
+
return { exitCode: 0 };
|
|
405
|
+
}
|
|
406
|
+
async function diffCommand(parsed, io) {
|
|
407
|
+
const file1 = parsed.positionals[0];
|
|
408
|
+
const file2 = parsed.positionals[1];
|
|
409
|
+
if (!file1 || !file2) {
|
|
410
|
+
io.stderr("markdy diff: expected two .markdy files to compare (e.g. markdy diff before.markdy after.markdy)");
|
|
411
|
+
return { exitCode: 1 };
|
|
412
|
+
}
|
|
413
|
+
const scene1 = await loadSceneFromFile(file1).catch((err) => {
|
|
414
|
+
io.stderr(`markdy diff: ${describeError(err)}`);
|
|
415
|
+
return null;
|
|
416
|
+
});
|
|
417
|
+
const scene2 = await loadSceneFromFile(file2).catch((err) => {
|
|
418
|
+
io.stderr(`markdy diff: ${describeError(err)}`);
|
|
419
|
+
return null;
|
|
420
|
+
});
|
|
421
|
+
if (!scene1 || !scene2) return { exitCode: 1 };
|
|
422
|
+
const diffResult = diffDiagramASTs(scene1.ast, scene2.ast);
|
|
423
|
+
if (hasFlag(parsed, "evolution")) {
|
|
424
|
+
io.stdout(diffResult.evolutionMarkdyScript);
|
|
425
|
+
return { exitCode: 0 };
|
|
426
|
+
}
|
|
427
|
+
io.stdout(diffResult.summaryMarkdown);
|
|
428
|
+
return { exitCode: 0 };
|
|
429
|
+
}
|
|
430
|
+
async function shareCommand(parsed, io) {
|
|
431
|
+
const file = parsed.positionals[0];
|
|
432
|
+
if (!file) {
|
|
433
|
+
io.stderr("markdy share: expected a .markdy input file");
|
|
434
|
+
return { exitCode: 1 };
|
|
435
|
+
}
|
|
436
|
+
const scene = await loadSceneFromFile(file).catch((err) => {
|
|
437
|
+
io.stderr(`markdy share: ${describeError(err)}`);
|
|
438
|
+
return null;
|
|
439
|
+
});
|
|
440
|
+
if (!scene) return { exitCode: 1 };
|
|
441
|
+
const hash = await compressMarkdyToUrlHash(scene.source);
|
|
442
|
+
const shareUrl = `https://markdy.com/playground/#code=${hash}`;
|
|
443
|
+
io.stdout(shareUrl);
|
|
444
|
+
return { exitCode: 0 };
|
|
445
|
+
}
|
|
313
446
|
async function docsCommand(parsed, io, runtime) {
|
|
314
447
|
const docsUrl = "https://markdy.com";
|
|
315
448
|
const links = [
|
|
@@ -692,14 +825,17 @@ function helpText() {
|
|
|
692
825
|
"",
|
|
693
826
|
"Usage:",
|
|
694
827
|
" markdy",
|
|
695
|
-
" markdy lint <file-or-dir> [--strict]",
|
|
828
|
+
" markdy lint <file-or-dir> [--strict] [--arch-rules]",
|
|
696
829
|
" markdy fmt <file-or-dir> [--write | --check]",
|
|
697
830
|
" markdy render <file.markdy> [--out file.html] [--port 4242] [--no-open]",
|
|
698
831
|
" markdy explain <file.markdy> [--json]",
|
|
832
|
+
" markdy import <file> [--from compose|k8s|terraform|mermaid] [--out scene.markdy]",
|
|
833
|
+
" markdy diff <before.markdy> <after.markdy> [--evolution]",
|
|
834
|
+
" markdy share <file.markdy>",
|
|
699
835
|
" markdy new [target.markdy] [--force]",
|
|
700
836
|
" markdy docs [--open]",
|
|
701
837
|
" markdy ai [--open]",
|
|
702
|
-
" markdy check-all [dir] [--strict]"
|
|
838
|
+
" markdy check-all [dir] [--strict] [--arch-rules]"
|
|
703
839
|
].join("\n");
|
|
704
840
|
}
|
|
705
841
|
function parseArgv(argv) {
|
|
@@ -758,7 +894,7 @@ function parseArgv(argv) {
|
|
|
758
894
|
return { command, positionals, flags };
|
|
759
895
|
}
|
|
760
896
|
function expectsValue(flag) {
|
|
761
|
-
return flag === "out" || flag === "port";
|
|
897
|
+
return flag === "out" || flag === "port" || flag === "config";
|
|
762
898
|
}
|
|
763
899
|
function hasFlag(parsed, name) {
|
|
764
900
|
return parsed.flags.get(name) === true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markdy/cli",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.21",
|
|
4
4
|
"description": "First-party CLI for diagram-native MarkdyScript diagrams: lint, format, explain, render, and preview.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -42,9 +42,10 @@
|
|
|
42
42
|
"access": "public"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@markdy/
|
|
46
|
-
"@markdy/
|
|
47
|
-
"@markdy/
|
|
45
|
+
"@markdy/compat": "0.8.21",
|
|
46
|
+
"@markdy/core": "0.8.21",
|
|
47
|
+
"@markdy/renderer-dom": "0.8.21",
|
|
48
|
+
"@markdy/stdlib-systems": "0.8.21"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
51
|
"@types/node": "^25.9.5",
|