@orkestrel/markdown 0.0.8 → 0.0.9
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/dist/src/core/index.cjs +146 -146
- package/dist/src/core/index.d.cts +5 -5
- package/dist/src/core/index.d.ts +5 -5
- package/dist/src/core/index.js +146 -146
- package/package.json +8 -6
- package/dist/src/core/index.cjs.map +0 -1
- package/dist/src/core/index.js.map +0 -1
package/dist/src/core/index.cjs
CHANGED
|
@@ -544,152 +544,6 @@ function parseBlocks(lines, depth) {
|
|
|
544
544
|
return blocks;
|
|
545
545
|
}
|
|
546
546
|
/**
|
|
547
|
-
* Collects a GFM table starting at a header row, parsing the header, the
|
|
548
|
-
* alignment row, and every contiguous body row that follows.
|
|
549
|
-
*
|
|
550
|
-
* @param lines - The markdown lines to scan.
|
|
551
|
-
* @param start - The index of the header row.
|
|
552
|
-
* @returns The parsed table node and the index of the first line after it.
|
|
553
|
-
*
|
|
554
|
-
* @example
|
|
555
|
-
* ```ts
|
|
556
|
-
* collectTable(['| a |', '| - |'], 0) // { node: { element: 'table', ... }, next: 2 }
|
|
557
|
-
* ```
|
|
558
|
-
*/
|
|
559
|
-
function collectTable(lines, start) {
|
|
560
|
-
const headerCells = splitTableRow(lines[start] ?? "");
|
|
561
|
-
const columns = headerCells.length;
|
|
562
|
-
const header = headerCells.map((cell) => parseInline(cell.trim()));
|
|
563
|
-
const align = delimiterToAlignments(lines[start + 1] ?? "");
|
|
564
|
-
const padded = [];
|
|
565
|
-
for (let column = 0; column < columns; column += 1) padded.push(align[column] ?? null);
|
|
566
|
-
const rows = [];
|
|
567
|
-
let index = start + 2;
|
|
568
|
-
while (index < lines.length && !isBlankLine(lines[index] ?? "") && (lines[index] ?? "").includes("|")) {
|
|
569
|
-
const cells = splitTableRow(lines[index] ?? "");
|
|
570
|
-
const row = [];
|
|
571
|
-
for (let column = 0; column < columns; column += 1) row.push(parseInline((cells[column] ?? "").trim()));
|
|
572
|
-
rows.push(row);
|
|
573
|
-
index += 1;
|
|
574
|
-
}
|
|
575
|
-
return {
|
|
576
|
-
node: {
|
|
577
|
-
element: "table",
|
|
578
|
-
header,
|
|
579
|
-
rows,
|
|
580
|
-
align: padded
|
|
581
|
-
},
|
|
582
|
-
next: index
|
|
583
|
-
};
|
|
584
|
-
}
|
|
585
|
-
/**
|
|
586
|
-
* Collects a list starting at the first item, gathering sibling items at the
|
|
587
|
-
* same indent/ordering and recursing into each item's own block content.
|
|
588
|
-
*
|
|
589
|
-
* @param lines - The markdown lines to scan.
|
|
590
|
-
* @param start - The index of the first list item.
|
|
591
|
-
* @param depth - The current recursion depth (each item recurses at `depth + 1`).
|
|
592
|
-
* @returns The parsed list node and the index of the first line after it.
|
|
593
|
-
*
|
|
594
|
-
* @example
|
|
595
|
-
* ```ts
|
|
596
|
-
* collectList(['- item'], 0, 0) // { node: { element: 'list', ... }, next: 1 }
|
|
597
|
-
* ```
|
|
598
|
-
*/
|
|
599
|
-
function collectList(lines, start, depth) {
|
|
600
|
-
const first = extractListItem(lines[start] ?? "");
|
|
601
|
-
const ordered = first?.ordered ?? false;
|
|
602
|
-
const startOrdinal = first?.start ?? 1;
|
|
603
|
-
const topIndent = first?.indent ?? 0;
|
|
604
|
-
const items = [];
|
|
605
|
-
const chain = [];
|
|
606
|
-
let nested = true;
|
|
607
|
-
for (let cursor = start; cursor < lines.length; cursor += 1) {
|
|
608
|
-
const parsed = extractListItem(lines[cursor] ?? "");
|
|
609
|
-
const previous = chain[chain.length - 1];
|
|
610
|
-
if (parsed === void 0 || previous !== void 0 && (previous.content.length > 0 || parsed.indent !== previous.marker)) {
|
|
611
|
-
nested = false;
|
|
612
|
-
break;
|
|
613
|
-
}
|
|
614
|
-
chain.push(parsed);
|
|
615
|
-
}
|
|
616
|
-
const remaining = 64 - depth;
|
|
617
|
-
if (nested && remaining > 0 && chain.length > remaining) {
|
|
618
|
-
const terminal = chain[remaining - 1];
|
|
619
|
-
if (terminal !== void 0) {
|
|
620
|
-
const source = [terminal.content];
|
|
621
|
-
for (let cursor = start + remaining; cursor < lines.length; cursor += 1) source.push((lines[cursor] ?? "").slice(terminal.marker));
|
|
622
|
-
let children = [{
|
|
623
|
-
element: "paragraph",
|
|
624
|
-
children: [{
|
|
625
|
-
element: "text",
|
|
626
|
-
value: source.join("\n")
|
|
627
|
-
}]
|
|
628
|
-
}];
|
|
629
|
-
let node;
|
|
630
|
-
for (let cursor = remaining - 1; cursor >= 0; cursor -= 1) {
|
|
631
|
-
const parsed = chain[cursor];
|
|
632
|
-
if (parsed === void 0) continue;
|
|
633
|
-
node = {
|
|
634
|
-
element: "list",
|
|
635
|
-
ordered: parsed.ordered,
|
|
636
|
-
start: parsed.start,
|
|
637
|
-
items: [{
|
|
638
|
-
element: "listItem",
|
|
639
|
-
children
|
|
640
|
-
}]
|
|
641
|
-
};
|
|
642
|
-
children = [node];
|
|
643
|
-
}
|
|
644
|
-
if (node !== void 0) return {
|
|
645
|
-
node,
|
|
646
|
-
next: lines.length
|
|
647
|
-
};
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
let index = start;
|
|
651
|
-
while (index < lines.length) {
|
|
652
|
-
const parsed = extractListItem(lines[index] ?? "");
|
|
653
|
-
if (!parsed || parsed.indent > topIndent || parsed.ordered !== ordered) break;
|
|
654
|
-
const itemLines = [parsed.content];
|
|
655
|
-
const continuation = parsed.marker;
|
|
656
|
-
index += 1;
|
|
657
|
-
while (index < lines.length) {
|
|
658
|
-
const next = lines[index] ?? "";
|
|
659
|
-
if (isBlankLine(next)) {
|
|
660
|
-
const after = lines[index + 1] ?? "";
|
|
661
|
-
if (index + 1 < lines.length && !isBlankLine(after) && countIndent(after) >= continuation) {
|
|
662
|
-
itemLines.push("");
|
|
663
|
-
index += 1;
|
|
664
|
-
continue;
|
|
665
|
-
}
|
|
666
|
-
break;
|
|
667
|
-
}
|
|
668
|
-
if (countIndent(next) >= continuation) {
|
|
669
|
-
itemLines.push(next.slice(continuation));
|
|
670
|
-
index += 1;
|
|
671
|
-
continue;
|
|
672
|
-
}
|
|
673
|
-
if (extractListItem(next) || startsBlock(lines, index)) break;
|
|
674
|
-
itemLines.push(next.trim());
|
|
675
|
-
index += 1;
|
|
676
|
-
}
|
|
677
|
-
items.push({
|
|
678
|
-
element: "listItem",
|
|
679
|
-
children: parseBlocks(itemLines, depth + 1)
|
|
680
|
-
});
|
|
681
|
-
}
|
|
682
|
-
return {
|
|
683
|
-
node: {
|
|
684
|
-
element: "list",
|
|
685
|
-
ordered,
|
|
686
|
-
start: startOrdinal,
|
|
687
|
-
items
|
|
688
|
-
},
|
|
689
|
-
next: index
|
|
690
|
-
};
|
|
691
|
-
}
|
|
692
|
-
/**
|
|
693
547
|
* Parses a markdown string into a typed {@link MarkdownDocument} AST via the
|
|
694
548
|
* block phase.
|
|
695
549
|
*
|
|
@@ -1644,6 +1498,152 @@ function scanInline(source, from, to, depth = 0) {
|
|
|
1644
1498
|
return nodes;
|
|
1645
1499
|
}
|
|
1646
1500
|
/**
|
|
1501
|
+
* Collects a GFM table starting at a header row, parsing the header, the
|
|
1502
|
+
* alignment row, and every contiguous body row that follows.
|
|
1503
|
+
*
|
|
1504
|
+
* @param lines - The markdown lines to scan.
|
|
1505
|
+
* @param start - The index of the header row.
|
|
1506
|
+
* @returns The parsed table node and the index of the first line after it.
|
|
1507
|
+
*
|
|
1508
|
+
* @example
|
|
1509
|
+
* ```ts
|
|
1510
|
+
* collectTable(['| a |', '| - |'], 0) // { node: { element: 'table', ... }, next: 2 }
|
|
1511
|
+
* ```
|
|
1512
|
+
*/
|
|
1513
|
+
function collectTable(lines, start) {
|
|
1514
|
+
const headerCells = splitTableRow(lines[start] ?? "");
|
|
1515
|
+
const columns = headerCells.length;
|
|
1516
|
+
const header = headerCells.map((cell) => parseInline(cell.trim()));
|
|
1517
|
+
const align = delimiterToAlignments(lines[start + 1] ?? "");
|
|
1518
|
+
const padded = [];
|
|
1519
|
+
for (let column = 0; column < columns; column += 1) padded.push(align[column] ?? null);
|
|
1520
|
+
const rows = [];
|
|
1521
|
+
let index = start + 2;
|
|
1522
|
+
while (index < lines.length && !isBlankLine(lines[index] ?? "") && (lines[index] ?? "").includes("|")) {
|
|
1523
|
+
const cells = splitTableRow(lines[index] ?? "");
|
|
1524
|
+
const row = [];
|
|
1525
|
+
for (let column = 0; column < columns; column += 1) row.push(parseInline((cells[column] ?? "").trim()));
|
|
1526
|
+
rows.push(row);
|
|
1527
|
+
index += 1;
|
|
1528
|
+
}
|
|
1529
|
+
return {
|
|
1530
|
+
node: {
|
|
1531
|
+
element: "table",
|
|
1532
|
+
header,
|
|
1533
|
+
rows,
|
|
1534
|
+
align: padded
|
|
1535
|
+
},
|
|
1536
|
+
next: index
|
|
1537
|
+
};
|
|
1538
|
+
}
|
|
1539
|
+
/**
|
|
1540
|
+
* Collects a list starting at the first item, gathering sibling items at the
|
|
1541
|
+
* same indent/ordering and recursing into each item's own block content.
|
|
1542
|
+
*
|
|
1543
|
+
* @param lines - The markdown lines to scan.
|
|
1544
|
+
* @param start - The index of the first list item.
|
|
1545
|
+
* @param depth - The current recursion depth (each item recurses at `depth + 1`).
|
|
1546
|
+
* @returns The parsed list node and the index of the first line after it.
|
|
1547
|
+
*
|
|
1548
|
+
* @example
|
|
1549
|
+
* ```ts
|
|
1550
|
+
* collectList(['- item'], 0, 0) // { node: { element: 'list', ... }, next: 1 }
|
|
1551
|
+
* ```
|
|
1552
|
+
*/
|
|
1553
|
+
function collectList(lines, start, depth) {
|
|
1554
|
+
const first = extractListItem(lines[start] ?? "");
|
|
1555
|
+
const ordered = first?.ordered ?? false;
|
|
1556
|
+
const startOrdinal = first?.start ?? 1;
|
|
1557
|
+
const topIndent = first?.indent ?? 0;
|
|
1558
|
+
const items = [];
|
|
1559
|
+
const chain = [];
|
|
1560
|
+
let nested = true;
|
|
1561
|
+
for (let cursor = start; cursor < lines.length; cursor += 1) {
|
|
1562
|
+
const parsed = extractListItem(lines[cursor] ?? "");
|
|
1563
|
+
const previous = chain[chain.length - 1];
|
|
1564
|
+
if (parsed === void 0 || previous !== void 0 && (previous.content.length > 0 || parsed.indent !== previous.marker)) {
|
|
1565
|
+
nested = false;
|
|
1566
|
+
break;
|
|
1567
|
+
}
|
|
1568
|
+
chain.push(parsed);
|
|
1569
|
+
}
|
|
1570
|
+
const remaining = 64 - depth;
|
|
1571
|
+
if (nested && remaining > 0 && chain.length > remaining) {
|
|
1572
|
+
const terminal = chain[remaining - 1];
|
|
1573
|
+
if (terminal !== void 0) {
|
|
1574
|
+
const source = [terminal.content];
|
|
1575
|
+
for (let cursor = start + remaining; cursor < lines.length; cursor += 1) source.push((lines[cursor] ?? "").slice(terminal.marker));
|
|
1576
|
+
let children = [{
|
|
1577
|
+
element: "paragraph",
|
|
1578
|
+
children: [{
|
|
1579
|
+
element: "text",
|
|
1580
|
+
value: source.join("\n")
|
|
1581
|
+
}]
|
|
1582
|
+
}];
|
|
1583
|
+
let node;
|
|
1584
|
+
for (let cursor = remaining - 1; cursor >= 0; cursor -= 1) {
|
|
1585
|
+
const parsed = chain[cursor];
|
|
1586
|
+
if (parsed === void 0) continue;
|
|
1587
|
+
node = {
|
|
1588
|
+
element: "list",
|
|
1589
|
+
ordered: parsed.ordered,
|
|
1590
|
+
start: parsed.start,
|
|
1591
|
+
items: [{
|
|
1592
|
+
element: "listItem",
|
|
1593
|
+
children
|
|
1594
|
+
}]
|
|
1595
|
+
};
|
|
1596
|
+
children = [node];
|
|
1597
|
+
}
|
|
1598
|
+
if (node !== void 0) return {
|
|
1599
|
+
node,
|
|
1600
|
+
next: lines.length
|
|
1601
|
+
};
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
let index = start;
|
|
1605
|
+
while (index < lines.length) {
|
|
1606
|
+
const parsed = extractListItem(lines[index] ?? "");
|
|
1607
|
+
if (!parsed || parsed.indent > topIndent || parsed.ordered !== ordered) break;
|
|
1608
|
+
const itemLines = [parsed.content];
|
|
1609
|
+
const continuation = parsed.marker;
|
|
1610
|
+
index += 1;
|
|
1611
|
+
while (index < lines.length) {
|
|
1612
|
+
const next = lines[index] ?? "";
|
|
1613
|
+
if (isBlankLine(next)) {
|
|
1614
|
+
const after = lines[index + 1] ?? "";
|
|
1615
|
+
if (index + 1 < lines.length && !isBlankLine(after) && countIndent(after) >= continuation) {
|
|
1616
|
+
itemLines.push("");
|
|
1617
|
+
index += 1;
|
|
1618
|
+
continue;
|
|
1619
|
+
}
|
|
1620
|
+
break;
|
|
1621
|
+
}
|
|
1622
|
+
if (countIndent(next) >= continuation) {
|
|
1623
|
+
itemLines.push(next.slice(continuation));
|
|
1624
|
+
index += 1;
|
|
1625
|
+
continue;
|
|
1626
|
+
}
|
|
1627
|
+
if (extractListItem(next) || startsBlock(lines, index)) break;
|
|
1628
|
+
itemLines.push(next.trim());
|
|
1629
|
+
index += 1;
|
|
1630
|
+
}
|
|
1631
|
+
items.push({
|
|
1632
|
+
element: "listItem",
|
|
1633
|
+
children: parseBlocks(itemLines, depth + 1)
|
|
1634
|
+
});
|
|
1635
|
+
}
|
|
1636
|
+
return {
|
|
1637
|
+
node: {
|
|
1638
|
+
element: "list",
|
|
1639
|
+
ordered,
|
|
1640
|
+
start: startOrdinal,
|
|
1641
|
+
items
|
|
1642
|
+
},
|
|
1643
|
+
next: index
|
|
1644
|
+
};
|
|
1645
|
+
}
|
|
1646
|
+
/**
|
|
1647
1647
|
* Project a {@link MarkdownNode} into an unsanitized {@link HTMLDocument}.
|
|
1648
1648
|
*
|
|
1649
1649
|
* @remarks
|
|
@@ -297,7 +297,7 @@ export declare function createThematicBreakContract(): ContractInterface<Themati
|
|
|
297
297
|
* delimiterToAlignments('| :--- | ---: |') // ['left', 'right']
|
|
298
298
|
* ```
|
|
299
299
|
*/
|
|
300
|
-
export declare function delimiterToAlignments(delimiter: string):
|
|
300
|
+
export declare function delimiterToAlignments(delimiter: string): ReadonlyArray<TableAlign | null>;
|
|
301
301
|
|
|
302
302
|
/**
|
|
303
303
|
* Emphasized inline content - `*italic*` / `_italic_` (`strong: false`) or
|
|
@@ -1184,7 +1184,7 @@ export declare interface MarkdownProjection {
|
|
|
1184
1184
|
/** The cells this node contributes to an enclosing row. */
|
|
1185
1185
|
readonly cells: readonly MarkdownCell[];
|
|
1186
1186
|
/** The rows this node contributes to an enclosing table - each its cells, in column order. */
|
|
1187
|
-
readonly rows: readonly
|
|
1187
|
+
readonly rows: ReadonlyArray<readonly MarkdownCell[]>;
|
|
1188
1188
|
}
|
|
1189
1189
|
|
|
1190
1190
|
/**
|
|
@@ -1701,16 +1701,16 @@ export declare const tableAlignShape: LiteralShape<readonly ["left", "right", "c
|
|
|
1701
1701
|
export declare interface TableNode {
|
|
1702
1702
|
readonly element: 'table';
|
|
1703
1703
|
/** The header row - one cell of inline content per column. */
|
|
1704
|
-
readonly header: readonly
|
|
1704
|
+
readonly header: ReadonlyArray<readonly InlineNode[]>;
|
|
1705
1705
|
/** The body rows - each a list of cells, each cell inline content. */
|
|
1706
|
-
readonly rows: readonly
|
|
1706
|
+
readonly rows: ReadonlyArray<ReadonlyArray<readonly InlineNode[]>>;
|
|
1707
1707
|
/**
|
|
1708
1708
|
* The per-column alignment from the delimiter row, in column order. `null`
|
|
1709
1709
|
* represents a bare `---` delimiter because this positional array requires one
|
|
1710
1710
|
* entry per column, JSON cannot carry `undefined` in an array, and the delimiter
|
|
1711
1711
|
* is an explicit no-alignment marker rather than an omitted value.
|
|
1712
1712
|
*/
|
|
1713
|
-
readonly align:
|
|
1713
|
+
readonly align: ReadonlyArray<TableAlign | null>;
|
|
1714
1714
|
}
|
|
1715
1715
|
|
|
1716
1716
|
/**
|
package/dist/src/core/index.d.ts
CHANGED
|
@@ -297,7 +297,7 @@ export declare function createThematicBreakContract(): ContractInterface<Themati
|
|
|
297
297
|
* delimiterToAlignments('| :--- | ---: |') // ['left', 'right']
|
|
298
298
|
* ```
|
|
299
299
|
*/
|
|
300
|
-
export declare function delimiterToAlignments(delimiter: string):
|
|
300
|
+
export declare function delimiterToAlignments(delimiter: string): ReadonlyArray<TableAlign | null>;
|
|
301
301
|
|
|
302
302
|
/**
|
|
303
303
|
* Emphasized inline content - `*italic*` / `_italic_` (`strong: false`) or
|
|
@@ -1184,7 +1184,7 @@ export declare interface MarkdownProjection {
|
|
|
1184
1184
|
/** The cells this node contributes to an enclosing row. */
|
|
1185
1185
|
readonly cells: readonly MarkdownCell[];
|
|
1186
1186
|
/** The rows this node contributes to an enclosing table - each its cells, in column order. */
|
|
1187
|
-
readonly rows: readonly
|
|
1187
|
+
readonly rows: ReadonlyArray<readonly MarkdownCell[]>;
|
|
1188
1188
|
}
|
|
1189
1189
|
|
|
1190
1190
|
/**
|
|
@@ -1701,16 +1701,16 @@ export declare const tableAlignShape: LiteralShape<readonly ["left", "right", "c
|
|
|
1701
1701
|
export declare interface TableNode {
|
|
1702
1702
|
readonly element: 'table';
|
|
1703
1703
|
/** The header row - one cell of inline content per column. */
|
|
1704
|
-
readonly header: readonly
|
|
1704
|
+
readonly header: ReadonlyArray<readonly InlineNode[]>;
|
|
1705
1705
|
/** The body rows - each a list of cells, each cell inline content. */
|
|
1706
|
-
readonly rows: readonly
|
|
1706
|
+
readonly rows: ReadonlyArray<ReadonlyArray<readonly InlineNode[]>>;
|
|
1707
1707
|
/**
|
|
1708
1708
|
* The per-column alignment from the delimiter row, in column order. `null`
|
|
1709
1709
|
* represents a bare `---` delimiter because this positional array requires one
|
|
1710
1710
|
* entry per column, JSON cannot carry `undefined` in an array, and the delimiter
|
|
1711
1711
|
* is an explicit no-alignment marker rather than an omitted value.
|
|
1712
1712
|
*/
|
|
1713
|
-
readonly align:
|
|
1713
|
+
readonly align: ReadonlyArray<TableAlign | null>;
|
|
1714
1714
|
}
|
|
1715
1715
|
|
|
1716
1716
|
/**
|
package/dist/src/core/index.js
CHANGED
|
@@ -543,152 +543,6 @@ function parseBlocks(lines, depth) {
|
|
|
543
543
|
return blocks;
|
|
544
544
|
}
|
|
545
545
|
/**
|
|
546
|
-
* Collects a GFM table starting at a header row, parsing the header, the
|
|
547
|
-
* alignment row, and every contiguous body row that follows.
|
|
548
|
-
*
|
|
549
|
-
* @param lines - The markdown lines to scan.
|
|
550
|
-
* @param start - The index of the header row.
|
|
551
|
-
* @returns The parsed table node and the index of the first line after it.
|
|
552
|
-
*
|
|
553
|
-
* @example
|
|
554
|
-
* ```ts
|
|
555
|
-
* collectTable(['| a |', '| - |'], 0) // { node: { element: 'table', ... }, next: 2 }
|
|
556
|
-
* ```
|
|
557
|
-
*/
|
|
558
|
-
function collectTable(lines, start) {
|
|
559
|
-
const headerCells = splitTableRow(lines[start] ?? "");
|
|
560
|
-
const columns = headerCells.length;
|
|
561
|
-
const header = headerCells.map((cell) => parseInline(cell.trim()));
|
|
562
|
-
const align = delimiterToAlignments(lines[start + 1] ?? "");
|
|
563
|
-
const padded = [];
|
|
564
|
-
for (let column = 0; column < columns; column += 1) padded.push(align[column] ?? null);
|
|
565
|
-
const rows = [];
|
|
566
|
-
let index = start + 2;
|
|
567
|
-
while (index < lines.length && !isBlankLine(lines[index] ?? "") && (lines[index] ?? "").includes("|")) {
|
|
568
|
-
const cells = splitTableRow(lines[index] ?? "");
|
|
569
|
-
const row = [];
|
|
570
|
-
for (let column = 0; column < columns; column += 1) row.push(parseInline((cells[column] ?? "").trim()));
|
|
571
|
-
rows.push(row);
|
|
572
|
-
index += 1;
|
|
573
|
-
}
|
|
574
|
-
return {
|
|
575
|
-
node: {
|
|
576
|
-
element: "table",
|
|
577
|
-
header,
|
|
578
|
-
rows,
|
|
579
|
-
align: padded
|
|
580
|
-
},
|
|
581
|
-
next: index
|
|
582
|
-
};
|
|
583
|
-
}
|
|
584
|
-
/**
|
|
585
|
-
* Collects a list starting at the first item, gathering sibling items at the
|
|
586
|
-
* same indent/ordering and recursing into each item's own block content.
|
|
587
|
-
*
|
|
588
|
-
* @param lines - The markdown lines to scan.
|
|
589
|
-
* @param start - The index of the first list item.
|
|
590
|
-
* @param depth - The current recursion depth (each item recurses at `depth + 1`).
|
|
591
|
-
* @returns The parsed list node and the index of the first line after it.
|
|
592
|
-
*
|
|
593
|
-
* @example
|
|
594
|
-
* ```ts
|
|
595
|
-
* collectList(['- item'], 0, 0) // { node: { element: 'list', ... }, next: 1 }
|
|
596
|
-
* ```
|
|
597
|
-
*/
|
|
598
|
-
function collectList(lines, start, depth) {
|
|
599
|
-
const first = extractListItem(lines[start] ?? "");
|
|
600
|
-
const ordered = first?.ordered ?? false;
|
|
601
|
-
const startOrdinal = first?.start ?? 1;
|
|
602
|
-
const topIndent = first?.indent ?? 0;
|
|
603
|
-
const items = [];
|
|
604
|
-
const chain = [];
|
|
605
|
-
let nested = true;
|
|
606
|
-
for (let cursor = start; cursor < lines.length; cursor += 1) {
|
|
607
|
-
const parsed = extractListItem(lines[cursor] ?? "");
|
|
608
|
-
const previous = chain[chain.length - 1];
|
|
609
|
-
if (parsed === void 0 || previous !== void 0 && (previous.content.length > 0 || parsed.indent !== previous.marker)) {
|
|
610
|
-
nested = false;
|
|
611
|
-
break;
|
|
612
|
-
}
|
|
613
|
-
chain.push(parsed);
|
|
614
|
-
}
|
|
615
|
-
const remaining = 64 - depth;
|
|
616
|
-
if (nested && remaining > 0 && chain.length > remaining) {
|
|
617
|
-
const terminal = chain[remaining - 1];
|
|
618
|
-
if (terminal !== void 0) {
|
|
619
|
-
const source = [terminal.content];
|
|
620
|
-
for (let cursor = start + remaining; cursor < lines.length; cursor += 1) source.push((lines[cursor] ?? "").slice(terminal.marker));
|
|
621
|
-
let children = [{
|
|
622
|
-
element: "paragraph",
|
|
623
|
-
children: [{
|
|
624
|
-
element: "text",
|
|
625
|
-
value: source.join("\n")
|
|
626
|
-
}]
|
|
627
|
-
}];
|
|
628
|
-
let node;
|
|
629
|
-
for (let cursor = remaining - 1; cursor >= 0; cursor -= 1) {
|
|
630
|
-
const parsed = chain[cursor];
|
|
631
|
-
if (parsed === void 0) continue;
|
|
632
|
-
node = {
|
|
633
|
-
element: "list",
|
|
634
|
-
ordered: parsed.ordered,
|
|
635
|
-
start: parsed.start,
|
|
636
|
-
items: [{
|
|
637
|
-
element: "listItem",
|
|
638
|
-
children
|
|
639
|
-
}]
|
|
640
|
-
};
|
|
641
|
-
children = [node];
|
|
642
|
-
}
|
|
643
|
-
if (node !== void 0) return {
|
|
644
|
-
node,
|
|
645
|
-
next: lines.length
|
|
646
|
-
};
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
let index = start;
|
|
650
|
-
while (index < lines.length) {
|
|
651
|
-
const parsed = extractListItem(lines[index] ?? "");
|
|
652
|
-
if (!parsed || parsed.indent > topIndent || parsed.ordered !== ordered) break;
|
|
653
|
-
const itemLines = [parsed.content];
|
|
654
|
-
const continuation = parsed.marker;
|
|
655
|
-
index += 1;
|
|
656
|
-
while (index < lines.length) {
|
|
657
|
-
const next = lines[index] ?? "";
|
|
658
|
-
if (isBlankLine(next)) {
|
|
659
|
-
const after = lines[index + 1] ?? "";
|
|
660
|
-
if (index + 1 < lines.length && !isBlankLine(after) && countIndent(after) >= continuation) {
|
|
661
|
-
itemLines.push("");
|
|
662
|
-
index += 1;
|
|
663
|
-
continue;
|
|
664
|
-
}
|
|
665
|
-
break;
|
|
666
|
-
}
|
|
667
|
-
if (countIndent(next) >= continuation) {
|
|
668
|
-
itemLines.push(next.slice(continuation));
|
|
669
|
-
index += 1;
|
|
670
|
-
continue;
|
|
671
|
-
}
|
|
672
|
-
if (extractListItem(next) || startsBlock(lines, index)) break;
|
|
673
|
-
itemLines.push(next.trim());
|
|
674
|
-
index += 1;
|
|
675
|
-
}
|
|
676
|
-
items.push({
|
|
677
|
-
element: "listItem",
|
|
678
|
-
children: parseBlocks(itemLines, depth + 1)
|
|
679
|
-
});
|
|
680
|
-
}
|
|
681
|
-
return {
|
|
682
|
-
node: {
|
|
683
|
-
element: "list",
|
|
684
|
-
ordered,
|
|
685
|
-
start: startOrdinal,
|
|
686
|
-
items
|
|
687
|
-
},
|
|
688
|
-
next: index
|
|
689
|
-
};
|
|
690
|
-
}
|
|
691
|
-
/**
|
|
692
546
|
* Parses a markdown string into a typed {@link MarkdownDocument} AST via the
|
|
693
547
|
* block phase.
|
|
694
548
|
*
|
|
@@ -1643,6 +1497,152 @@ function scanInline(source, from, to, depth = 0) {
|
|
|
1643
1497
|
return nodes;
|
|
1644
1498
|
}
|
|
1645
1499
|
/**
|
|
1500
|
+
* Collects a GFM table starting at a header row, parsing the header, the
|
|
1501
|
+
* alignment row, and every contiguous body row that follows.
|
|
1502
|
+
*
|
|
1503
|
+
* @param lines - The markdown lines to scan.
|
|
1504
|
+
* @param start - The index of the header row.
|
|
1505
|
+
* @returns The parsed table node and the index of the first line after it.
|
|
1506
|
+
*
|
|
1507
|
+
* @example
|
|
1508
|
+
* ```ts
|
|
1509
|
+
* collectTable(['| a |', '| - |'], 0) // { node: { element: 'table', ... }, next: 2 }
|
|
1510
|
+
* ```
|
|
1511
|
+
*/
|
|
1512
|
+
function collectTable(lines, start) {
|
|
1513
|
+
const headerCells = splitTableRow(lines[start] ?? "");
|
|
1514
|
+
const columns = headerCells.length;
|
|
1515
|
+
const header = headerCells.map((cell) => parseInline(cell.trim()));
|
|
1516
|
+
const align = delimiterToAlignments(lines[start + 1] ?? "");
|
|
1517
|
+
const padded = [];
|
|
1518
|
+
for (let column = 0; column < columns; column += 1) padded.push(align[column] ?? null);
|
|
1519
|
+
const rows = [];
|
|
1520
|
+
let index = start + 2;
|
|
1521
|
+
while (index < lines.length && !isBlankLine(lines[index] ?? "") && (lines[index] ?? "").includes("|")) {
|
|
1522
|
+
const cells = splitTableRow(lines[index] ?? "");
|
|
1523
|
+
const row = [];
|
|
1524
|
+
for (let column = 0; column < columns; column += 1) row.push(parseInline((cells[column] ?? "").trim()));
|
|
1525
|
+
rows.push(row);
|
|
1526
|
+
index += 1;
|
|
1527
|
+
}
|
|
1528
|
+
return {
|
|
1529
|
+
node: {
|
|
1530
|
+
element: "table",
|
|
1531
|
+
header,
|
|
1532
|
+
rows,
|
|
1533
|
+
align: padded
|
|
1534
|
+
},
|
|
1535
|
+
next: index
|
|
1536
|
+
};
|
|
1537
|
+
}
|
|
1538
|
+
/**
|
|
1539
|
+
* Collects a list starting at the first item, gathering sibling items at the
|
|
1540
|
+
* same indent/ordering and recursing into each item's own block content.
|
|
1541
|
+
*
|
|
1542
|
+
* @param lines - The markdown lines to scan.
|
|
1543
|
+
* @param start - The index of the first list item.
|
|
1544
|
+
* @param depth - The current recursion depth (each item recurses at `depth + 1`).
|
|
1545
|
+
* @returns The parsed list node and the index of the first line after it.
|
|
1546
|
+
*
|
|
1547
|
+
* @example
|
|
1548
|
+
* ```ts
|
|
1549
|
+
* collectList(['- item'], 0, 0) // { node: { element: 'list', ... }, next: 1 }
|
|
1550
|
+
* ```
|
|
1551
|
+
*/
|
|
1552
|
+
function collectList(lines, start, depth) {
|
|
1553
|
+
const first = extractListItem(lines[start] ?? "");
|
|
1554
|
+
const ordered = first?.ordered ?? false;
|
|
1555
|
+
const startOrdinal = first?.start ?? 1;
|
|
1556
|
+
const topIndent = first?.indent ?? 0;
|
|
1557
|
+
const items = [];
|
|
1558
|
+
const chain = [];
|
|
1559
|
+
let nested = true;
|
|
1560
|
+
for (let cursor = start; cursor < lines.length; cursor += 1) {
|
|
1561
|
+
const parsed = extractListItem(lines[cursor] ?? "");
|
|
1562
|
+
const previous = chain[chain.length - 1];
|
|
1563
|
+
if (parsed === void 0 || previous !== void 0 && (previous.content.length > 0 || parsed.indent !== previous.marker)) {
|
|
1564
|
+
nested = false;
|
|
1565
|
+
break;
|
|
1566
|
+
}
|
|
1567
|
+
chain.push(parsed);
|
|
1568
|
+
}
|
|
1569
|
+
const remaining = 64 - depth;
|
|
1570
|
+
if (nested && remaining > 0 && chain.length > remaining) {
|
|
1571
|
+
const terminal = chain[remaining - 1];
|
|
1572
|
+
if (terminal !== void 0) {
|
|
1573
|
+
const source = [terminal.content];
|
|
1574
|
+
for (let cursor = start + remaining; cursor < lines.length; cursor += 1) source.push((lines[cursor] ?? "").slice(terminal.marker));
|
|
1575
|
+
let children = [{
|
|
1576
|
+
element: "paragraph",
|
|
1577
|
+
children: [{
|
|
1578
|
+
element: "text",
|
|
1579
|
+
value: source.join("\n")
|
|
1580
|
+
}]
|
|
1581
|
+
}];
|
|
1582
|
+
let node;
|
|
1583
|
+
for (let cursor = remaining - 1; cursor >= 0; cursor -= 1) {
|
|
1584
|
+
const parsed = chain[cursor];
|
|
1585
|
+
if (parsed === void 0) continue;
|
|
1586
|
+
node = {
|
|
1587
|
+
element: "list",
|
|
1588
|
+
ordered: parsed.ordered,
|
|
1589
|
+
start: parsed.start,
|
|
1590
|
+
items: [{
|
|
1591
|
+
element: "listItem",
|
|
1592
|
+
children
|
|
1593
|
+
}]
|
|
1594
|
+
};
|
|
1595
|
+
children = [node];
|
|
1596
|
+
}
|
|
1597
|
+
if (node !== void 0) return {
|
|
1598
|
+
node,
|
|
1599
|
+
next: lines.length
|
|
1600
|
+
};
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
let index = start;
|
|
1604
|
+
while (index < lines.length) {
|
|
1605
|
+
const parsed = extractListItem(lines[index] ?? "");
|
|
1606
|
+
if (!parsed || parsed.indent > topIndent || parsed.ordered !== ordered) break;
|
|
1607
|
+
const itemLines = [parsed.content];
|
|
1608
|
+
const continuation = parsed.marker;
|
|
1609
|
+
index += 1;
|
|
1610
|
+
while (index < lines.length) {
|
|
1611
|
+
const next = lines[index] ?? "";
|
|
1612
|
+
if (isBlankLine(next)) {
|
|
1613
|
+
const after = lines[index + 1] ?? "";
|
|
1614
|
+
if (index + 1 < lines.length && !isBlankLine(after) && countIndent(after) >= continuation) {
|
|
1615
|
+
itemLines.push("");
|
|
1616
|
+
index += 1;
|
|
1617
|
+
continue;
|
|
1618
|
+
}
|
|
1619
|
+
break;
|
|
1620
|
+
}
|
|
1621
|
+
if (countIndent(next) >= continuation) {
|
|
1622
|
+
itemLines.push(next.slice(continuation));
|
|
1623
|
+
index += 1;
|
|
1624
|
+
continue;
|
|
1625
|
+
}
|
|
1626
|
+
if (extractListItem(next) || startsBlock(lines, index)) break;
|
|
1627
|
+
itemLines.push(next.trim());
|
|
1628
|
+
index += 1;
|
|
1629
|
+
}
|
|
1630
|
+
items.push({
|
|
1631
|
+
element: "listItem",
|
|
1632
|
+
children: parseBlocks(itemLines, depth + 1)
|
|
1633
|
+
});
|
|
1634
|
+
}
|
|
1635
|
+
return {
|
|
1636
|
+
node: {
|
|
1637
|
+
element: "list",
|
|
1638
|
+
ordered,
|
|
1639
|
+
start: startOrdinal,
|
|
1640
|
+
items
|
|
1641
|
+
},
|
|
1642
|
+
next: index
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
/**
|
|
1646
1646
|
* Project a {@link MarkdownNode} into an unsanitized {@link HTMLDocument}.
|
|
1647
1647
|
*
|
|
1648
1648
|
* @remarks
|