@liminis/editor 0.1.0-rc.1
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/LICENSE +34 -0
- package/README.md +376 -0
- package/dist/annotations/anchor-align.d.ts +50 -0
- package/dist/annotations/anchor-align.js +74 -0
- package/dist/annotations/anchor-model.d.ts +81 -0
- package/dist/annotations/anchor-model.js +80 -0
- package/dist/annotations/anchor-resolver.d.ts +92 -0
- package/dist/annotations/anchor-resolver.js +231 -0
- package/dist/annotations/block-structure.d.ts +65 -0
- package/dist/annotations/block-structure.js +113 -0
- package/dist/annotations/types.d.ts +159 -0
- package/dist/annotations/types.js +50 -0
- package/dist/annotations.d.ts +25 -0
- package/dist/annotations.js +27 -0
- package/dist/app/App.d.ts +64 -0
- package/dist/app/App.js +180 -0
- package/dist/app/editor/AmbientCorrectionPlugin.d.ts +18 -0
- package/dist/app/editor/AmbientCorrectionPlugin.js +185 -0
- package/dist/app/editor/AnchorScrollPlugin.d.ts +16 -0
- package/dist/app/editor/AnchorScrollPlugin.js +134 -0
- package/dist/app/editor/AnnotationMarkerPlugin.d.ts +24 -0
- package/dist/app/editor/AnnotationMarkerPlugin.js +224 -0
- package/dist/app/editor/AnnotationPlugin.d.ts +42 -0
- package/dist/app/editor/AnnotationPlugin.js +135 -0
- package/dist/app/editor/AnnotationSurface.d.ts +34 -0
- package/dist/app/editor/AnnotationSurface.js +115 -0
- package/dist/app/editor/AssetContext.d.ts +20 -0
- package/dist/app/editor/AssetContext.js +81 -0
- package/dist/app/editor/BlockClickPlugin.d.ts +8 -0
- package/dist/app/editor/BlockClickPlugin.js +126 -0
- package/dist/app/editor/CodeBlockPlugin.d.ts +1 -0
- package/dist/app/editor/CodeBlockPlugin.js +219 -0
- package/dist/app/editor/CorrectionPanelPlugin.d.ts +14 -0
- package/dist/app/editor/CorrectionPanelPlugin.js +333 -0
- package/dist/app/editor/DragHandlePlugin.d.ts +1 -0
- package/dist/app/editor/DragHandlePlugin.js +300 -0
- package/dist/app/editor/Editor.d.ts +84 -0
- package/dist/app/editor/Editor.js +539 -0
- package/dist/app/editor/FrontmatterPlugin.d.ts +19 -0
- package/dist/app/editor/FrontmatterPlugin.js +301 -0
- package/dist/app/editor/ImageModal.d.ts +13 -0
- package/dist/app/editor/ImageModal.js +267 -0
- package/dist/app/editor/ImagePlugin.d.ts +3 -0
- package/dist/app/editor/ImagePlugin.js +187 -0
- package/dist/app/editor/LinkClickPlugin.d.ts +23 -0
- package/dist/app/editor/LinkClickPlugin.js +90 -0
- package/dist/app/editor/MarkdownShortcutsPlugin.d.ts +49 -0
- package/dist/app/editor/MarkdownShortcutsPlugin.js +373 -0
- package/dist/app/editor/OrderedTaskListPlugin.d.ts +21 -0
- package/dist/app/editor/OrderedTaskListPlugin.js +78 -0
- package/dist/app/editor/SearchPlugin.d.ts +1 -0
- package/dist/app/editor/SearchPlugin.js +379 -0
- package/dist/app/editor/SelectionContextMenuPlugin.d.ts +29 -0
- package/dist/app/editor/SelectionContextMenuPlugin.js +160 -0
- package/dist/app/editor/SlashMenu.d.ts +11 -0
- package/dist/app/editor/SlashMenu.js +494 -0
- package/dist/app/editor/SlashMenuPlugin.d.ts +1 -0
- package/dist/app/editor/SlashMenuPlugin.js +168 -0
- package/dist/app/editor/TableActionsPlugin.d.ts +1 -0
- package/dist/app/editor/TableActionsPlugin.js +398 -0
- package/dist/app/editor/TogglePlugin.d.ts +1 -0
- package/dist/app/editor/TogglePlugin.js +159 -0
- package/dist/app/editor/Toolbar.d.ts +10 -0
- package/dist/app/editor/Toolbar.js +263 -0
- package/dist/app/editor/WikiLinkExistencePlugin.d.ts +17 -0
- package/dist/app/editor/WikiLinkExistencePlugin.js +104 -0
- package/dist/app/editor/WikiLinkFormatPlugin.d.ts +17 -0
- package/dist/app/editor/WikiLinkFormatPlugin.js +203 -0
- package/dist/app/editor/annotation-marks.d.ts +177 -0
- package/dist/app/editor/annotation-marks.js +750 -0
- package/dist/app/editor/annotationCommands.d.ts +13 -0
- package/dist/app/editor/annotationCommands.js +11 -0
- package/dist/app/editor/c4/C4InteractiveRenderer.d.ts +35 -0
- package/dist/app/editor/c4/C4InteractiveRenderer.js +299 -0
- package/dist/app/editor/c4/edge-clipping.d.ts +24 -0
- package/dist/app/editor/c4/edge-clipping.js +139 -0
- package/dist/app/editor/c4/hooks/useC4DiagramDrag.d.ts +38 -0
- package/dist/app/editor/c4/hooks/useC4DiagramDrag.js +112 -0
- package/dist/app/editor/c4/layout.d.ts +25 -0
- package/dist/app/editor/c4/layout.js +839 -0
- package/dist/app/editor/c4/parser.d.ts +19 -0
- package/dist/app/editor/c4/parser.js +410 -0
- package/dist/app/editor/c4/render-to-string.d.ts +24 -0
- package/dist/app/editor/c4/render-to-string.js +34 -0
- package/dist/app/editor/c4/renderer.d.ts +64 -0
- package/dist/app/editor/c4/renderer.js +569 -0
- package/dist/app/editor/c4/types.d.ts +203 -0
- package/dist/app/editor/c4/types.js +43 -0
- package/dist/app/editor/correction-yaml.d.ts +10 -0
- package/dist/app/editor/correction-yaml.js +40 -0
- package/dist/app/editor/editorNodes.d.ts +9 -0
- package/dist/app/editor/editorNodes.js +36 -0
- package/dist/app/editor/index.d.ts +1 -0
- package/dist/app/editor/index.js +1 -0
- package/dist/app/editor/mark-overlap-resolver.d.ts +6 -0
- package/dist/app/editor/mark-overlap-resolver.js +45 -0
- package/dist/app/editor/nodes/C4Component.d.ts +14 -0
- package/dist/app/editor/nodes/C4Component.js +284 -0
- package/dist/app/editor/nodes/C4Node.d.ts +32 -0
- package/dist/app/editor/nodes/C4Node.js +112 -0
- package/dist/app/editor/nodes/CalloutNode.d.ts +23 -0
- package/dist/app/editor/nodes/CalloutNode.js +94 -0
- package/dist/app/editor/nodes/CustomLinkNode.d.ts +57 -0
- package/dist/app/editor/nodes/CustomLinkNode.js +209 -0
- package/dist/app/editor/nodes/CustomListItemNode.d.ts +46 -0
- package/dist/app/editor/nodes/CustomListItemNode.js +120 -0
- package/dist/app/editor/nodes/CustomListNode.d.ts +27 -0
- package/dist/app/editor/nodes/CustomListNode.js +54 -0
- package/dist/app/editor/nodes/DefinitionListNode.d.ts +44 -0
- package/dist/app/editor/nodes/DefinitionListNode.js +151 -0
- package/dist/app/editor/nodes/DiagramContextMenu.d.ts +45 -0
- package/dist/app/editor/nodes/DiagramContextMenu.js +116 -0
- package/dist/app/editor/nodes/EquationComponent.d.ts +17 -0
- package/dist/app/editor/nodes/EquationComponent.js +130 -0
- package/dist/app/editor/nodes/EquationNode.d.ts +43 -0
- package/dist/app/editor/nodes/EquationNode.js +179 -0
- package/dist/app/editor/nodes/FootnoteNode.d.ts +40 -0
- package/dist/app/editor/nodes/FootnoteNode.js +151 -0
- package/dist/app/editor/nodes/FrontmatterNode.d.ts +24 -0
- package/dist/app/editor/nodes/FrontmatterNode.js +88 -0
- package/dist/app/editor/nodes/HorizontalRuleNode.d.ts +17 -0
- package/dist/app/editor/nodes/HorizontalRuleNode.js +57 -0
- package/dist/app/editor/nodes/HtmlNode.d.ts +32 -0
- package/dist/app/editor/nodes/HtmlNode.js +121 -0
- package/dist/app/editor/nodes/ImageComponent.d.ts +11 -0
- package/dist/app/editor/nodes/ImageComponent.js +228 -0
- package/dist/app/editor/nodes/ImageNode.d.ts +53 -0
- package/dist/app/editor/nodes/ImageNode.js +183 -0
- package/dist/app/editor/nodes/ListItemParagraphBreakNode.d.ts +42 -0
- package/dist/app/editor/nodes/ListItemParagraphBreakNode.js +95 -0
- package/dist/app/editor/nodes/MermaidComponent.d.ts +11 -0
- package/dist/app/editor/nodes/MermaidComponent.js +216 -0
- package/dist/app/editor/nodes/MermaidNode.d.ts +27 -0
- package/dist/app/editor/nodes/MermaidNode.js +96 -0
- package/dist/app/editor/nodes/ToggleNode.d.ts +56 -0
- package/dist/app/editor/nodes/ToggleNode.js +237 -0
- package/dist/app/editor/nodes/diagram-context-menu.d.ts +11 -0
- package/dist/app/editor/nodes/diagram-context-menu.js +70 -0
- package/dist/app/editor/nodes/diagram-utils.d.ts +13 -0
- package/dist/app/editor/nodes/diagram-utils.js +29 -0
- package/dist/app/editor/nodes/index.d.ts +30 -0
- package/dist/app/editor/nodes/index.js +17 -0
- package/dist/app/editor/utils.d.ts +2 -0
- package/dist/app/editor/utils.js +17 -0
- package/dist/app/mapper/index.d.ts +3 -0
- package/dist/app/mapper/index.js +2 -0
- package/dist/app/mapper/lexicalToMdast.d.ts +62 -0
- package/dist/app/mapper/lexicalToMdast.js +1937 -0
- package/dist/app/mapper/mdastToLexical.d.ts +31 -0
- package/dist/app/mapper/mdastToLexical.js +1075 -0
- package/dist/contract.d.ts +8 -0
- package/dist/contract.js +8 -0
- package/dist/headless.d.ts +40 -0
- package/dist/headless.js +45 -0
- package/dist/host/context.d.ts +20 -0
- package/dist/host/context.js +24 -0
- package/dist/host/defaults.d.ts +21 -0
- package/dist/host/defaults.js +47 -0
- package/dist/host/messages.d.ts +26 -0
- package/dist/host/messages.js +47 -0
- package/dist/host/types.d.ts +75 -0
- package/dist/host/types.js +13 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +51 -0
- package/dist/markdown/annotate-sentinels.d.ts +42 -0
- package/dist/markdown/annotate-sentinels.js +86 -0
- package/dist/markdown/parse.d.ts +56 -0
- package/dist/markdown/parse.js +516 -0
- package/dist/markdown/stringify.d.ts +7 -0
- package/dist/markdown/stringify.js +439 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/LICENSE +21 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/README.md +41 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/from-markdown.d.ts +72 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/from-markdown.js +127 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/index.d.ts +8 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/index.js +6 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/to-markdown.d.ts +17 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/to-markdown.js +33 -0
- package/dist/markdown.d.ts +28 -0
- package/dist/markdown.js +44 -0
- package/dist/mathjax-config.d.ts +89 -0
- package/dist/mathjax-config.js +133 -0
- package/dist/nodes.d.ts +34 -0
- package/dist/nodes.js +34 -0
- package/dist/stores/correctionStore.d.ts +15 -0
- package/dist/stores/correctionStore.js +12 -0
- package/dist/stores/editorStore.d.ts +16 -0
- package/dist/stores/editorStore.js +27 -0
- package/dist/styles.css +2477 -0
- package/dist/types.d.ts +296 -0
- package/dist/types.js +91 -0
- package/dist/utils/file-types.d.ts +10 -0
- package/dist/utils/file-types.js +46 -0
- package/docs/annotations.md +134 -0
- package/docs/decisions/README.md +72 -0
- package/docs/decisions/adr-002.md +27 -0
- package/docs/decisions/adr-007.md +27 -0
- package/docs/decisions/adr-010.md +26 -0
- package/docs/decisions/adr-024.md +124 -0
- package/docs/decisions/adr-024b.md +0 -0
- package/docs/decisions/adr-025.md +103 -0
- package/docs/decisions/adr-027.md +66 -0
- package/docs/decisions/adr-057.md +67 -0
- package/docs/decisions/adr-070.md +131 -0
- package/docs/decisions/adr-075.md +410 -0
- package/docs/decisions/adr-076.md +183 -0
- package/docs/decisions/adr-077.md +410 -0
- package/docs/decisions/adr-078.md +243 -0
- package/docs/decisions/adr-079.md +167 -0
- package/docs/decisions/adr-080.md +325 -0
- package/docs/decisions/adr-081.md +108 -0
- package/docs/decisions/adr-082.md +123 -0
- package/docs/decisions/adr-083.md +204 -0
- package/docs/decisions/adr-084.md +179 -0
- package/docs/editor-api.md +106 -0
- package/docs/markdown-pipeline.md +282 -0
- package/docs/provenance.md +106 -0
- package/docs/zusammen-editor-capability-map.md +232 -0
- package/package.json +175 -0
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
import { toMarkdown } from 'mdast-util-to-markdown';
|
|
2
|
+
import { gfmToMarkdown } from 'mdast-util-gfm';
|
|
3
|
+
import { mathToMarkdown } from 'mdast-util-math';
|
|
4
|
+
import { defListToMarkdown } from 'mdast-util-definition-list';
|
|
5
|
+
import { gfmFootnoteToMarkdown } from 'mdast-util-gfm-footnote';
|
|
6
|
+
import { frontmatterToMarkdown } from 'mdast-util-frontmatter';
|
|
7
|
+
import { stripAnnotateSentinels, splitOnSentinelTokens } from './annotate-sentinels.js';
|
|
8
|
+
// Wiki-link options: use | as the alias divider (Obsidian/Foam style)
|
|
9
|
+
const wikiLinkOptions = { aliasDivider: '|' };
|
|
10
|
+
/**
|
|
11
|
+
* Compute the mdast `spread` property on lists and list items, which
|
|
12
|
+
* controls whether blank lines are added between items (and, for a list
|
|
13
|
+
* item, between that item's own children).
|
|
14
|
+
*
|
|
15
|
+
* The incoming `node.spread` (already set by lexicalToMdast.ts's
|
|
16
|
+
* convertListNode/convertListItemNode from the list's CustomListNode, which
|
|
17
|
+
* carries the source document's original loose/tight state through the
|
|
18
|
+
* round trip) is the baseline and is never downgraded to false here — that
|
|
19
|
+
* would silently re-introduce the loose-list-forced-tight defect. It can
|
|
20
|
+
* only be upgraded to true: an item needs its own children ("spread")
|
|
21
|
+
* blank-line-separated when it holds more than one non-nested-list child
|
|
22
|
+
* (multiple paragraphs, or a paragraph alongside a code block/table/
|
|
23
|
+
* blockquote): abutting them with no blank line is ambiguous or misparses
|
|
24
|
+
* on the way back in. A nested list doesn't count towards this — it already
|
|
25
|
+
* reads unambiguously right after preceding content with no blank line.
|
|
26
|
+
* Per CommonMark, if any item in a list is "loose", the whole list must
|
|
27
|
+
* render loose too, or re-parsing collapses the blank lines and misreads
|
|
28
|
+
* item boundaries — so a list's spread is the OR of its own baseline and its
|
|
29
|
+
* items' spread.
|
|
30
|
+
*/
|
|
31
|
+
function computeListSpread(node) {
|
|
32
|
+
if (!node || typeof node !== 'object') {
|
|
33
|
+
return node;
|
|
34
|
+
}
|
|
35
|
+
if (node.type === 'listItem') {
|
|
36
|
+
const children = (node.children || []).map(computeListSpread);
|
|
37
|
+
const nonListChildCount = children.filter((c) => c.type !== 'list').length;
|
|
38
|
+
return {
|
|
39
|
+
...node,
|
|
40
|
+
spread: node.spread === true || nonListChildCount > 1,
|
|
41
|
+
children,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (node.type === 'list') {
|
|
45
|
+
const children = (node.children || []).map(computeListSpread);
|
|
46
|
+
return {
|
|
47
|
+
...node,
|
|
48
|
+
spread: node.spread === true || children.some((item) => item.spread === true),
|
|
49
|
+
children,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
// Recursively process children
|
|
53
|
+
if (node.children && Array.isArray(node.children)) {
|
|
54
|
+
return {
|
|
55
|
+
...node,
|
|
56
|
+
children: node.children.map(computeListSpread),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return node;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Add checkbox text to ordered list items that have checked property.
|
|
63
|
+
*
|
|
64
|
+
* GFM only outputs checkboxes ([ ] or [x]) for unordered lists.
|
|
65
|
+
* For ordered lists with checkboxes (common in Foam/Obsidian), we need to
|
|
66
|
+
* manually add the checkbox text to the first paragraph.
|
|
67
|
+
*/
|
|
68
|
+
function addCheckboxTextToOrderedLists(root) {
|
|
69
|
+
function processNode(node) {
|
|
70
|
+
if (node.type === 'list' && node.ordered === true) {
|
|
71
|
+
return {
|
|
72
|
+
...node,
|
|
73
|
+
children: node.children.map((listItem) => {
|
|
74
|
+
// Only process list items that have a checked property
|
|
75
|
+
if (listItem.checked === true || listItem.checked === false) {
|
|
76
|
+
const checkbox = listItem.checked ? '[x] ' : '[ ] ';
|
|
77
|
+
// Find the first paragraph and prepend the checkbox text
|
|
78
|
+
if (listItem.children && listItem.children.length > 0) {
|
|
79
|
+
const firstChild = listItem.children[0];
|
|
80
|
+
if (firstChild.type === 'paragraph' && firstChild.children && firstChild.children.length > 0) {
|
|
81
|
+
const firstInline = firstChild.children[0];
|
|
82
|
+
if (firstInline.type === 'text') {
|
|
83
|
+
// If text already starts with a checkbox marker, don't duplicate it
|
|
84
|
+
if (firstInline.value.match(/^\[( |x|X)\]\s+/)) {
|
|
85
|
+
return listItem;
|
|
86
|
+
}
|
|
87
|
+
// Prepend checkbox to existing text
|
|
88
|
+
return {
|
|
89
|
+
...listItem,
|
|
90
|
+
checked: null, // Clear checked so gfm doesn't output it again
|
|
91
|
+
children: [
|
|
92
|
+
{
|
|
93
|
+
...firstChild,
|
|
94
|
+
children: [
|
|
95
|
+
{ ...firstInline, value: checkbox + firstInline.value },
|
|
96
|
+
...firstChild.children.slice(1),
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
...listItem.children.slice(1),
|
|
100
|
+
],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// First inline is not text, insert checkbox text before it
|
|
105
|
+
return {
|
|
106
|
+
...listItem,
|
|
107
|
+
checked: null,
|
|
108
|
+
children: [
|
|
109
|
+
{
|
|
110
|
+
...firstChild,
|
|
111
|
+
children: [
|
|
112
|
+
{ type: 'text', value: checkbox },
|
|
113
|
+
...firstChild.children,
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
...listItem.children.slice(1),
|
|
117
|
+
],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return listItem;
|
|
124
|
+
}),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// Recursively process children
|
|
128
|
+
if (node.children && Array.isArray(node.children)) {
|
|
129
|
+
return {
|
|
130
|
+
...node,
|
|
131
|
+
children: node.children.map(processNode),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return node;
|
|
135
|
+
}
|
|
136
|
+
return processNode(root);
|
|
137
|
+
}
|
|
138
|
+
// Transient placeholder codepoint wrapping a force-escaped character during
|
|
139
|
+
// stringification (#17). Distinct from `annotate-sentinels.ts`'s E000-E003
|
|
140
|
+
// range, module-local to this file, and never exported: it exists only
|
|
141
|
+
// between `convertForceEscapeTextNodes` (below) and the final
|
|
142
|
+
// placeholder-restoration post-process at the end of `stringifyMarkdown`, on
|
|
143
|
+
// a mdast tree reconstructed fresh from Lexical at export time and discarded
|
|
144
|
+
// immediately after — it never touches Lexical or live document state. Being
|
|
145
|
+
// backslash-free, it is provably invisible to the existing intraword-
|
|
146
|
+
// underscore and bracket-preservation post-processes below, so neither one
|
|
147
|
+
// needs to change to account for it.
|
|
148
|
+
const FORCE_ESCAPE_PLACEHOLDER = '\u{E004}';
|
|
149
|
+
// The character class the placeholder-restoration post-process (below) is
|
|
150
|
+
// allowed to wrap in a backslash. Kept narrow — rather than matching any
|
|
151
|
+
// character between two placeholder codepoints — so that if the placeholder
|
|
152
|
+
// codepoint itself ever showed up in real content (e.g. pasted from
|
|
153
|
+
// somewhere, or inside a code span the pre-process didn't anticipate), this
|
|
154
|
+
// step can't misfire and inject a backslash in front of arbitrary text (#17).
|
|
155
|
+
const FORCE_ESCAPE_RESTORE_CHAR_CLASS = '*_`\\[\\]#\\\\';
|
|
156
|
+
/**
|
|
157
|
+
* Convert any `{type: 'text', data: {_forceEscape: true}}` node (produced by
|
|
158
|
+
* `splitEscapedPunctuation` in `parse.ts` and carried through Lexical via the
|
|
159
|
+
* `--md-force-escape` style hint) into a dedicated `escapedChar` node, so its
|
|
160
|
+
* default `toMarkdown` handler can emit a backslash-free placeholder instead
|
|
161
|
+
* of running through the normal text-escaping logic that the two blind-strip
|
|
162
|
+
* post-processes below (intraword underscore, bracket preservation) operate
|
|
163
|
+
* on (#17).
|
|
164
|
+
*/
|
|
165
|
+
function convertForceEscapeTextNodes(node) {
|
|
166
|
+
if (!node || typeof node !== 'object') {
|
|
167
|
+
return node;
|
|
168
|
+
}
|
|
169
|
+
if (node.children && Array.isArray(node.children)) {
|
|
170
|
+
const children = [];
|
|
171
|
+
for (const child of node.children) {
|
|
172
|
+
if (child?.type === 'text' && child.data?._forceEscape === true) {
|
|
173
|
+
// Lexical's own reconciliation merges adjacent TextNodes that share
|
|
174
|
+
// identical format/style (see mdastToLexical.ts's setForceEscape),
|
|
175
|
+
// so two force-escaped characters that were adjacent in the source
|
|
176
|
+
// (e.g. "\*\*") can arrive here fused into one multi-character node.
|
|
177
|
+
// Only single-character force-escaped nodes are ever produced
|
|
178
|
+
// upstream, so every character of a fused node was independently
|
|
179
|
+
// force-escaped — expand back into one escapedChar node per
|
|
180
|
+
// character rather than assuming the node is already length 1.
|
|
181
|
+
//
|
|
182
|
+
// An annotate-serialize sentinel token (see annotate-sentinels.ts)
|
|
183
|
+
// can also be spliced onto this exact node's text by
|
|
184
|
+
// `sentinelAugmentedText` in lexicalToMdast.ts before this data is
|
|
185
|
+
// read, so the value isn't always pure escaped content — split those
|
|
186
|
+
// parts out first and leave them as plain, unescaped text (#970).
|
|
187
|
+
for (const part of splitOnSentinelTokens(child.value)) {
|
|
188
|
+
if (part.isSentinel) {
|
|
189
|
+
children.push({ type: 'text', value: part.text });
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
for (const ch of part.text) {
|
|
193
|
+
children.push({ type: 'escapedChar', value: ch });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
children.push(convertForceEscapeTextNodes(child));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return { ...node, children };
|
|
202
|
+
}
|
|
203
|
+
return node;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Normalize wiki-link nodes for stringification
|
|
207
|
+
*
|
|
208
|
+
* mdast-util-wiki-link expects `data` to exist and reads `data.alias` directly.
|
|
209
|
+
* We keep `data` as an object to avoid runtime errors.
|
|
210
|
+
*
|
|
211
|
+
* We keep data as an object if present, but do not force alias state here.
|
|
212
|
+
*/
|
|
213
|
+
function normalizeWikiLinkNodes(node) {
|
|
214
|
+
if (!node || typeof node !== 'object') {
|
|
215
|
+
return node;
|
|
216
|
+
}
|
|
217
|
+
if (node.type === 'wikiLink') {
|
|
218
|
+
const data = node.data && typeof node.data === 'object' ? { ...node.data } : {};
|
|
219
|
+
return {
|
|
220
|
+
...node,
|
|
221
|
+
data,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
// Recursively normalize children
|
|
225
|
+
if (node.children && Array.isArray(node.children)) {
|
|
226
|
+
return {
|
|
227
|
+
...node,
|
|
228
|
+
children: node.children.map(normalizeWikiLinkNodes),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return node;
|
|
232
|
+
}
|
|
233
|
+
export function stringifyMarkdown(root, options = {}) {
|
|
234
|
+
// Pre-process: add checkbox text to ordered list items (GFM only outputs for unordered)
|
|
235
|
+
let processedRoot = addCheckboxTextToOrderedLists(root);
|
|
236
|
+
// Normalize wiki-link nodes before stringifying
|
|
237
|
+
processedRoot = normalizeWikiLinkNodes(processedRoot);
|
|
238
|
+
// Pre-process: isolate force-escaped characters into their own node type
|
|
239
|
+
// so the existing blind-strip post-processes below never see them (#17)
|
|
240
|
+
processedRoot = convertForceEscapeTextNodes(processedRoot);
|
|
241
|
+
// Compute real spread values for lists/list items (loose vs. tight)
|
|
242
|
+
processedRoot = computeListSpread(processedRoot);
|
|
243
|
+
let result = toMarkdown(processedRoot, {
|
|
244
|
+
extensions: [
|
|
245
|
+
gfmToMarkdown({
|
|
246
|
+
// Keep a single space padding around cell content, but don't align columns.
|
|
247
|
+
// This preserves the common "| a | b |" style without spacing churn.
|
|
248
|
+
tableCellPadding: true,
|
|
249
|
+
tablePipeAlign: false,
|
|
250
|
+
}),
|
|
251
|
+
mathToMarkdown(),
|
|
252
|
+
defListToMarkdown,
|
|
253
|
+
gfmFootnoteToMarkdown(),
|
|
254
|
+
frontmatterToMarkdown(['yaml']),
|
|
255
|
+
{
|
|
256
|
+
// Custom handlers for emphasis markers and wiki links
|
|
257
|
+
// Cast to allow custom wikiLink handler (not in base Handlers type)
|
|
258
|
+
handlers: {
|
|
259
|
+
emphasis: (node, _parent, state) => {
|
|
260
|
+
const marker = node?.data?._emphasisMarker === '_' ? '_' : '*';
|
|
261
|
+
const content = state.containerPhrasing(node, { before: marker, after: marker });
|
|
262
|
+
return marker + content + marker;
|
|
263
|
+
},
|
|
264
|
+
strong: (node, _parent, state) => {
|
|
265
|
+
const markerChar = node?.data?._strongMarker === '_' ? '_' : '*';
|
|
266
|
+
const marker = markerChar + markerChar;
|
|
267
|
+
const content = state.containerPhrasing(node, { before: marker, after: marker });
|
|
268
|
+
return marker + content + marker;
|
|
269
|
+
},
|
|
270
|
+
escapedChar: (node) => `${FORCE_ESCAPE_PLACEHOLDER}${node.value}${FORCE_ESCAPE_PLACEHOLDER}`,
|
|
271
|
+
wikiLink: (node) => {
|
|
272
|
+
const value = node.value ?? '';
|
|
273
|
+
const data = node.data && typeof node.data === 'object' ? node.data : {};
|
|
274
|
+
const alias = typeof data.alias === 'string' ? data.alias : '';
|
|
275
|
+
const hasAlias = alias.length > 0 && alias !== value;
|
|
276
|
+
const emptyAlias = data._emptyAlias === true;
|
|
277
|
+
const aliasPart = hasAlias ? `${wikiLinkOptions.aliasDivider}${alias}` : emptyAlias ? `${wikiLinkOptions.aliasDivider}` : '';
|
|
278
|
+
return `[[${value}${aliasPart}]]`;
|
|
279
|
+
},
|
|
280
|
+
// Override mdast-util-definition-list's default (`:` + 3 spaces, i.e. a
|
|
281
|
+
// 4-char marker matching a 4-space continuation indent) with the
|
|
282
|
+
// single-space `: ` marker convention used throughout PHP Markdown
|
|
283
|
+
// Extra source and this codebase's own fixtures/spec examples, so a
|
|
284
|
+
// `: Definition` input round-trips byte-identical instead of being
|
|
285
|
+
// reformatted to `: Definition`.
|
|
286
|
+
defListDescription: (node, _parent, state, info) => {
|
|
287
|
+
const exit = state.enter('defListDescription');
|
|
288
|
+
const value = state.indentLines(state.containerFlow(node, info), (line, index, blank) => {
|
|
289
|
+
if (index) {
|
|
290
|
+
return blank ? '' : ' ' + line;
|
|
291
|
+
}
|
|
292
|
+
return blank ? ':' : ': ' + line;
|
|
293
|
+
});
|
|
294
|
+
exit();
|
|
295
|
+
return value;
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
],
|
|
300
|
+
bullet: options.bulletStyle || '-',
|
|
301
|
+
fence: options.fenceStyle || '`',
|
|
302
|
+
listItemIndent: 'one',
|
|
303
|
+
rule: '-',
|
|
304
|
+
// Custom join function to prevent extra blank lines
|
|
305
|
+
join: [
|
|
306
|
+
// Don't add blank line between paragraph and block when paragraph ends with ':'
|
|
307
|
+
// This preserves the "label + block" pattern (e.g., "Fenced code:", "Block math:").
|
|
308
|
+
// Excludes 'list': unlike table/code/math, a list following a colon-ending
|
|
309
|
+
// paragraph must still get the standard one-blank-line separation every other
|
|
310
|
+
// preceding-block type already gets before a list (see #943) — the zero-blank
|
|
311
|
+
// convention here is specific to the "label:" + verbatim/tabular block pattern.
|
|
312
|
+
// Doesn't apply inside a list item: a table/code fence there needs blank-line
|
|
313
|
+
// separation from the preceding paragraph to parse back correctly, regardless
|
|
314
|
+
// of how that paragraph ends.
|
|
315
|
+
// The colon test reads the sentinel-*free* text: in annotated-serialize
|
|
316
|
+
// mode (see `annotate-sentinels.ts`) the paragraph's last value can carry
|
|
317
|
+
// a trailing close token, so matching the raw value made the paragraph
|
|
318
|
+
// stop "ending with a colon" and re-inserted the blank line before the
|
|
319
|
+
// fence — annotate mode changing output outside its own tokens, which is
|
|
320
|
+
// exactly what `locateLiveMarkdownRange`'s offset math forbids (#970).
|
|
321
|
+
(left, right, parent) => {
|
|
322
|
+
if (left.type === 'paragraph' && parent?.type !== 'listItem') {
|
|
323
|
+
const lastChild = left.children?.[left.children.length - 1];
|
|
324
|
+
const endsWithColon = lastChild?.type === 'text' && stripAnnotateSentinels(lastChild.value ?? '').trimEnd().endsWith(':');
|
|
325
|
+
if (endsWithColon && ['table', 'code', 'math'].includes(right.type)) {
|
|
326
|
+
return 0; // No blank line
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return undefined; // Use default
|
|
330
|
+
},
|
|
331
|
+
],
|
|
332
|
+
});
|
|
333
|
+
// Post-process: convert escaped spaces back to regular spaces
|
|
334
|
+
// mdast-util-to-markdown escapes leading/trailing spaces at paragraph
|
|
335
|
+
// boundaries as   which looks ugly in raw markdown
|
|
336
|
+
result = result.replace(/ /g, ' ');
|
|
337
|
+
// Post-process: prefer two-space hard breaks over backslash hard breaks
|
|
338
|
+
// Convert a trailing backslash at end of line to two spaces.
|
|
339
|
+
result = result.replace(/\\\n/g, ' \n');
|
|
340
|
+
// Post-process: unescape callout markers in blockquotes
|
|
341
|
+
// Convert "> \[!NOTE]" -> "> [!NOTE]"
|
|
342
|
+
result = result.replace(/^> \\\[!([A-Z]+)\]/gm, '> [!$1]');
|
|
343
|
+
// Post-process: unescape ordered task list markers
|
|
344
|
+
// Convert "1. \[ ]" -> "1. [ ]"
|
|
345
|
+
result = result.replace(/^(\s*\d+\.\s*)\\\[( |x|X)\]/gm, '$1[$2]');
|
|
346
|
+
// Post-process: unescape wiki-link brackets that were escaped
|
|
347
|
+
// mdast-util-to-markdown escapes [ to \[ to prevent link interpretation
|
|
348
|
+
// Pattern: \[\[content]] or \[\[content\]\] → [[content]]
|
|
349
|
+
// NOTE: must run before the bracket preservation step below
|
|
350
|
+
result = result.replace(/\\\[\\\[([^\]\n]+?)\\\]\\\]/g, '[[$1]]');
|
|
351
|
+
result = result.replace(/\\\[\\\[([^\]\n]+?)\]\]/g, '[[$1]]');
|
|
352
|
+
// Post-process: unescape balanced bracket pairs inside image alt text
|
|
353
|
+
// mdast-util-to-markdown always escapes both '[' and ']' within an image's
|
|
354
|
+
// alt-text label (unlike plain phrasing, where only '[' is unsafe), so a
|
|
355
|
+
// literal bracket pair like "see [note] here" serializes as
|
|
356
|
+
// "see \[note\] here" regardless of whether the source was already escaped
|
|
357
|
+
// (the AST only stores the plain alt string; escaped and unescaped sources
|
|
358
|
+
// are indistinguishable once parsed). The bracket-preservation regex below
|
|
359
|
+
// was written for the plain-phrasing case (escaped opener, bare closer) and
|
|
360
|
+
// mis-fires on this already-fully-escaped span, corrupting it (see #903).
|
|
361
|
+
// Strip the escapes back to literal brackets whenever an image's alt-text
|
|
362
|
+
// span contains a properly nested run of escaped brackets — every ']'
|
|
363
|
+
// closes a preceding '[' and depth returns to 0 by the end of the span. An
|
|
364
|
+
// equal open/close *count* alone isn't sufficient (e.g. "a\]b\[c" has one of
|
|
365
|
+
// each but the ']' closes nothing): unescaping that would leave a bare ']'
|
|
366
|
+
// in the middle of the image label and truncate it early, so any span with
|
|
367
|
+
// an out-of-order or otherwise unbalanced bracket is left untouched and
|
|
368
|
+
// deferred to the existing behavior below. `(?:\\.|[^\]\n])*` consumes
|
|
369
|
+
// escaped characters as a unit so an escaped closing bracket in the alt text
|
|
370
|
+
// isn't confused with the image label's own (unescaped) terminating ']'.
|
|
371
|
+
result = result.replace(/!\[((?:\\.|[^\]\n])*)\]\(/g, (match, alt) => {
|
|
372
|
+
if (!/\\[[\]]/.test(alt)) {
|
|
373
|
+
return match;
|
|
374
|
+
}
|
|
375
|
+
const unescaped = alt.replace(/\\([[\]])/g, '$1');
|
|
376
|
+
let depth = 0;
|
|
377
|
+
for (const ch of unescaped) {
|
|
378
|
+
if (ch === '[') {
|
|
379
|
+
depth++;
|
|
380
|
+
}
|
|
381
|
+
else if (ch === ']') {
|
|
382
|
+
depth--;
|
|
383
|
+
if (depth < 0) {
|
|
384
|
+
return match;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
if (depth !== 0) {
|
|
389
|
+
return match;
|
|
390
|
+
}
|
|
391
|
+
return `;
|
|
393
|
+
// Post-process: strip mdast-util-to-markdown's conservative '[' escape in
|
|
394
|
+
// plain phrasing. mdast-util-to-markdown always escapes a literal '[' in
|
|
395
|
+
// phrasing content (in case a same-named link reference definition exists
|
|
396
|
+
// elsewhere in the document) but leaves a lone ']' bare, since ']' is only
|
|
397
|
+
// ever unsafe inside an actual link/image label — and label content is
|
|
398
|
+
// always escaped in full by the link/image handlers themselves (see #918
|
|
399
|
+
// research: no code path produces a half-escaped label). So an escaped '\['
|
|
400
|
+
// followed eventually by a bare ']' can only originate from genuine
|
|
401
|
+
// top-level phrasing, never from an under-escaped label, and the '[' escape
|
|
402
|
+
// there is unnecessary: strip it rather than "completing" it by escaping
|
|
403
|
+
// the ']' too (which corrupted plain prose brackets, see #918).
|
|
404
|
+
// Image alt-text spans are excluded (tried first, left untouched) so this
|
|
405
|
+
// doesn't undo the deliberate escape-preservation of the block above, which
|
|
406
|
+
// already resolved (or intentionally preserved) unbalanced/pre-escaped
|
|
407
|
+
// bracket content inside `, stripping the
|
|
410
|
+
// escape means the round-tripped prose will now parse as a real reference
|
|
411
|
+
// link instead of staying inert — a semantic change. This mirrors
|
|
412
|
+
// mdast-util-to-markdown's own conservative-by-default behavior and is out
|
|
413
|
+
// of scope for this fix (see #918 "Out of Scope").
|
|
414
|
+
// Known limitation: unlike the underscore step below, this (and the #903
|
|
415
|
+
// block above it) operate on the fully serialized string without excluding
|
|
416
|
+
// fenced/inline code or math spans, so literal `\[...]` text inside one of
|
|
417
|
+
// those verbatim regions would also be stripped. Pre-existing gap (not
|
|
418
|
+
// introduced by this fix); out of scope per #918 "Out of Scope" (brackets
|
|
419
|
+
// inside code spans).
|
|
420
|
+
result = result.replace(/!\[(?:\\.|[^\\\]\n])*\]\(|\\\[([^\]\n]+)\](?!\])/g, (match, plainPhrasingContent) => (plainPhrasingContent === undefined ? match : `[${plainPhrasingContent}]`));
|
|
421
|
+
// Post-process: unescape intraword underscores, outside of code and math.
|
|
422
|
+
// mdast-util-to-markdown escapes every `_` in text conservatively, but CommonMark
|
|
423
|
+
// never lets an intraword underscore (flanked by alphanumerics on both sides) open
|
|
424
|
+
// or close emphasis — so escaping it is unnecessary. An underscore adjacent to
|
|
425
|
+
// whitespace/punctuation (e.g. `_word_`) can still form emphasis and must stay escaped.
|
|
426
|
+
// Code (fenced blocks and inline spans) and math ($...$ / $$...$$) are skipped: their
|
|
427
|
+
// content is emitted verbatim, so a literal `\_` there (e.g. a regex/shell escape, or
|
|
428
|
+
// a LaTeX subscript marker) is the user's own text and must not be rewritten. The fenced
|
|
429
|
+
// code alternative backreferences the opening fence run so a closing line using a
|
|
430
|
+
// different fence character/length (or a same-character run that's just code content)
|
|
431
|
+
// doesn't end the protected region early.
|
|
432
|
+
result = result.replace(/^[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*?^[ \t]*\1[ \t]*$|(`+)[^\n]*?\2|\$\$[\s\S]*?\$\$|\$[^\n$]*?\$|((?<=[\p{L}\p{N}])\\_(?=[\p{L}\p{N}]))/gmu, (match, _fenceRun, _inlineOpen, underscore) => (underscore === undefined ? match : '_'));
|
|
433
|
+
// Post-process: restore the real backslash for every force-escaped
|
|
434
|
+
// character (#17), now that every other post-process above — which only
|
|
435
|
+
// ever strips backslashes, never adds them — has already run and had no
|
|
436
|
+
// chance to see (or strip) a backslash that wasn't there yet.
|
|
437
|
+
result = result.replace(new RegExp(`${FORCE_ESCAPE_PLACEHOLDER}([${FORCE_ESCAPE_RESTORE_CHAR_CLASS}])${FORCE_ESCAPE_PLACEHOLDER}`, 'gu'), '\\$1');
|
|
438
|
+
return result;
|
|
439
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 Mark Hudnall
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Vendored `mdast-util-wiki-link`
|
|
2
|
+
|
|
3
|
+
Source: [`landakram/mdast-util-wiki-link`](https://github.com/landakram/mdast-util-wiki-link) v0.1.2,
|
|
4
|
+
MIT-licensed (see `LICENSE`). Copyright (c) 2020 Mark Hudnall.
|
|
5
|
+
|
|
6
|
+
## Why this is vendored
|
|
7
|
+
|
|
8
|
+
The behaviour this package needs was previously obtained by patching the npm
|
|
9
|
+
dependency through pnpm's `patchedDependencies`, in
|
|
10
|
+
`liminis-app/patches/mdast-util-wiki-link@0.1.2.patch`. A pnpm patch is a
|
|
11
|
+
property of *this workspace*, not of the published package: an external consumer
|
|
12
|
+
of `@liminis/editor` installing from a registry would get the unpatched
|
|
13
|
+
upstream, and would silently get different — and wrong — round-trip behaviour for
|
|
14
|
+
wiki-links inside markdown tables.
|
|
15
|
+
|
|
16
|
+
Vendoring makes `@liminis/editor` self-contained (#940 / FR-004), and removes
|
|
17
|
+
the `mdast-util-to-markdown@0.6.5` + `@babel/runtime` duplicate dependency graph
|
|
18
|
+
upstream pulled in.
|
|
19
|
+
|
|
20
|
+
## Divergences from upstream
|
|
21
|
+
|
|
22
|
+
1. **Trailing-backslash strip in `from-markdown.ts`.** When a wiki-link has an
|
|
23
|
+
alias and its target ends with a backslash, the backslash is dropped before
|
|
24
|
+
the target is resolved. This is the fix for
|
|
25
|
+
[#347](https://github.com/verveguy/liminis/issues/347) — `parseMarkdown`
|
|
26
|
+
escapes the alias divider (`\|`) so that GFM table parsing does not split a
|
|
27
|
+
wiki-link at its pipe, and the escape must not survive into the target.
|
|
28
|
+
Removing this behaviour re-breaks whole markdown tables that contain an
|
|
29
|
+
aliased wiki-link, not just the link. Do not re-remove it.
|
|
30
|
+
|
|
31
|
+
Because the strip happens *before* `pageResolver` runs, `data.permalink` and
|
|
32
|
+
`data.exists` are derived from the stripped value, exactly as under the patch.
|
|
33
|
+
|
|
34
|
+
2. **`to-markdown.ts` uses `mdast-util-to-markdown` v2's `state.safe()`** rather
|
|
35
|
+
than upstream's deep import of `mdast-util-to-markdown/lib/util/safe` from
|
|
36
|
+
v0.6.5. Same escaping, no duplicate dependency.
|
|
37
|
+
|
|
38
|
+
3. **Types.** The `any`-typed option and node shapes are given real types.
|
|
39
|
+
|
|
40
|
+
Parse and serialize behaviour is otherwise unchanged. Parity is covered by
|
|
41
|
+
`src/markdown/__tests__/vendor-wiki-link.test.ts`.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vendored from `mdast-util-wiki-link@0.1.2` (MIT, Mark Hudnall — see LICENSE
|
|
3
|
+
* in this directory), with the Liminis trailing-backslash fix applied inline.
|
|
4
|
+
*
|
|
5
|
+
* Why vendored rather than depended-upon-and-patched: the upstream behaviour we
|
|
6
|
+
* need was only reachable through a `pnpm` `patchedDependencies` entry, which
|
|
7
|
+
* does not travel outside the workspace that declares it. An external consumer of the published
|
|
8
|
+
* package would silently get different — and wrong — wiki-link behaviour inside
|
|
9
|
+
* markdown tables (#347). Vendoring makes the package self-contained.
|
|
10
|
+
*
|
|
11
|
+
* Four deliberate divergences from upstream:
|
|
12
|
+
* 1. The trailing-backslash strip (see `exitWikiLink`), previously carried as
|
|
13
|
+
* `liminis-app/patches/mdast-util-wiki-link@0.1.2.patch` in `verveguy/liminis`.
|
|
14
|
+
* 2. Real types instead of `any` on the public option and node shapes.
|
|
15
|
+
* 3. `exitWikiLink` reads the node off `this.stack` rather than off a
|
|
16
|
+
* closure variable shared by every handler in one `fromMarkdown()` call.
|
|
17
|
+
* 4. `top()` asserts the stack frame is actually a wiki-link, so the
|
|
18
|
+
* cannot-nest assumption fails loudly rather than corrupting a sibling node.
|
|
19
|
+
*
|
|
20
|
+
* Nothing else about the parse behaviour changes: `value`, `data.alias`,
|
|
21
|
+
* `data.permalink`, `data.exists`, `data.hName`, `data.hProperties` and
|
|
22
|
+
* `data.hChildren` are computed exactly as upstream computes them.
|
|
23
|
+
*/
|
|
24
|
+
export interface WikiLinkFromMarkdownOptions {
|
|
25
|
+
permalinks?: string[];
|
|
26
|
+
pageResolver?: (name: string) => string[];
|
|
27
|
+
newClassName?: string;
|
|
28
|
+
wikiLinkClassName?: string;
|
|
29
|
+
hrefTemplate?: (permalink: string) => string;
|
|
30
|
+
/** Accepted and ignored here; consumed by `toMarkdown` and the micromark syntax extension. */
|
|
31
|
+
aliasDivider?: string;
|
|
32
|
+
}
|
|
33
|
+
interface WikiLinkNode {
|
|
34
|
+
type: 'wikiLink';
|
|
35
|
+
value: string;
|
|
36
|
+
data: {
|
|
37
|
+
alias: string | null;
|
|
38
|
+
permalink: string | null;
|
|
39
|
+
exists: boolean | null;
|
|
40
|
+
hName?: string;
|
|
41
|
+
hProperties?: {
|
|
42
|
+
className: string;
|
|
43
|
+
href: string;
|
|
44
|
+
};
|
|
45
|
+
hChildren?: {
|
|
46
|
+
type: 'text';
|
|
47
|
+
value: string;
|
|
48
|
+
}[];
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The subset of `mdast-util-from-markdown`'s `CompileContext` this handler uses.
|
|
53
|
+
* Typed structurally so the vendored module does not depend on that package's
|
|
54
|
+
* (unexported, version-specific) context type.
|
|
55
|
+
*/
|
|
56
|
+
interface WikiLinkCompileContext {
|
|
57
|
+
stack: unknown[];
|
|
58
|
+
enter: (node: WikiLinkNode, token: unknown) => void;
|
|
59
|
+
exit: (token: unknown) => void;
|
|
60
|
+
sliceSerialize: (token: unknown) => string;
|
|
61
|
+
}
|
|
62
|
+
export declare function fromMarkdown(opts?: WikiLinkFromMarkdownOptions): {
|
|
63
|
+
enter: {
|
|
64
|
+
wikiLink: (this: WikiLinkCompileContext, token: unknown) => void;
|
|
65
|
+
};
|
|
66
|
+
exit: {
|
|
67
|
+
wikiLinkTarget: (this: WikiLinkCompileContext, token: unknown) => void;
|
|
68
|
+
wikiLinkAlias: (this: WikiLinkCompileContext, token: unknown) => void;
|
|
69
|
+
wikiLink: (this: WikiLinkCompileContext, token: unknown) => void;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
export {};
|