@abraca/cli 2.3.0 → 2.5.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.
@@ -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.push({
1461
- text: match[10],
1462
- attrs: { strike: true }
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 xtNodes = filtered.map(() => new Y.XmlText());
2012
- el.insert(0, xtNodes);
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
- if (tok.attrs) xtNodes[i].insert(0, tok.text, tok.attrs);
2015
- else xtNodes[i].insert(0, tok.text);
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 instanceof Y.XmlText) parts.push(serializeDelta(child.toDelta()));
2432
- else if (child instanceof Y.XmlElement) parts.push(serializeInline(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 instanceof Y.XmlText) return serializeDelta(el.toDelta());
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 instanceof Y.XmlElement) {
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 instanceof Y.XmlElement).map((c) => serializeBlock(c)).join("\n\n")}\n::`;
2518
- case "codeCollapse": return `::code-collapse\n${el.toArray().filter((c) => c instanceof Y.XmlElement && c.nodeName === "codeBlock").map((c) => serializeBlock(c)).join("\n\n")}\n::`;
2519
- case "codeGroup": return `::code-group\n${el.toArray().filter((c) => c instanceof Y.XmlElement && c.nodeName === "codeBlock").map((c) => serializeBlock(c)).join("\n\n")}\n::`;
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 instanceof Y.XmlElement);
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 instanceof Y.XmlElement).map((c) => serializeBlock(c)).join("\n\n")}\n::`;
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 instanceof Y.XmlElement) {
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 instanceof Y.XmlText) {
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 instanceof Y.XmlElement) || child.nodeName !== "listItem") continue;
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 instanceof Y.XmlElement)) continue;
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 instanceof Y.XmlElement) || child.nodeName !== "taskItem") continue;
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 instanceof Y.XmlElement)) continue;
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 instanceof Y.XmlText) return child.toString();
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 instanceof Y.XmlElement);
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 instanceof Y.XmlElement).map((cell) => {
2606
- return cell.toArray().filter((c) => c instanceof Y.XmlElement).map((c) => serializeInline(c)).join(" ");
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 instanceof Y.XmlElement && c.nodeName === childName).map((item) => {
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 instanceof Y.XmlElement) || child.nodeName !== "documentMeta") continue;
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 instanceof Y.XmlElement) || child.nodeName !== "documentHeader") continue;
2719
- const text = child.toArray().find((c) => c instanceof Y.XmlText);
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 instanceof Y.XmlElement)) continue;
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 { title, markdown } = yjsToMarkdown((await conn.getChildProvider(docId)).document.getXmlFragment("default"));
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 = title;
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 || title;
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 { markdown: existing } = yjsToMarkdown(fragment);
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 { markdown } = yjsToMarkdown((await conn.getChildProvider(docId)).document.getXmlFragment("default"));
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 { title, markdown } = yjsToMarkdown((await conn.getChildProvider(docId)).document.getXmlFragment("default"));
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)`;