@react-x11/components 0.8.0 → 0.9.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 +95 -34
- package/dist/charts/node.d.ts +1 -1
- package/dist/charts/node.d.ts.map +1 -1
- package/dist/charts/node.js +6 -2
- package/dist/charts/node.js.map +1 -1
- package/dist/embed/client.d.ts +48 -3
- package/dist/embed/client.d.ts.map +1 -1
- package/dist/embed/client.js +79 -5
- package/dist/embed/client.js.map +1 -1
- package/dist/embed/index.d.ts +1 -1
- package/dist/embed/index.d.ts.map +1 -1
- package/dist/embed/index.js +1 -1
- package/dist/embed/index.js.map +1 -1
- package/dist/html/node.d.ts +3 -1
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +7 -5
- package/dist/html/node.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/{markdown → internal/markdown}/ast.d.ts +12 -0
- package/dist/internal/markdown/ast.d.ts.map +1 -0
- package/dist/internal/markdown/ast.js +22 -0
- package/dist/internal/markdown/ast.js.map +1 -0
- package/dist/internal/markdown/parse.d.ts.map +1 -0
- package/dist/{markdown → internal/markdown}/parse.js +112 -22
- package/dist/internal/markdown/parse.js.map +1 -0
- package/dist/internal/markdown/stringify.d.ts +4 -0
- package/dist/internal/markdown/stringify.d.ts.map +1 -0
- package/dist/internal/markdown/stringify.js +684 -0
- package/dist/internal/markdown/stringify.js.map +1 -0
- package/dist/internal/markdown/tags.d.ts.map +1 -0
- package/dist/internal/markdown/tags.js.map +1 -0
- package/dist/internal/scroll.d.ts +3 -2
- package/dist/internal/scroll.d.ts.map +1 -1
- package/dist/internal/scroll.js +1 -0
- package/dist/internal/scroll.js.map +1 -1
- package/dist/maps/anchors.d.ts +98 -0
- package/dist/maps/anchors.d.ts.map +1 -0
- package/dist/maps/anchors.js +531 -0
- package/dist/maps/anchors.js.map +1 -0
- package/dist/maps/controller.d.ts +283 -0
- package/dist/maps/controller.d.ts.map +1 -0
- package/dist/maps/controller.js +743 -0
- package/dist/maps/controller.js.map +1 -0
- package/dist/maps/gl/buckets.d.ts +95 -0
- package/dist/maps/gl/buckets.d.ts.map +1 -0
- package/dist/maps/gl/buckets.js +362 -0
- package/dist/maps/gl/buckets.js.map +1 -0
- package/dist/maps/gl/build-worker.d.ts +13 -0
- package/dist/maps/gl/build-worker.d.ts.map +1 -0
- package/dist/maps/gl/build-worker.js +44 -0
- package/dist/maps/gl/build-worker.js.map +1 -0
- package/dist/maps/gl/color.d.ts +8 -0
- package/dist/maps/gl/color.d.ts.map +1 -0
- package/dist/maps/gl/color.js +57 -0
- package/dist/maps/gl/color.js.map +1 -0
- package/dist/maps/gl/cover.d.ts +51 -0
- package/dist/maps/gl/cover.d.ts.map +1 -0
- package/dist/maps/gl/cover.js +124 -0
- package/dist/maps/gl/cover.js.map +1 -0
- package/dist/maps/gl/index.d.ts +3 -0
- package/dist/maps/gl/index.d.ts.map +1 -0
- package/dist/maps/gl/index.js +20 -0
- package/dist/maps/gl/index.js.map +1 -0
- package/dist/maps/gl/markers.d.ts +28 -0
- package/dist/maps/gl/markers.d.ts.map +1 -0
- package/dist/maps/gl/markers.js +60 -0
- package/dist/maps/gl/markers.js.map +1 -0
- package/dist/maps/gl/overlays.d.ts +68 -0
- package/dist/maps/gl/overlays.d.ts.map +1 -0
- package/dist/maps/gl/overlays.js +300 -0
- package/dist/maps/gl/overlays.js.map +1 -0
- package/dist/maps/gl/pane.d.ts +12 -0
- package/dist/maps/gl/pane.d.ts.map +1 -0
- package/dist/maps/gl/pane.js +37 -0
- package/dist/maps/gl/pane.js.map +1 -0
- package/dist/maps/gl/placement.d.ts +111 -0
- package/dist/maps/gl/placement.d.ts.map +1 -0
- package/dist/maps/gl/placement.js +567 -0
- package/dist/maps/gl/placement.js.map +1 -0
- package/dist/maps/gl/renderer.d.ts +352 -0
- package/dist/maps/gl/renderer.d.ts.map +1 -0
- package/dist/maps/gl/renderer.js +1127 -0
- package/dist/maps/gl/renderer.js.map +1 -0
- package/dist/maps/gl/shaders.d.ts +120 -0
- package/dist/maps/gl/shaders.d.ts.map +1 -0
- package/dist/maps/gl/shaders.js +451 -0
- package/dist/maps/gl/shaders.js.map +1 -0
- package/dist/maps/gl/store.d.ts +115 -0
- package/dist/maps/gl/store.d.ts.map +1 -0
- package/dist/maps/gl/store.js +469 -0
- package/dist/maps/gl/store.js.map +1 -0
- package/dist/maps/gl/text.d.ts +154 -0
- package/dist/maps/gl/text.d.ts.map +1 -0
- package/dist/maps/gl/text.js +382 -0
- package/dist/maps/gl/text.js.map +1 -0
- package/dist/maps/gl/view.d.ts +54 -0
- package/dist/maps/gl/view.d.ts.map +1 -0
- package/dist/maps/gl/view.js +974 -0
- package/dist/maps/gl/view.js.map +1 -0
- package/dist/maps/index.d.ts +1 -1
- package/dist/maps/index.d.ts.map +1 -1
- package/dist/maps/index.js +216 -52
- package/dist/maps/index.js.map +1 -1
- package/dist/maps/labels.d.ts +37 -16
- package/dist/maps/labels.d.ts.map +1 -1
- package/dist/maps/labels.js +188 -119
- package/dist/maps/labels.js.map +1 -1
- package/dist/maps/node.d.ts +162 -56
- package/dist/maps/node.d.ts.map +1 -1
- package/dist/maps/node.js +607 -515
- package/dist/maps/node.js.map +1 -1
- package/dist/maps/overlay.d.ts +72 -9
- package/dist/maps/overlay.d.ts.map +1 -1
- package/dist/maps/overlay.js +108 -28
- package/dist/maps/overlay.js.map +1 -1
- package/dist/maps/paint.d.ts +3 -0
- package/dist/maps/paint.d.ts.map +1 -1
- package/dist/maps/paint.js.map +1 -1
- package/dist/maps/proj.d.ts +14 -0
- package/dist/maps/proj.d.ts.map +1 -1
- package/dist/maps/proj.js +28 -0
- package/dist/maps/proj.js.map +1 -1
- package/dist/maps/renderer.d.ts +69 -0
- package/dist/maps/renderer.d.ts.map +1 -0
- package/dist/maps/renderer.js +66 -0
- package/dist/maps/renderer.js.map +1 -0
- package/dist/maps/sources.d.ts +25 -3
- package/dist/maps/sources.d.ts.map +1 -1
- package/dist/maps/sources.js +17 -0
- package/dist/maps/sources.js.map +1 -1
- package/dist/maps/theme.d.ts +19 -0
- package/dist/maps/theme.d.ts.map +1 -0
- package/dist/maps/theme.js +44 -0
- package/dist/maps/theme.js.map +1 -0
- package/dist/maps/tiles.d.ts +117 -17
- package/dist/maps/tiles.d.ts.map +1 -1
- package/dist/maps/tiles.js +163 -30
- package/dist/maps/tiles.js.map +1 -1
- package/dist/maps/types.d.ts +188 -20
- package/dist/maps/types.d.ts.map +1 -1
- package/dist/markdown/expressions.d.ts +1 -1
- package/dist/markdown/expressions.d.ts.map +1 -1
- package/dist/markdown/expressions.js.map +1 -1
- package/dist/markdown/index.d.ts +7 -6
- package/dist/markdown/index.d.ts.map +1 -1
- package/dist/markdown/index.js +9 -7
- package/dist/markdown/index.js.map +1 -1
- package/dist/markdown/spans.d.ts +1 -1
- package/dist/markdown/spans.d.ts.map +1 -1
- package/dist/media-player/index.d.ts +11 -3
- package/dist/media-player/index.d.ts.map +1 -1
- package/dist/media-player/index.js +13 -2
- package/dist/media-player/index.js.map +1 -1
- package/dist/rich-text-editor/clipboard.d.ts +23 -0
- package/dist/rich-text-editor/clipboard.d.ts.map +1 -0
- package/dist/rich-text-editor/clipboard.js +307 -0
- package/dist/rich-text-editor/clipboard.js.map +1 -0
- package/dist/rich-text-editor/collab.d.ts +32 -0
- package/dist/rich-text-editor/collab.d.ts.map +1 -0
- package/dist/rich-text-editor/collab.js +55 -0
- package/dist/rich-text-editor/collab.js.map +1 -0
- package/dist/rich-text-editor/commands.d.ts +50 -0
- package/dist/rich-text-editor/commands.d.ts.map +1 -0
- package/dist/rich-text-editor/commands.js +376 -0
- package/dist/rich-text-editor/commands.js.map +1 -0
- package/dist/rich-text-editor/drag.d.ts +68 -0
- package/dist/rich-text-editor/drag.d.ts.map +1 -0
- package/dist/rich-text-editor/drag.js +148 -0
- package/dist/rich-text-editor/drag.js.map +1 -0
- package/dist/rich-text-editor/html.d.ts +145 -0
- package/dist/rich-text-editor/html.d.ts.map +1 -0
- package/dist/rich-text-editor/html.js +516 -0
- package/dist/rich-text-editor/html.js.map +1 -0
- package/dist/rich-text-editor/index.d.ts +263 -0
- package/dist/rich-text-editor/index.d.ts.map +1 -0
- package/dist/rich-text-editor/index.js +822 -0
- package/dist/rich-text-editor/index.js.map +1 -0
- package/dist/rich-text-editor/inline.d.ts +94 -0
- package/dist/rich-text-editor/inline.d.ts.map +1 -0
- package/dist/rich-text-editor/inline.js +252 -0
- package/dist/rich-text-editor/inline.js.map +1 -0
- package/dist/rich-text-editor/keymap.d.ts +71 -0
- package/dist/rich-text-editor/keymap.d.ts.map +1 -0
- package/dist/rich-text-editor/keymap.js +393 -0
- package/dist/rich-text-editor/keymap.js.map +1 -0
- package/dist/rich-text-editor/keys.d.ts +29 -0
- package/dist/rich-text-editor/keys.d.ts.map +1 -0
- package/dist/rich-text-editor/keys.js +93 -0
- package/dist/rich-text-editor/keys.js.map +1 -0
- package/dist/rich-text-editor/look.d.ts +71 -0
- package/dist/rich-text-editor/look.d.ts.map +1 -0
- package/dist/rich-text-editor/look.js +342 -0
- package/dist/rich-text-editor/look.js.map +1 -0
- package/dist/rich-text-editor/markdown.d.ts +18 -0
- package/dist/rich-text-editor/markdown.d.ts.map +1 -0
- package/dist/rich-text-editor/markdown.js +496 -0
- package/dist/rich-text-editor/markdown.js.map +1 -0
- package/dist/rich-text-editor/nodes.d.ts +135 -0
- package/dist/rich-text-editor/nodes.d.ts.map +1 -0
- package/dist/rich-text-editor/nodes.js +397 -0
- package/dist/rich-text-editor/nodes.js.map +1 -0
- package/dist/rich-text-editor/render.d.ts +68 -0
- package/dist/rich-text-editor/render.d.ts.map +1 -0
- package/dist/rich-text-editor/render.js +500 -0
- package/dist/rich-text-editor/render.js.map +1 -0
- package/dist/rich-text-editor/schema.d.ts +16 -0
- package/dist/rich-text-editor/schema.d.ts.map +1 -0
- package/dist/rich-text-editor/schema.js +386 -0
- package/dist/rich-text-editor/schema.js.map +1 -0
- package/dist/rich-text-editor/store.d.ts +11 -0
- package/dist/rich-text-editor/store.d.ts.map +1 -0
- package/dist/rich-text-editor/store.js +43 -0
- package/dist/rich-text-editor/store.js.map +1 -0
- package/dist/rich-text-editor/suggest.d.ts +127 -0
- package/dist/rich-text-editor/suggest.d.ts.map +1 -0
- package/dist/rich-text-editor/suggest.js +0 -0
- package/dist/rich-text-editor/suggest.js.map +1 -0
- package/dist/rich-text-editor/tables.d.ts +41 -0
- package/dist/rich-text-editor/tables.d.ts.map +1 -0
- package/dist/rich-text-editor/tables.js +204 -0
- package/dist/rich-text-editor/tables.js.map +1 -0
- package/dist/rich-text-editor/toolbar.d.ts +58 -0
- package/dist/rich-text-editor/toolbar.d.ts.map +1 -0
- package/dist/rich-text-editor/toolbar.js +548 -0
- package/dist/rich-text-editor/toolbar.js.map +1 -0
- package/dist/rich-text-editor/view.d.ts +368 -0
- package/dist/rich-text-editor/view.d.ts.map +1 -0
- package/dist/rich-text-editor/view.js +1968 -0
- package/dist/rich-text-editor/view.js.map +1 -0
- package/dist/rich-text-editor/virtual.d.ts +54 -0
- package/dist/rich-text-editor/virtual.d.ts.map +1 -0
- package/dist/rich-text-editor/virtual.js +262 -0
- package/dist/rich-text-editor/virtual.js.map +1 -0
- package/dist/richtext/node.d.ts +25 -2
- package/dist/richtext/node.d.ts.map +1 -1
- package/dist/richtext/node.js +46 -24
- package/dist/richtext/node.js.map +1 -1
- package/dist/terminal/backends.d.ts +4 -1
- package/dist/terminal/backends.d.ts.map +1 -1
- package/dist/terminal/backends.js.map +1 -1
- package/dist/terminal/index.d.ts +18 -7
- package/dist/terminal/index.d.ts.map +1 -1
- package/dist/terminal/index.js +28 -11
- package/dist/terminal/index.js.map +1 -1
- package/package.json +23 -4
- package/src/charts/node.ts +6 -2
- package/src/embed/client.ts +102 -8
- package/src/embed/index.ts +2 -0
- package/src/html/node.ts +7 -5
- package/src/index.ts +2 -0
- package/src/{markdown → internal/markdown}/ast.ts +24 -7
- package/src/{markdown → internal/markdown}/parse.ts +134 -24
- package/src/internal/markdown/stringify.ts +772 -0
- package/src/internal/scroll.ts +5 -3
- package/src/maps/anchors.ts +607 -0
- package/src/maps/controller.ts +999 -0
- package/src/maps/gl/buckets.ts +457 -0
- package/src/maps/gl/build-worker.ts +55 -0
- package/src/maps/gl/color.ts +62 -0
- package/src/maps/gl/cover.ts +170 -0
- package/src/maps/gl/index.ts +23 -0
- package/src/maps/gl/markers.ts +83 -0
- package/src/maps/gl/overlays.ts +379 -0
- package/src/maps/gl/pane.ts +42 -0
- package/src/maps/gl/placement.ts +816 -0
- package/src/maps/gl/renderer.ts +1576 -0
- package/src/maps/gl/shaders.ts +486 -0
- package/src/maps/gl/store.ts +566 -0
- package/src/maps/gl/text.ts +495 -0
- package/src/maps/gl/view.ts +1224 -0
- package/src/maps/index.ts +270 -81
- package/src/maps/labels.ts +214 -121
- package/src/maps/node.ts +763 -629
- package/src/maps/overlay.ts +148 -43
- package/src/maps/paint.ts +3 -0
- package/src/maps/proj.ts +31 -0
- package/src/maps/renderer.ts +155 -0
- package/src/maps/sources.ts +38 -3
- package/src/maps/theme.ts +51 -0
- package/src/maps/tiles.ts +191 -33
- package/src/maps/types.ts +188 -20
- package/src/markdown/expressions.ts +5 -1
- package/src/markdown/index.ts +14 -11
- package/src/markdown/spans.ts +1 -1
- package/src/media-player/index.ts +18 -4
- package/src/rich-text-editor/clipboard.ts +439 -0
- package/src/rich-text-editor/collab.ts +91 -0
- package/src/rich-text-editor/commands.ts +448 -0
- package/src/rich-text-editor/dom-globals.d.ts +23 -0
- package/src/rich-text-editor/drag.ts +211 -0
- package/src/rich-text-editor/html.ts +619 -0
- package/src/rich-text-editor/index.ts +1331 -0
- package/src/rich-text-editor/inline.ts +345 -0
- package/src/rich-text-editor/keymap.ts +510 -0
- package/src/rich-text-editor/keys.ts +119 -0
- package/src/rich-text-editor/look.ts +427 -0
- package/src/rich-text-editor/markdown.ts +653 -0
- package/src/rich-text-editor/nodes.ts +497 -0
- package/src/rich-text-editor/render.ts +800 -0
- package/src/rich-text-editor/schema.ts +431 -0
- package/src/rich-text-editor/store.ts +39 -0
- package/src/rich-text-editor/suggest.ts +597 -0
- package/src/rich-text-editor/tables.ts +272 -0
- package/src/rich-text-editor/toolbar.ts +724 -0
- package/src/rich-text-editor/view.ts +2287 -0
- package/src/rich-text-editor/virtual.ts +347 -0
- package/src/richtext/node.ts +59 -30
- package/src/terminal/backends.ts +4 -1
- package/src/terminal/index.ts +43 -15
- package/dist/markdown/ast.d.ts.map +0 -1
- package/dist/markdown/ast.js +0 -17
- package/dist/markdown/ast.js.map +0 -1
- package/dist/markdown/parse.d.ts.map +0 -1
- package/dist/markdown/parse.js.map +0 -1
- package/dist/markdown/tags.d.ts.map +0 -1
- package/dist/markdown/tags.js.map +0 -1
- /package/dist/{markdown → internal/markdown}/parse.d.ts +0 -0
- /package/dist/{markdown → internal/markdown}/tags.d.ts +0 -0
- /package/dist/{markdown → internal/markdown}/tags.js +0 -0
- /package/src/{markdown → internal/markdown}/tags.ts +0 -0
|
@@ -0,0 +1,684 @@
|
|
|
1
|
+
import { parseInline } from './parse.js';
|
|
2
|
+
/** Markdown for a document, or for a run of blocks. */
|
|
3
|
+
export function stringify(doc) {
|
|
4
|
+
const blocks = 'blocks' in doc ? doc.blocks : doc;
|
|
5
|
+
return blockSequence(blocks, false).join('\n');
|
|
6
|
+
}
|
|
7
|
+
/** A list line with a marker and nothing after it. */
|
|
8
|
+
const EMPTY_ITEM = /^(?:\d{1,9}[.)]|[-+*])(?: \[[ x]\])? *$/;
|
|
9
|
+
/**
|
|
10
|
+
* The lines for a run of sibling blocks, with a blank line between each two.
|
|
11
|
+
* The exception is a nested list straight after the paragraph of a tight
|
|
12
|
+
* item — "- a\n - b" — which is how a tight list stays tight.
|
|
13
|
+
*/
|
|
14
|
+
function blockSequence(blocks, tightItem) {
|
|
15
|
+
const out = [];
|
|
16
|
+
let prevList = null;
|
|
17
|
+
for (const block of blocks) {
|
|
18
|
+
let lines;
|
|
19
|
+
let list = null;
|
|
20
|
+
if (block.type === 'list') {
|
|
21
|
+
// Two lists in a row with the same marker are one list to a reader —
|
|
22
|
+
// a blank line between them only makes it loose — so the second takes
|
|
23
|
+
// its kind's other marker, which is the break CommonMark recognises.
|
|
24
|
+
const base = block.ordered ? '.' : '-';
|
|
25
|
+
const other = block.ordered ? ')' : '*';
|
|
26
|
+
const marker = prevList &&
|
|
27
|
+
prevList.ordered === block.ordered &&
|
|
28
|
+
prevList.marker === base
|
|
29
|
+
? other
|
|
30
|
+
: base;
|
|
31
|
+
list = { ordered: block.ordered, marker };
|
|
32
|
+
lines = listLines(block, marker);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
lines = blockLines(block);
|
|
36
|
+
}
|
|
37
|
+
// an empty paragraph has no markdown; dropping it is the only honest
|
|
38
|
+
// spelling of "nothing", and it keeps adjacent lists adjacent
|
|
39
|
+
if (lines.length === 0)
|
|
40
|
+
continue;
|
|
41
|
+
// …and an empty item cannot interrupt a paragraph ("a\n3." is one
|
|
42
|
+
// paragraph), so a list that opens with one keeps its blank line
|
|
43
|
+
const interrupts = tightItem && block.type === 'list' && !EMPTY_ITEM.test(lines[0]);
|
|
44
|
+
if (out.length > 0 && !interrupts)
|
|
45
|
+
out.push('');
|
|
46
|
+
out.push(...lines);
|
|
47
|
+
prevList = list;
|
|
48
|
+
}
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
function blockLines(block) {
|
|
52
|
+
switch (block.type) {
|
|
53
|
+
case 'paragraph': {
|
|
54
|
+
const text = inline(block.children, 'block');
|
|
55
|
+
return text === '' ? [] : text.split('\n');
|
|
56
|
+
}
|
|
57
|
+
case 'heading':
|
|
58
|
+
return [headingLine(block.depth, block.children)];
|
|
59
|
+
case 'code':
|
|
60
|
+
return fenceLines(block);
|
|
61
|
+
case 'quote':
|
|
62
|
+
return blockSequence(block.children, false).map((line) => line === '' ? '>' : `> ${line}`);
|
|
63
|
+
case 'list':
|
|
64
|
+
return listLines(block, block.ordered ? '.' : '-');
|
|
65
|
+
case 'table':
|
|
66
|
+
return tableLines(block);
|
|
67
|
+
case 'rule':
|
|
68
|
+
return ['---'];
|
|
69
|
+
case 'component':
|
|
70
|
+
// The editor never builds one; an AST that has one keeps its content.
|
|
71
|
+
return blockSequence(block.children, false);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function headingLine(depth, children) {
|
|
75
|
+
let text = inline(children, 'heading');
|
|
76
|
+
// `# Title #` loses its closing run to the ATX rule — so a heading that
|
|
77
|
+
// really ends in a `#` escapes the last one
|
|
78
|
+
if (/(^|[ \t])#+$/.test(text))
|
|
79
|
+
text = `${text.slice(0, -1)}\\#`;
|
|
80
|
+
const level = Math.max(1, Math.min(6, Math.round(depth)));
|
|
81
|
+
return '#'.repeat(level) + (text ? ` ${text}` : '');
|
|
82
|
+
}
|
|
83
|
+
function fenceLines(block) {
|
|
84
|
+
const info = (block.info ?? block.lang).trim();
|
|
85
|
+
// a backtick fence cannot carry a backtick in its info string
|
|
86
|
+
const char = info.includes('`') ? '~' : '`';
|
|
87
|
+
let longest = 0;
|
|
88
|
+
for (const m of block.text.matchAll(char === '`' ? /`+/g : /~+/g)) {
|
|
89
|
+
longest = Math.max(longest, m[0].length);
|
|
90
|
+
}
|
|
91
|
+
const fence = char.repeat(Math.max(3, longest + 1));
|
|
92
|
+
const body = block.text === '' ? [] : block.text.split('\n');
|
|
93
|
+
return [fence + info, ...body, fence];
|
|
94
|
+
}
|
|
95
|
+
function listLines(list, marker) {
|
|
96
|
+
const out = [];
|
|
97
|
+
list.items.forEach((item, index) => {
|
|
98
|
+
const bullet = list.ordered ? `${list.start + index}${marker}` : marker;
|
|
99
|
+
const pad = ' '.repeat(bullet.length + 1);
|
|
100
|
+
const task = item.checked === null ? '' : item.checked ? '[x] ' : '[ ] ';
|
|
101
|
+
const body = blockSequence(item.children, list.tight);
|
|
102
|
+
if (index > 0 && !list.tight)
|
|
103
|
+
out.push('');
|
|
104
|
+
if (body.length === 0) {
|
|
105
|
+
// "- [ ] " keeps the space: a task marker needs one after it
|
|
106
|
+
out.push(task ? `${bullet} ${task}` : bullet);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
out.push(`${bullet} ${task}${body[0]}`);
|
|
110
|
+
for (let i = 1; i < body.length; i++) {
|
|
111
|
+
out.push(body[i] === '' ? '' : pad + body[i]);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
function tableLines(table) {
|
|
117
|
+
const row = (cells) => `| ${cells.map((c) => c || ' ').join(' | ')} |`;
|
|
118
|
+
const cell = (nodes) => inline(nodes, 'cell');
|
|
119
|
+
const delimiter = table.align.map((a) => a === 'left'
|
|
120
|
+
? ':---'
|
|
121
|
+
: a === 'center'
|
|
122
|
+
? ':---:'
|
|
123
|
+
: a === 'right'
|
|
124
|
+
? '---:'
|
|
125
|
+
: '---');
|
|
126
|
+
return [
|
|
127
|
+
row(table.header.map(cell)),
|
|
128
|
+
row(delimiter),
|
|
129
|
+
...table.rows.map((r) => row(r.map(cell))),
|
|
130
|
+
];
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Inline content as markdown — checked. The rules above cover what prose
|
|
134
|
+
* does, but the flanking rules have corners no delimiter placement escapes
|
|
135
|
+
* (bold that ends in punctuation, glued to the word after it, next to
|
|
136
|
+
* italic that does the same), and a corner is where a document loses text
|
|
137
|
+
* to literal asterisks. So the output is read back with the parser and
|
|
138
|
+
* compared, as flat runs of marked text, with what was meant; if they
|
|
139
|
+
* differ, the same content is written without its emphasis, and failing
|
|
140
|
+
* that as plain text. Links, code and every character survive either way.
|
|
141
|
+
*/
|
|
142
|
+
function inline(nodes, mode) {
|
|
143
|
+
const meant = expel(nodes);
|
|
144
|
+
const first = write(meant, mode);
|
|
145
|
+
if (!checkable(meant) || readsAs(first, meant, mode))
|
|
146
|
+
return first;
|
|
147
|
+
const plainer = expel(stripEmphasis(meant));
|
|
148
|
+
const second = write(plainer, mode);
|
|
149
|
+
if (readsAs(second, plainer, mode))
|
|
150
|
+
return second;
|
|
151
|
+
return write(expel(textOnly(meant)), mode);
|
|
152
|
+
}
|
|
153
|
+
function write(nodes, mode) {
|
|
154
|
+
const w = new InlineWriter(mode);
|
|
155
|
+
w.nodes(nodes);
|
|
156
|
+
return w.finish();
|
|
157
|
+
}
|
|
158
|
+
/** Only content the parser can say back — no MDX nodes — is checked. */
|
|
159
|
+
function checkable(nodes) {
|
|
160
|
+
return nodes.every((n) => n.type !== 'expression' &&
|
|
161
|
+
n.type !== 'component' &&
|
|
162
|
+
(!('children' in n) || checkable(n.children)));
|
|
163
|
+
}
|
|
164
|
+
/** Whether `text`, read back where it will be read, is `meant`. */
|
|
165
|
+
function readsAs(text, meant, mode) {
|
|
166
|
+
// a row is split on bare pipes before its cells are read, and a cell is
|
|
167
|
+
// trimmed; the other two modes read their text as it is
|
|
168
|
+
const source = mode === 'cell' ? text.replace(/\\\|/g, '|').trim() : text;
|
|
169
|
+
const back = parseInline(source, false);
|
|
170
|
+
return flatKey(back, mode) === flatKey(meant, mode);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Inline content as flat runs of marked text — what a ProseMirror document
|
|
174
|
+
* holds, and so what has to survive. Nesting order is not in it (`***x***`
|
|
175
|
+
* is bold and italic either way round), and neither are the breaks that
|
|
176
|
+
* trail a paragraph, which markdown cannot hold.
|
|
177
|
+
*/
|
|
178
|
+
function flatKey(nodes, mode) {
|
|
179
|
+
const runs = [];
|
|
180
|
+
const push = (marks, text) => {
|
|
181
|
+
const last = runs[runs.length - 1];
|
|
182
|
+
if (last && last[0] === marks)
|
|
183
|
+
last[1] += text;
|
|
184
|
+
else
|
|
185
|
+
runs.push([marks, text]);
|
|
186
|
+
};
|
|
187
|
+
const walk = (list, marks) => {
|
|
188
|
+
for (const node of list) {
|
|
189
|
+
switch (node.type) {
|
|
190
|
+
case 'text':
|
|
191
|
+
for (const part of node.text.split(/(\n)/)) {
|
|
192
|
+
if (part === '\n')
|
|
193
|
+
push('\n', mode === 'block' ? '\n' : ' ');
|
|
194
|
+
else if (part)
|
|
195
|
+
push(marks, part);
|
|
196
|
+
}
|
|
197
|
+
break;
|
|
198
|
+
case 'code':
|
|
199
|
+
push(`${marks}|code`, node.text.replace(/\n/g, ' '));
|
|
200
|
+
break;
|
|
201
|
+
case 'break':
|
|
202
|
+
if (mode === 'block')
|
|
203
|
+
push('\n', '\n');
|
|
204
|
+
else
|
|
205
|
+
push(marks, ' ');
|
|
206
|
+
break;
|
|
207
|
+
case 'link':
|
|
208
|
+
if (node.image)
|
|
209
|
+
push(`${marks}|img:${node.href}:${node.title ?? ''}`, plain(node.children) || '');
|
|
210
|
+
else if (node.href === null)
|
|
211
|
+
walk(node.children, marks);
|
|
212
|
+
else
|
|
213
|
+
walk(node.children, `${marks}|a:${node.href}:${node.title ?? ''}`);
|
|
214
|
+
break;
|
|
215
|
+
case 'strong':
|
|
216
|
+
case 'em':
|
|
217
|
+
case 'del':
|
|
218
|
+
walk(node.children, [...marks.split('|').filter(Boolean), node.type].sort().join('|'));
|
|
219
|
+
break;
|
|
220
|
+
default:
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
walk(nodes, '');
|
|
226
|
+
while (runs.length && runs[runs.length - 1][0] === '\n')
|
|
227
|
+
runs.pop();
|
|
228
|
+
return JSON.stringify(runs);
|
|
229
|
+
}
|
|
230
|
+
function plain(nodes) {
|
|
231
|
+
let out = '';
|
|
232
|
+
for (const node of nodes) {
|
|
233
|
+
if (node.type === 'text' || node.type === 'code')
|
|
234
|
+
out += node.text;
|
|
235
|
+
else if ('children' in node)
|
|
236
|
+
out += plain(node.children);
|
|
237
|
+
}
|
|
238
|
+
return out;
|
|
239
|
+
}
|
|
240
|
+
function stripEmphasis(nodes) {
|
|
241
|
+
const out = [];
|
|
242
|
+
for (const node of nodes) {
|
|
243
|
+
if (node.type === 'strong' || node.type === 'em' || node.type === 'del') {
|
|
244
|
+
out.push(...stripEmphasis(node.children));
|
|
245
|
+
}
|
|
246
|
+
else if (node.type === 'link') {
|
|
247
|
+
out.push({ ...node, children: stripEmphasis(node.children) });
|
|
248
|
+
}
|
|
249
|
+
else
|
|
250
|
+
out.push(node);
|
|
251
|
+
}
|
|
252
|
+
return out;
|
|
253
|
+
}
|
|
254
|
+
function textOnly(nodes) {
|
|
255
|
+
const out = [];
|
|
256
|
+
for (const node of nodes) {
|
|
257
|
+
if (node.type === 'text' || node.type === 'break')
|
|
258
|
+
out.push(node);
|
|
259
|
+
else if (node.type === 'code')
|
|
260
|
+
out.push({ type: 'text', text: node.text });
|
|
261
|
+
else if ('children' in node)
|
|
262
|
+
out.push(...textOnly(node.children));
|
|
263
|
+
}
|
|
264
|
+
return out;
|
|
265
|
+
}
|
|
266
|
+
const WS = /\s/u;
|
|
267
|
+
const WORD = /[\p{L}\p{N}]/u;
|
|
268
|
+
const ENTITY_LIKE = /^&(?:#\d{1,7}|#[xX][0-9a-fA-F]{1,6}|[a-zA-Z][a-zA-Z0-9]{1,31});/;
|
|
269
|
+
class InlineWriter {
|
|
270
|
+
mode;
|
|
271
|
+
out = '';
|
|
272
|
+
/** The next character starts a line — the top of the text, or just after
|
|
273
|
+
* a hard break — which is where block syntax lives. */
|
|
274
|
+
lineStart = true;
|
|
275
|
+
constructor(mode) {
|
|
276
|
+
this.mode = mode;
|
|
277
|
+
}
|
|
278
|
+
finish() {
|
|
279
|
+
let text = this.out;
|
|
280
|
+
if (this.mode === 'block') {
|
|
281
|
+
// A hard break has to be followed by something: at the very end of a
|
|
282
|
+
// paragraph it would read back as a literal backslash, so it goes.
|
|
283
|
+
text = text.replace(/(\\\n)+$/, '');
|
|
284
|
+
}
|
|
285
|
+
else if (/\s$/u.test(text)) {
|
|
286
|
+
// The ATX rule and a cell's trim both eat trailing whitespace; the last
|
|
287
|
+
// character of it is spelled as a reference so the text keeps it.
|
|
288
|
+
const last = text[text.length - 1];
|
|
289
|
+
text = text.slice(0, -1) + (last === '\t' ? '	' : ' ');
|
|
290
|
+
}
|
|
291
|
+
return text;
|
|
292
|
+
}
|
|
293
|
+
/** `after` is the character the output continues with once these nodes
|
|
294
|
+
* are written — a closing delimiter, a `]`, or nothing at all. */
|
|
295
|
+
nodes(nodes, after) {
|
|
296
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
297
|
+
const node = nodes[i];
|
|
298
|
+
const next = i + 1 < nodes.length ? leadOf(nodes[i + 1]) : after;
|
|
299
|
+
switch (node.type) {
|
|
300
|
+
case 'text':
|
|
301
|
+
this.text(node.text, next);
|
|
302
|
+
break;
|
|
303
|
+
case 'code':
|
|
304
|
+
this.code(node.text);
|
|
305
|
+
break;
|
|
306
|
+
case 'strong':
|
|
307
|
+
this.wrap('**', node.children, next);
|
|
308
|
+
break;
|
|
309
|
+
case 'em': {
|
|
310
|
+
// Beside another `*` run — italic at the start of bold, at its
|
|
311
|
+
// end, or right before it — the two delimiters would merge into
|
|
312
|
+
// one run and be read differently (`***x***` is bold inside
|
|
313
|
+
// italic), so this one is `_`, where `_` can stand: not against a
|
|
314
|
+
// word on either side.
|
|
315
|
+
const besideStar = this.out.endsWith('*') || next === '*';
|
|
316
|
+
const underscore = besideStar &&
|
|
317
|
+
!WORD.test(lastChar(this.out) ?? '') &&
|
|
318
|
+
!WORD.test(next ?? '');
|
|
319
|
+
this.wrap(underscore ? '_' : '*', node.children, next);
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
case 'del':
|
|
323
|
+
this.wrap('~~', node.children, next);
|
|
324
|
+
break;
|
|
325
|
+
case 'link':
|
|
326
|
+
this.link(node);
|
|
327
|
+
break;
|
|
328
|
+
case 'break':
|
|
329
|
+
this.hardBreak();
|
|
330
|
+
break;
|
|
331
|
+
case 'expression':
|
|
332
|
+
this.raw(`{${node.src}}`);
|
|
333
|
+
break;
|
|
334
|
+
case 'component':
|
|
335
|
+
this.nodes(node.children);
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
raw(text) {
|
|
341
|
+
this.out += text;
|
|
342
|
+
if (text)
|
|
343
|
+
this.lineStart = false;
|
|
344
|
+
}
|
|
345
|
+
hardBreak() {
|
|
346
|
+
if (this.mode !== 'block') {
|
|
347
|
+
// a heading or a cell is one line: the break is a space, spelled the
|
|
348
|
+
// way any other space at its edge is
|
|
349
|
+
this.text(' ', undefined);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
this.out += '\\\n';
|
|
353
|
+
this.lineStart = true;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* A delimited run. Whether a delimiter opens or closes depends on the
|
|
357
|
+
* characters either side of it — CommonMark's flanking rules — so the
|
|
358
|
+
* content is written first and the placement checked against what
|
|
359
|
+
* actually surrounds it. Some placements have no spelling at all: `**`
|
|
360
|
+
* cannot close after punctuation when a letter follows (`**"a"**s`). Then
|
|
361
|
+
* the text is kept and the emphasis is not, because a word that stops
|
|
362
|
+
* being bold is a smaller loss than a document that grows literal
|
|
363
|
+
* asterisks every time it is saved.
|
|
364
|
+
*
|
|
365
|
+
* A dropped run is written again rather than unwrapped: what is inside it
|
|
366
|
+
* was judged against this run's delimiters, which are no longer there, so
|
|
367
|
+
* an inner run that stood beside them may not stand beside what replaced
|
|
368
|
+
* them — and a line start it sat after is a line start again.
|
|
369
|
+
*/
|
|
370
|
+
wrap(delimiter, children, after) {
|
|
371
|
+
const start = this.out.length;
|
|
372
|
+
const wasLineStart = this.lineStart;
|
|
373
|
+
const before = lastChar(this.out);
|
|
374
|
+
this.raw(delimiter);
|
|
375
|
+
const innerStart = this.out.length;
|
|
376
|
+
this.nodes(children, delimiter[0]);
|
|
377
|
+
const inner = this.out.slice(innerStart);
|
|
378
|
+
const ch = delimiter[0];
|
|
379
|
+
if (inner !== '' &&
|
|
380
|
+
canOpen(ch, before, firstChar(inner)) &&
|
|
381
|
+
canClose(ch, lastChar(inner), after)) {
|
|
382
|
+
this.raw(delimiter);
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
this.out = this.out.slice(0, start);
|
|
386
|
+
this.lineStart = wasLineStart;
|
|
387
|
+
this.nodes(children, after);
|
|
388
|
+
}
|
|
389
|
+
code(text) {
|
|
390
|
+
const flat = text.replace(/\n/g, ' ');
|
|
391
|
+
let longest = 0;
|
|
392
|
+
for (const m of flat.matchAll(/`+/g))
|
|
393
|
+
longest = Math.max(longest, m[0].length);
|
|
394
|
+
const fence = '`'.repeat(longest + 1);
|
|
395
|
+
// CommonMark shaves one space off each end when both have one, and a
|
|
396
|
+
// span that starts or ends in a backtick would merge with its fence
|
|
397
|
+
const pad = flat.startsWith('`') ||
|
|
398
|
+
flat.endsWith('`') ||
|
|
399
|
+
(flat.startsWith(' ') && flat.endsWith(' ') && flat.trim() !== '')
|
|
400
|
+
? ' '
|
|
401
|
+
: '';
|
|
402
|
+
this.raw(this.cellSafe(fence + pad + flat + pad + fence));
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* In a table cell a bare `|` ends the cell — inside a code span or a link
|
|
406
|
+
* destination too, because a table row is split before its cells' inline
|
|
407
|
+
* syntax is read, and `\|` is turned back into `|` as it is split.
|
|
408
|
+
*/
|
|
409
|
+
cellSafe(raw) {
|
|
410
|
+
return this.mode === 'cell' ? raw.replace(/\|/g, '\\|') : raw;
|
|
411
|
+
}
|
|
412
|
+
link(node) {
|
|
413
|
+
if (node.href === null) {
|
|
414
|
+
this.nodes(node.children); // a link still streaming in has nowhere to go
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
const tail = this.cellSafe(`](${destination(node.href)}${titlePart(node.title)})`);
|
|
418
|
+
if (node.image) {
|
|
419
|
+
this.raw('![');
|
|
420
|
+
this.nodes(node.children, ']');
|
|
421
|
+
this.raw(tail);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
const auto = autolink(node);
|
|
425
|
+
if (auto !== null) {
|
|
426
|
+
this.raw(this.cellSafe(auto));
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
this.raw('[');
|
|
430
|
+
this.nodes(node.children, ']');
|
|
431
|
+
this.raw(tail);
|
|
432
|
+
}
|
|
433
|
+
/** `after` is the character the output continues with past this text, so
|
|
434
|
+
* a character at the text's edge is judged by its real neighbour and not
|
|
435
|
+
* by where one node happened to end. */
|
|
436
|
+
text(text, after) {
|
|
437
|
+
for (let i = 0; i < text.length; i++) {
|
|
438
|
+
const ch = text[i];
|
|
439
|
+
if (ch === '\n') {
|
|
440
|
+
this.hardBreak(); // a text node the editor built may carry one
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
if (this.lineStart) {
|
|
444
|
+
this.lineStart = false;
|
|
445
|
+
const lead = this.lineLead(text, i);
|
|
446
|
+
if (lead !== null) {
|
|
447
|
+
this.out += lead.text;
|
|
448
|
+
i += lead.consumed - 1;
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
this.out += this.escape(text, i, after);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* The first character of a line, when it would open a block: `# ` a
|
|
457
|
+
* heading, `>` a quote, `-`/`+`/`=`/`:`/`|`/`~` a list, a setext underline,
|
|
458
|
+
* a table's delimiter row or a fence, `12.` an ordered list, and leading
|
|
459
|
+
* whitespace an indented code block (or nothing, after a trim). The last is
|
|
460
|
+
* spelled as a character reference, which no block rule looks at.
|
|
461
|
+
*/
|
|
462
|
+
lineLead(text, i) {
|
|
463
|
+
const ch = text[i];
|
|
464
|
+
if (ch === ' ')
|
|
465
|
+
return { text: ' ', consumed: 1 };
|
|
466
|
+
if (ch === '\t')
|
|
467
|
+
return { text: '	', consumed: 1 };
|
|
468
|
+
if (this.mode !== 'block')
|
|
469
|
+
return null;
|
|
470
|
+
if ('#>-+=:|~'.includes(ch))
|
|
471
|
+
return { text: `\\${ch}`, consumed: 1 };
|
|
472
|
+
const ordered = /^(\d{1,9})([.)])/.exec(text.slice(i));
|
|
473
|
+
if (ordered) {
|
|
474
|
+
const after = text[i + ordered[0].length];
|
|
475
|
+
if (after === undefined || WS.test(after)) {
|
|
476
|
+
return {
|
|
477
|
+
text: `${ordered[1]}\\${ordered[2]}`,
|
|
478
|
+
consumed: ordered[0].length,
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
return null;
|
|
483
|
+
}
|
|
484
|
+
escape(text, i, after) {
|
|
485
|
+
const ch = text[i];
|
|
486
|
+
const prev = i > 0 ? text[i - 1] : lastChar(this.out);
|
|
487
|
+
const next = i + 1 < text.length ? text[i + 1] : after;
|
|
488
|
+
switch (ch) {
|
|
489
|
+
case '\\':
|
|
490
|
+
case '`':
|
|
491
|
+
case '*':
|
|
492
|
+
case '[':
|
|
493
|
+
case ']':
|
|
494
|
+
return `\\${ch}`;
|
|
495
|
+
case '_':
|
|
496
|
+
// intraword `_` is never emphasis — snake_case needs nothing
|
|
497
|
+
return WORD.test(prev ?? '') && WORD.test(next ?? '') ? ch : `\\${ch}`;
|
|
498
|
+
case '~':
|
|
499
|
+
// strikethrough is `~~` only, so a lone tilde is text — but one
|
|
500
|
+
// beside another, or beside a strikethrough's own delimiter, is not
|
|
501
|
+
return prev === '~' || next === '~' ? `\\${ch}` : ch;
|
|
502
|
+
case '<':
|
|
503
|
+
return /[A-Za-z/!?]/.test(text[i + 1] ?? '') ? `\\${ch}` : ch;
|
|
504
|
+
case '&':
|
|
505
|
+
return ENTITY_LIKE.test(text.slice(i)) ? `\\${ch}` : ch;
|
|
506
|
+
case '!':
|
|
507
|
+
// `!` then a link is an image
|
|
508
|
+
return next === '[' ? `\\${ch}` : ch;
|
|
509
|
+
case '|':
|
|
510
|
+
return this.mode === 'cell' ? `\\${ch}` : ch;
|
|
511
|
+
default:
|
|
512
|
+
return ch;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
// --- flanking -----------------------------------------------------------------
|
|
517
|
+
//
|
|
518
|
+
// The parser's own rules (`./parse.ts`, the emphasis scan), asked in advance.
|
|
519
|
+
const PUNCT = /[\p{P}\p{S}]/u;
|
|
520
|
+
function kind(ch) {
|
|
521
|
+
if (ch === undefined || ch === '' || WS.test(ch))
|
|
522
|
+
return 'ws';
|
|
523
|
+
return PUNCT.test(ch) ? 'punct' : 'other';
|
|
524
|
+
}
|
|
525
|
+
function flanks(before, after) {
|
|
526
|
+
const b = kind(before);
|
|
527
|
+
const a = kind(after);
|
|
528
|
+
return {
|
|
529
|
+
b,
|
|
530
|
+
a,
|
|
531
|
+
left: a !== 'ws' && (a !== 'punct' || b !== 'other'),
|
|
532
|
+
right: b !== 'ws' && (b !== 'punct' || a !== 'other'),
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
function canOpen(ch, before, first) {
|
|
536
|
+
const f = flanks(before, first);
|
|
537
|
+
return ch === '_' ? f.left && (!f.right || f.b === 'punct') : f.left;
|
|
538
|
+
}
|
|
539
|
+
function canClose(ch, last, after) {
|
|
540
|
+
const f = flanks(last, after);
|
|
541
|
+
return ch === '_' ? f.right && (!f.left || f.a === 'punct') : f.right;
|
|
542
|
+
}
|
|
543
|
+
/** The whole last character of `text` — both halves of a surrogate pair. */
|
|
544
|
+
function lastChar(text) {
|
|
545
|
+
if (text === '')
|
|
546
|
+
return undefined;
|
|
547
|
+
const unit = text.charCodeAt(text.length - 1);
|
|
548
|
+
return unit >= 0xdc00 && unit <= 0xdfff && text.length >= 2
|
|
549
|
+
? text.slice(-2)
|
|
550
|
+
: text[text.length - 1];
|
|
551
|
+
}
|
|
552
|
+
function firstChar(text) {
|
|
553
|
+
const cp = text.codePointAt(0);
|
|
554
|
+
return cp === undefined ? undefined : String.fromCodePoint(cp);
|
|
555
|
+
}
|
|
556
|
+
/** The first character a node writes — all its neighbour's flanking can
|
|
557
|
+
* see. Every delimiter and bracket is punctuation, whichever it turns out
|
|
558
|
+
* to be. */
|
|
559
|
+
function leadOf(node) {
|
|
560
|
+
switch (node.type) {
|
|
561
|
+
case 'text':
|
|
562
|
+
return firstChar(node.text);
|
|
563
|
+
case 'code':
|
|
564
|
+
return '`';
|
|
565
|
+
case 'strong':
|
|
566
|
+
case 'em':
|
|
567
|
+
return '*';
|
|
568
|
+
case 'del':
|
|
569
|
+
return '~';
|
|
570
|
+
case 'link':
|
|
571
|
+
return node.image ? '!' : '[';
|
|
572
|
+
case 'break':
|
|
573
|
+
return '\\';
|
|
574
|
+
case 'expression':
|
|
575
|
+
return '{';
|
|
576
|
+
case 'component':
|
|
577
|
+
return node.children.length ? leadOf(node.children[0]) : undefined;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
/** `<https://…>` for a link whose text is its own target. */
|
|
581
|
+
function autolink(node) {
|
|
582
|
+
if (node.title || !node.href || node.children.length !== 1)
|
|
583
|
+
return null;
|
|
584
|
+
const only = node.children[0];
|
|
585
|
+
if (only.type !== 'text')
|
|
586
|
+
return null;
|
|
587
|
+
const text = only.text;
|
|
588
|
+
if (text === node.href &&
|
|
589
|
+
/^[a-zA-Z][a-zA-Z0-9+.-]{1,31}:[^<>\s]*$/.test(text)) {
|
|
590
|
+
return `<${text}>`;
|
|
591
|
+
}
|
|
592
|
+
if (node.href === `mailto:${text}` &&
|
|
593
|
+
/^[^<>\s@]+@[^<>\s@]+\.[^<>\s@]+$/.test(text)) {
|
|
594
|
+
return `<${text}>`;
|
|
595
|
+
}
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
598
|
+
/** A link destination: bare with its parentheses escaped, or in `<…>` when
|
|
599
|
+
* it has whitespace in it — the one form a space survives in. */
|
|
600
|
+
function destination(href) {
|
|
601
|
+
if (href === '')
|
|
602
|
+
return '<>';
|
|
603
|
+
if (/[\s<>]/.test(href)) {
|
|
604
|
+
return `<${href.replace(/[\n\r]/g, '').replace(/[<>]/g, (c) => encodeURIComponent(c))}>`;
|
|
605
|
+
}
|
|
606
|
+
return href.replace(/[\\()]/g, (c) => `\\${c}`);
|
|
607
|
+
}
|
|
608
|
+
/** ` "title"`, in whichever of the three quote styles the title does not
|
|
609
|
+
* contain — the parser's title scan has no escapes. */
|
|
610
|
+
function titlePart(title) {
|
|
611
|
+
if (!title)
|
|
612
|
+
return '';
|
|
613
|
+
if (!title.includes('"'))
|
|
614
|
+
return ` "${title}"`;
|
|
615
|
+
if (!title.includes("'"))
|
|
616
|
+
return ` '${title}'`;
|
|
617
|
+
if (!title.includes(')'))
|
|
618
|
+
return ` (${title})`;
|
|
619
|
+
return ` "${title.replace(/"/g, '"')}"`;
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Emphasis can open only before non-space and close only after it (the
|
|
623
|
+
* flanking rules), so `**bold **` is not bold. Whitespace at the edge of a
|
|
624
|
+
* strong/em/del node moves outside it, and a node that was all whitespace
|
|
625
|
+
* becomes that whitespace.
|
|
626
|
+
*/
|
|
627
|
+
function expel(nodes) {
|
|
628
|
+
const out = [];
|
|
629
|
+
for (const node of nodes) {
|
|
630
|
+
if (node.type === 'strong' || node.type === 'em' || node.type === 'del') {
|
|
631
|
+
const children = expel(node.children);
|
|
632
|
+
const lead = takeEdge(children, 'start');
|
|
633
|
+
const trail = takeEdge(children, 'end');
|
|
634
|
+
if (lead)
|
|
635
|
+
out.push({ type: 'text', text: lead });
|
|
636
|
+
if (children.length > 0)
|
|
637
|
+
out.push({ type: node.type, children });
|
|
638
|
+
if (trail)
|
|
639
|
+
out.push({ type: 'text', text: trail });
|
|
640
|
+
}
|
|
641
|
+
else if (node.type === 'link' || node.type === 'component') {
|
|
642
|
+
out.push({ ...node, children: expel(node.children) });
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
out.push(node);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
return mergeTexts(out);
|
|
649
|
+
}
|
|
650
|
+
/** Remove, and return, the whitespace at one end of `nodes` (in place). */
|
|
651
|
+
function takeEdge(nodes, side) {
|
|
652
|
+
const at = side === 'start' ? 0 : nodes.length - 1;
|
|
653
|
+
const node = nodes[at];
|
|
654
|
+
if (!node || node.type !== 'text')
|
|
655
|
+
return '';
|
|
656
|
+
const m = side === 'start' ? /^\s+/u.exec(node.text) : /\s+$/u.exec(node.text);
|
|
657
|
+
if (!m)
|
|
658
|
+
return '';
|
|
659
|
+
const rest = side === 'start'
|
|
660
|
+
? node.text.slice(m[0].length)
|
|
661
|
+
: node.text.slice(0, node.text.length - m[0].length);
|
|
662
|
+
if (rest === '')
|
|
663
|
+
nodes.splice(at, 1);
|
|
664
|
+
else
|
|
665
|
+
nodes[at] = { type: 'text', text: rest };
|
|
666
|
+
return m[0];
|
|
667
|
+
}
|
|
668
|
+
function mergeTexts(nodes) {
|
|
669
|
+
const out = [];
|
|
670
|
+
for (const node of nodes) {
|
|
671
|
+
const prev = out[out.length - 1];
|
|
672
|
+
if (node.type === 'text') {
|
|
673
|
+
if (node.text === '')
|
|
674
|
+
continue;
|
|
675
|
+
if (prev?.type === 'text') {
|
|
676
|
+
out[out.length - 1] = { type: 'text', text: prev.text + node.text };
|
|
677
|
+
continue;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
out.push(node);
|
|
681
|
+
}
|
|
682
|
+
return out;
|
|
683
|
+
}
|
|
684
|
+
//# sourceMappingURL=stringify.js.map
|