@abraca/cli 2.4.0 → 2.6.0
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 +27 -0
- package/dist/abracadabra-cli.cjs +78 -50
- package/dist/abracadabra-cli.cjs.map +1 -1
- package/dist/abracadabra-cli.esm.js +78 -50
- package/dist/abracadabra-cli.esm.js.map +1 -1
- package/package.json +2 -2
- package/src/commands/content.ts +5 -3
- package/src/commands/documents.ts +3 -3
|
@@ -1409,6 +1409,23 @@ function parseFrontmatter(markdown) {
|
|
|
1409
1409
|
body
|
|
1410
1410
|
};
|
|
1411
1411
|
}
|
|
1412
|
+
function pushNested(out, inner, wrap) {
|
|
1413
|
+
const children = parseInline(inner);
|
|
1414
|
+
if (children.length === 0) {
|
|
1415
|
+
out.push({
|
|
1416
|
+
text: inner,
|
|
1417
|
+
attrs: { ...wrap }
|
|
1418
|
+
});
|
|
1419
|
+
return;
|
|
1420
|
+
}
|
|
1421
|
+
for (const child of children) out.push({
|
|
1422
|
+
text: child.text,
|
|
1423
|
+
attrs: {
|
|
1424
|
+
...child.attrs ?? {},
|
|
1425
|
+
...wrap
|
|
1426
|
+
}
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1412
1429
|
function parseInline(text) {
|
|
1413
1430
|
const stripped = text.replace(/\{lang="[^"]*"\}/g, "").replace(/:(?!badge|icon|kbd)(\w[\w-]*)\[([^\]]*)\](\{[^}]*\})?/g, "$2").replace(/:(?!badge|icon|kbd)(\w[\w-]*)(\{[^}]*\})/g, "");
|
|
1414
1431
|
const tokens = [];
|
|
@@ -1457,22 +1474,10 @@ function parseInline(text) {
|
|
|
1457
1474
|
text: label,
|
|
1458
1475
|
attrs: { docLink: { docId } }
|
|
1459
1476
|
});
|
|
1460
|
-
} else if (match[10] !== void 0) tokens
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
});
|
|
1464
|
-
else if (match[11] !== void 0) tokens.push({
|
|
1465
|
-
text: match[11],
|
|
1466
|
-
attrs: { bold: true }
|
|
1467
|
-
});
|
|
1468
|
-
else if (match[12] !== void 0) tokens.push({
|
|
1469
|
-
text: match[12],
|
|
1470
|
-
attrs: { italic: true }
|
|
1471
|
-
});
|
|
1472
|
-
else if (match[13] !== void 0) tokens.push({
|
|
1473
|
-
text: match[13],
|
|
1474
|
-
attrs: { italic: true }
|
|
1475
|
-
});
|
|
1477
|
+
} else if (match[10] !== void 0) pushNested(tokens, match[10], { strike: true });
|
|
1478
|
+
else if (match[11] !== void 0) pushNested(tokens, match[11], { bold: true });
|
|
1479
|
+
else if (match[12] !== void 0) pushNested(tokens, match[12], { italic: true });
|
|
1480
|
+
else if (match[13] !== void 0) pushNested(tokens, match[13], { italic: true });
|
|
1476
1481
|
else if (match[14] !== void 0) tokens.push({
|
|
1477
1482
|
text: match[14],
|
|
1478
1483
|
attrs: { code: true }
|
|
@@ -2008,11 +2013,19 @@ function parseBlocks(markdown) {
|
|
|
2008
2013
|
function fillTextInto(el, tokens) {
|
|
2009
2014
|
const filtered = tokens.filter((t) => t.text.length > 0);
|
|
2010
2015
|
if (!filtered.length) return;
|
|
2011
|
-
const
|
|
2012
|
-
|
|
2016
|
+
const children = filtered.map((tok) => {
|
|
2017
|
+
return (tok.attrs?.docLink)?.docId ? new Y.XmlElement("docLink") : new Y.XmlText();
|
|
2018
|
+
});
|
|
2019
|
+
el.insert(0, children);
|
|
2013
2020
|
filtered.forEach((tok, i) => {
|
|
2014
|
-
|
|
2015
|
-
|
|
2021
|
+
const node = children[i];
|
|
2022
|
+
if (node instanceof Y.XmlElement) {
|
|
2023
|
+
const dl = tok.attrs.docLink;
|
|
2024
|
+
node.setAttribute("docId", dl.docId);
|
|
2025
|
+
return;
|
|
2026
|
+
}
|
|
2027
|
+
if (tok.attrs) node.insert(0, tok.text, tok.attrs);
|
|
2028
|
+
else node.insert(0, tok.text);
|
|
2016
2029
|
});
|
|
2017
2030
|
}
|
|
2018
2031
|
function blockElName(b) {
|
|
@@ -2368,6 +2381,15 @@ function populateYDocFromMarkdown(fragment, markdown, fallbackTitle = "Untitled"
|
|
|
2368
2381
|
|
|
2369
2382
|
//#endregion
|
|
2370
2383
|
//#region packages/convert/src/yjs-to-markdown.ts
|
|
2384
|
+
function isXElem(n) {
|
|
2385
|
+
return !!n && typeof n.nodeName === "string";
|
|
2386
|
+
}
|
|
2387
|
+
function isXText(n) {
|
|
2388
|
+
return !!n && typeof n.nodeName !== "string" && typeof n.toDelta === "function";
|
|
2389
|
+
}
|
|
2390
|
+
function localizeFragment(fragment) {
|
|
2391
|
+
return fragment;
|
|
2392
|
+
}
|
|
2371
2393
|
function serializeDelta(delta) {
|
|
2372
2394
|
let result = "";
|
|
2373
2395
|
for (const op of delta) {
|
|
@@ -2428,12 +2450,15 @@ function serializeDelta(delta) {
|
|
|
2428
2450
|
}
|
|
2429
2451
|
function serializeInline(el) {
|
|
2430
2452
|
const parts = [];
|
|
2431
|
-
for (const child of el.toArray()) if (child
|
|
2432
|
-
else if (child
|
|
2453
|
+
for (const child of el.toArray()) if (isXText(child)) parts.push(serializeDelta(child.toDelta()));
|
|
2454
|
+
else if (isXElem(child)) if (child.nodeName === "docLink") {
|
|
2455
|
+
const docId = child.getAttribute("docId") ?? "";
|
|
2456
|
+
parts.push(`[[${docId}]]`);
|
|
2457
|
+
} else parts.push(serializeInline(child));
|
|
2433
2458
|
return parts.join("");
|
|
2434
2459
|
}
|
|
2435
2460
|
function serializeBlock(el, indent = "") {
|
|
2436
|
-
if (el
|
|
2461
|
+
if (isXText(el)) return serializeDelta(el.toDelta());
|
|
2437
2462
|
switch (el.nodeName) {
|
|
2438
2463
|
case "documentHeader":
|
|
2439
2464
|
case "documentMeta": return "";
|
|
@@ -2453,7 +2478,7 @@ function serializeBlock(el, indent = "") {
|
|
|
2453
2478
|
}
|
|
2454
2479
|
case "blockquote": {
|
|
2455
2480
|
const lines = [];
|
|
2456
|
-
for (const child of el.toArray()) if (child
|
|
2481
|
+
for (const child of el.toArray()) if (isXElem(child)) {
|
|
2457
2482
|
const text = serializeBlock(child);
|
|
2458
2483
|
for (const line of text.split("\n")) lines.push(`> ${line}`);
|
|
2459
2484
|
}
|
|
@@ -2514,11 +2539,11 @@ function serializeBlock(el, indent = "") {
|
|
|
2514
2539
|
if (to) props.push(`to="${to}"`);
|
|
2515
2540
|
return `::card${props.length ? `{${props.join(" ")}}` : ""}\n${serializeChildren(el)}\n::`;
|
|
2516
2541
|
}
|
|
2517
|
-
case "cardGroup": return `::card-group\n${el.toArray().filter((c) => c
|
|
2518
|
-
case "codeCollapse": return `::code-collapse\n${el.toArray().filter((c) => c
|
|
2519
|
-
case "codeGroup": return `::code-group\n${el.toArray().filter((c) => c
|
|
2542
|
+
case "cardGroup": return `::card-group\n${el.toArray().filter((c) => isXElem(c)).map((c) => serializeBlock(c)).join("\n\n")}\n::`;
|
|
2543
|
+
case "codeCollapse": return `::code-collapse\n${el.toArray().filter((c) => isXElem(c) && c.nodeName === "codeBlock").map((c) => serializeBlock(c)).join("\n\n")}\n::`;
|
|
2544
|
+
case "codeGroup": return `::code-group\n${el.toArray().filter((c) => isXElem(c) && c.nodeName === "codeBlock").map((c) => serializeBlock(c)).join("\n\n")}\n::`;
|
|
2520
2545
|
case "codePreview": {
|
|
2521
|
-
const children = el.toArray().filter((c) => c
|
|
2546
|
+
const children = el.toArray().filter((c) => isXElem(c));
|
|
2522
2547
|
const nonCode = children.filter((c) => c.nodeName !== "codeBlock").map((c) => serializeBlock(c)).join("\n\n");
|
|
2523
2548
|
const code = children.filter((c) => c.nodeName === "codeBlock").map((c) => serializeBlock(c)).join("\n\n");
|
|
2524
2549
|
const parts = [nonCode];
|
|
@@ -2536,16 +2561,16 @@ function serializeBlock(el, indent = "") {
|
|
|
2536
2561
|
if (required === true || required === "true") props.push("required=\"true\"");
|
|
2537
2562
|
return `::field{${props.join(" ")}}\n${serializeChildren(el)}\n::`;
|
|
2538
2563
|
}
|
|
2539
|
-
case "fieldGroup": return `::field-group\n${el.toArray().filter((c) => c
|
|
2564
|
+
case "fieldGroup": return `::field-group\n${el.toArray().filter((c) => isXElem(c)).map((c) => serializeBlock(c)).join("\n\n")}\n::`;
|
|
2540
2565
|
default: return serializeChildren(el);
|
|
2541
2566
|
}
|
|
2542
2567
|
}
|
|
2543
2568
|
function serializeChildren(el) {
|
|
2544
2569
|
const blocks = [];
|
|
2545
|
-
for (const child of el.toArray()) if (child
|
|
2570
|
+
for (const child of el.toArray()) if (isXElem(child)) {
|
|
2546
2571
|
const text = serializeBlock(child);
|
|
2547
2572
|
if (text) blocks.push(text);
|
|
2548
|
-
} else if (child
|
|
2573
|
+
} else if (isXText(child)) {
|
|
2549
2574
|
const text = serializeDelta(child.toDelta());
|
|
2550
2575
|
if (text) blocks.push(text);
|
|
2551
2576
|
}
|
|
@@ -2555,11 +2580,11 @@ function serializeListItems(el, type, indent) {
|
|
|
2555
2580
|
const lines = [];
|
|
2556
2581
|
let counter = 1;
|
|
2557
2582
|
for (const child of el.toArray()) {
|
|
2558
|
-
if (!(child
|
|
2583
|
+
if (!isXElem(child) || child.nodeName !== "listItem") continue;
|
|
2559
2584
|
const prefix = type === "bullet" ? "- " : `${counter++}. `;
|
|
2560
2585
|
const subParts = [];
|
|
2561
2586
|
for (const sub of child.toArray()) {
|
|
2562
|
-
if (!(sub
|
|
2587
|
+
if (!isXElem(sub)) continue;
|
|
2563
2588
|
if (sub.nodeName === "bulletList") subParts.push(serializeListItems(sub, "bullet", indent + " "));
|
|
2564
2589
|
else if (sub.nodeName === "orderedList") subParts.push(serializeListItems(sub, "ordered", indent + " "));
|
|
2565
2590
|
else subParts.push(serializeInline(sub));
|
|
@@ -2575,13 +2600,13 @@ function serializeListItems(el, type, indent) {
|
|
|
2575
2600
|
function serializeTaskList(el, indent) {
|
|
2576
2601
|
const lines = [];
|
|
2577
2602
|
for (const child of el.toArray()) {
|
|
2578
|
-
if (!(child
|
|
2603
|
+
if (!isXElem(child) || child.nodeName !== "taskItem") continue;
|
|
2579
2604
|
const checked = child.getAttribute("checked");
|
|
2580
2605
|
const marker = checked === true || checked === "true" ? "[x]" : "[ ]";
|
|
2581
2606
|
let header = "";
|
|
2582
2607
|
const nestedParts = [];
|
|
2583
2608
|
for (const sub of child.toArray()) {
|
|
2584
|
-
if (!(sub
|
|
2609
|
+
if (!isXElem(sub)) continue;
|
|
2585
2610
|
if (sub.nodeName === "paragraph" && header === "") header = serializeInline(sub);
|
|
2586
2611
|
else if (sub.nodeName === "bulletList") nestedParts.push(serializeListItems(sub, "bullet", indent + " "));
|
|
2587
2612
|
else if (sub.nodeName === "orderedList") nestedParts.push(serializeListItems(sub, "ordered", indent + " "));
|
|
@@ -2594,16 +2619,16 @@ function serializeTaskList(el, indent) {
|
|
|
2594
2619
|
return lines.join("\n");
|
|
2595
2620
|
}
|
|
2596
2621
|
function getCodeBlockText(el) {
|
|
2597
|
-
for (const child of el.toArray()) if (child
|
|
2622
|
+
for (const child of el.toArray()) if (isXText(child)) return child.toString();
|
|
2598
2623
|
return "";
|
|
2599
2624
|
}
|
|
2600
2625
|
function serializeTable(el) {
|
|
2601
|
-
const rows = el.toArray().filter((c) => c
|
|
2626
|
+
const rows = el.toArray().filter((c) => isXElem(c));
|
|
2602
2627
|
if (!rows.length) return "";
|
|
2603
2628
|
const serializedRows = [];
|
|
2604
2629
|
for (const row of rows) {
|
|
2605
|
-
const cells = row.toArray().filter((c) => c
|
|
2606
|
-
return cell.toArray().filter((c) => c
|
|
2630
|
+
const cells = row.toArray().filter((c) => isXElem(c)).map((cell) => {
|
|
2631
|
+
return cell.toArray().filter((c) => isXElem(c)).map((c) => serializeInline(c)).join(" ");
|
|
2607
2632
|
});
|
|
2608
2633
|
serializedRows.push(cells);
|
|
2609
2634
|
}
|
|
@@ -2622,7 +2647,7 @@ function serializeTable(el) {
|
|
|
2622
2647
|
].join("\n");
|
|
2623
2648
|
}
|
|
2624
2649
|
function serializeSlottedContainer(el, containerName, childName, slotPrefix) {
|
|
2625
|
-
return `::${containerName}\n${el.toArray().filter((c) => c
|
|
2650
|
+
return `::${containerName}\n${el.toArray().filter((c) => isXElem(c) && c.nodeName === childName).map((item) => {
|
|
2626
2651
|
const label = item.getAttribute("label") ?? "";
|
|
2627
2652
|
const icon = item.getAttribute("icon") ?? "";
|
|
2628
2653
|
const props = [];
|
|
@@ -2672,6 +2697,7 @@ function escapeYaml(s) {
|
|
|
2672
2697
|
return s.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
2673
2698
|
}
|
|
2674
2699
|
function yjsToMarkdown(fragment, label, meta, type) {
|
|
2700
|
+
fragment = localizeFragment(fragment);
|
|
2675
2701
|
const { text: headerText, source: titleSource } = readDocumentHeader(fragment);
|
|
2676
2702
|
const effectiveTitle = headerText || label;
|
|
2677
2703
|
const docMeta = readDocumentMeta(fragment);
|
|
@@ -2695,7 +2721,7 @@ function readDocumentMeta(fragment) {
|
|
|
2695
2721
|
const meta = {};
|
|
2696
2722
|
let type;
|
|
2697
2723
|
for (const child of fragment.toArray()) {
|
|
2698
|
-
if (!(child
|
|
2724
|
+
if (!isXElem(child) || child.nodeName !== "documentMeta") continue;
|
|
2699
2725
|
const attrs = child.getAttributes();
|
|
2700
2726
|
for (const k of Object.keys(attrs)) {
|
|
2701
2727
|
const v = attrs[k];
|
|
@@ -2715,8 +2741,8 @@ function readDocumentMeta(fragment) {
|
|
|
2715
2741
|
}
|
|
2716
2742
|
function readDocumentHeader(fragment) {
|
|
2717
2743
|
for (const child of fragment.toArray()) {
|
|
2718
|
-
if (!(child
|
|
2719
|
-
const text = child.toArray().find((c) => c
|
|
2744
|
+
if (!isXElem(child) || child.nodeName !== "documentHeader") continue;
|
|
2745
|
+
const text = child.toArray().find((c) => isXText(c));
|
|
2720
2746
|
const src = child.getAttribute("titleSource");
|
|
2721
2747
|
const source = src === "h1" || src === "frontmatter" ? src : void 0;
|
|
2722
2748
|
return {
|
|
@@ -2729,7 +2755,7 @@ function readDocumentHeader(fragment) {
|
|
|
2729
2755
|
function collectBodyBlocks(fragment) {
|
|
2730
2756
|
const out = [];
|
|
2731
2757
|
for (const child of fragment.toArray()) {
|
|
2732
|
-
if (!(child
|
|
2758
|
+
if (!isXElem(child)) continue;
|
|
2733
2759
|
if (child.nodeName === "documentHeader" || child.nodeName === "documentMeta") continue;
|
|
2734
2760
|
out.push(child);
|
|
2735
2761
|
}
|
|
@@ -3673,17 +3699,17 @@ registerCommand({
|
|
|
3673
3699
|
const docId = resolveDocument(conn, args.params, args.positional);
|
|
3674
3700
|
if (!docId) return "Document not found. Specify id=, name=, or path= to identify the document.";
|
|
3675
3701
|
try {
|
|
3676
|
-
const
|
|
3702
|
+
const markdown = yjsToMarkdown((await conn.getChildProvider(docId)).document.getXmlFragment("default"), "");
|
|
3677
3703
|
if (args.flags.has("json") || args.params["format"] === "json") {
|
|
3678
3704
|
const treeMap = conn.getTreeMap();
|
|
3679
|
-
let label =
|
|
3705
|
+
let label = "";
|
|
3680
3706
|
let type;
|
|
3681
3707
|
let meta;
|
|
3682
3708
|
let children = [];
|
|
3683
3709
|
if (treeMap) {
|
|
3684
3710
|
const entry = treeMap.get(docId);
|
|
3685
3711
|
if (entry) {
|
|
3686
|
-
label = entry.label ||
|
|
3712
|
+
label = entry.label || label;
|
|
3687
3713
|
type = entry.type;
|
|
3688
3714
|
meta = entry.meta;
|
|
3689
3715
|
}
|
|
@@ -3998,7 +4024,7 @@ registerCommand({
|
|
|
3998
4024
|
try {
|
|
3999
4025
|
const doc = (await conn.getChildProvider(docId)).document;
|
|
4000
4026
|
const fragment = doc.getXmlFragment("default");
|
|
4001
|
-
const
|
|
4027
|
+
const existing = yjsToMarkdown(fragment, "");
|
|
4002
4028
|
const text = content.replace(/\\n/g, "\n").replace(/\\t/g, " ");
|
|
4003
4029
|
const combined = args.flags.has("inline") ? text + existing : text + "\n" + existing;
|
|
4004
4030
|
doc.transact(() => {
|
|
@@ -4021,7 +4047,7 @@ registerCommand({
|
|
|
4021
4047
|
const docId = resolveDocument(conn, args.params, args.positional);
|
|
4022
4048
|
if (!docId) return "Document not found.";
|
|
4023
4049
|
try {
|
|
4024
|
-
const
|
|
4050
|
+
const markdown = yjsToMarkdown((await conn.getChildProvider(docId)).document.getXmlFragment("default"), "");
|
|
4025
4051
|
const words = markdown.split(/\s+/).filter(Boolean).length;
|
|
4026
4052
|
const chars = markdown.length;
|
|
4027
4053
|
if (args.flags.has("words")) return String(words);
|
|
@@ -4043,7 +4069,9 @@ registerCommand({
|
|
|
4043
4069
|
const outputPath = args.params["output"] || args.params["to"] || args.params["path"];
|
|
4044
4070
|
if (!outputPath) return "Missing required parameter: output=<filepath>";
|
|
4045
4071
|
try {
|
|
4046
|
-
const
|
|
4072
|
+
const fragment = (await conn.getChildProvider(docId)).document.getXmlFragment("default");
|
|
4073
|
+
const title = (conn.getTreeMap()?.get(docId))?.label;
|
|
4074
|
+
const markdown = yjsToMarkdown(fragment, title ?? "");
|
|
4047
4075
|
const resolvedPath = path.resolve(outputPath);
|
|
4048
4076
|
fs.writeFileSync(resolvedPath, markdown, "utf-8");
|
|
4049
4077
|
return `Exported "${title || "document"}" to ${resolvedPath} (${Buffer.byteLength(markdown)} bytes)`;
|