@markdy/cli 0.8.20 → 0.8.22
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 +145 -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,13 @@
|
|
|
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";
|
|
5
11
|
import { createRequire } from "module";
|
|
6
12
|
import { basename, dirname, extname, join, resolve, sep } from "path";
|
|
7
13
|
import { fileURLToPath, pathToFileURL } from "url";
|
|
@@ -140,6 +146,13 @@ function formatValue(value) {
|
|
|
140
146
|
var DEFAULT_PORT = 4242;
|
|
141
147
|
var MARKDY_EXT = ".markdy";
|
|
142
148
|
var PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
149
|
+
var compatModulePromise = null;
|
|
150
|
+
function loadCompatModule() {
|
|
151
|
+
if (!compatModulePromise) {
|
|
152
|
+
compatModulePromise = import("@markdy/compat");
|
|
153
|
+
}
|
|
154
|
+
return compatModulePromise;
|
|
155
|
+
}
|
|
143
156
|
async function runCli(argv, io = defaultIo(), runtime = defaultRuntime()) {
|
|
144
157
|
const parsed = parseArgv(argv);
|
|
145
158
|
if (hasFlag(parsed, "help")) {
|
|
@@ -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,89 @@ 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
|
+
const compat = await loadCompatModule().catch((err) => {
|
|
383
|
+
io.stderr(`markdy import: failed to load @markdy/compat: ${describeError(err)}`);
|
|
384
|
+
return null;
|
|
385
|
+
});
|
|
386
|
+
if (!compat) return { exitCode: 1 };
|
|
387
|
+
let markdyCode;
|
|
388
|
+
if (formatFlag === "mermaid" || ext === ".mmd" || ext === ".mermaid") {
|
|
389
|
+
markdyCode = compat.transpileMermaidToMarkdy(content, title).code;
|
|
390
|
+
} else if (formatFlag === "compose" || (ext === ".yml" || ext === ".yaml") && (inputFile.includes("compose") || content.includes("services:"))) {
|
|
391
|
+
markdyCode = compat.transpileDockerComposeToMarkdy(content, title);
|
|
392
|
+
} else if (formatFlag === "k8s" || content.includes("apiVersion:") && content.includes("kind:")) {
|
|
393
|
+
markdyCode = compat.transpileKubernetesManifestsToMarkdy(content, title);
|
|
394
|
+
} else if (formatFlag === "terraform" || ext === ".tfstate" || (content.includes("terraform_version") || content.includes('"resources":'))) {
|
|
395
|
+
markdyCode = compat.transpileTerraformStateToMarkdy(content, title);
|
|
396
|
+
} else if (formatFlag === "drawio" || ext === ".drawio" || ext === ".xml" && content.includes("<mxCell")) {
|
|
397
|
+
markdyCode = compat.transpileDrawioToMarkdy(content, title).code;
|
|
398
|
+
} else {
|
|
399
|
+
markdyCode = compat.transpileMermaidToMarkdy(content, title).code;
|
|
400
|
+
}
|
|
401
|
+
const outPath = getStringFlag(parsed, "out");
|
|
402
|
+
if (outPath) {
|
|
403
|
+
const resolvedOut = resolve(outPath);
|
|
404
|
+
await writeFile(resolvedOut, markdyCode, "utf8");
|
|
405
|
+
io.stdout(`Wrote ${resolvedOut}`);
|
|
406
|
+
} else {
|
|
407
|
+
io.stdout(markdyCode);
|
|
408
|
+
}
|
|
409
|
+
return { exitCode: 0 };
|
|
410
|
+
}
|
|
411
|
+
async function diffCommand(parsed, io) {
|
|
412
|
+
const file1 = parsed.positionals[0];
|
|
413
|
+
const file2 = parsed.positionals[1];
|
|
414
|
+
if (!file1 || !file2) {
|
|
415
|
+
io.stderr("markdy diff: expected two .markdy files to compare (e.g. markdy diff before.markdy after.markdy)");
|
|
416
|
+
return { exitCode: 1 };
|
|
417
|
+
}
|
|
418
|
+
const scene1 = await loadSceneFromFile(file1).catch((err) => {
|
|
419
|
+
io.stderr(`markdy diff: ${describeError(err)}`);
|
|
420
|
+
return null;
|
|
421
|
+
});
|
|
422
|
+
const scene2 = await loadSceneFromFile(file2).catch((err) => {
|
|
423
|
+
io.stderr(`markdy diff: ${describeError(err)}`);
|
|
424
|
+
return null;
|
|
425
|
+
});
|
|
426
|
+
if (!scene1 || !scene2) return { exitCode: 1 };
|
|
427
|
+
const diffResult = diffDiagramASTs(scene1.ast, scene2.ast);
|
|
428
|
+
if (hasFlag(parsed, "evolution")) {
|
|
429
|
+
io.stdout(diffResult.evolutionMarkdyScript);
|
|
430
|
+
return { exitCode: 0 };
|
|
431
|
+
}
|
|
432
|
+
io.stdout(diffResult.summaryMarkdown);
|
|
433
|
+
return { exitCode: 0 };
|
|
434
|
+
}
|
|
435
|
+
async function shareCommand(parsed, io) {
|
|
436
|
+
const file = parsed.positionals[0];
|
|
437
|
+
if (!file) {
|
|
438
|
+
io.stderr("markdy share: expected a .markdy input file");
|
|
439
|
+
return { exitCode: 1 };
|
|
440
|
+
}
|
|
441
|
+
const scene = await loadSceneFromFile(file).catch((err) => {
|
|
442
|
+
io.stderr(`markdy share: ${describeError(err)}`);
|
|
443
|
+
return null;
|
|
444
|
+
});
|
|
445
|
+
if (!scene) return { exitCode: 1 };
|
|
446
|
+
const hash = await compressMarkdyToUrlHash(scene.source);
|
|
447
|
+
const shareUrl = `https://markdy.com/playground/#code=${hash}`;
|
|
448
|
+
io.stdout(shareUrl);
|
|
449
|
+
return { exitCode: 0 };
|
|
450
|
+
}
|
|
313
451
|
async function docsCommand(parsed, io, runtime) {
|
|
314
452
|
const docsUrl = "https://markdy.com";
|
|
315
453
|
const links = [
|
|
@@ -692,14 +830,17 @@ function helpText() {
|
|
|
692
830
|
"",
|
|
693
831
|
"Usage:",
|
|
694
832
|
" markdy",
|
|
695
|
-
" markdy lint <file-or-dir> [--strict]",
|
|
833
|
+
" markdy lint <file-or-dir> [--strict] [--arch-rules]",
|
|
696
834
|
" markdy fmt <file-or-dir> [--write | --check]",
|
|
697
835
|
" markdy render <file.markdy> [--out file.html] [--port 4242] [--no-open]",
|
|
698
836
|
" markdy explain <file.markdy> [--json]",
|
|
837
|
+
" markdy import <file> [--from compose|k8s|terraform|mermaid] [--out scene.markdy]",
|
|
838
|
+
" markdy diff <before.markdy> <after.markdy> [--evolution]",
|
|
839
|
+
" markdy share <file.markdy>",
|
|
699
840
|
" markdy new [target.markdy] [--force]",
|
|
700
841
|
" markdy docs [--open]",
|
|
701
842
|
" markdy ai [--open]",
|
|
702
|
-
" markdy check-all [dir] [--strict]"
|
|
843
|
+
" markdy check-all [dir] [--strict] [--arch-rules]"
|
|
703
844
|
].join("\n");
|
|
704
845
|
}
|
|
705
846
|
function parseArgv(argv) {
|
|
@@ -758,7 +899,7 @@ function parseArgv(argv) {
|
|
|
758
899
|
return { command, positionals, flags };
|
|
759
900
|
}
|
|
760
901
|
function expectsValue(flag) {
|
|
761
|
-
return flag === "out" || flag === "port";
|
|
902
|
+
return flag === "out" || flag === "port" || flag === "config";
|
|
762
903
|
}
|
|
763
904
|
function hasFlag(parsed, name) {
|
|
764
905
|
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.22",
|
|
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/renderer-dom": "0.8.
|
|
47
|
-
"@markdy/stdlib-systems": "0.8.
|
|
45
|
+
"@markdy/compat": "0.8.22",
|
|
46
|
+
"@markdy/renderer-dom": "0.8.22",
|
|
47
|
+
"@markdy/stdlib-systems": "0.8.22",
|
|
48
|
+
"@markdy/core": "0.8.22"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
51
|
"@types/node": "^25.9.5",
|