@orz/ui-markdown 0.0.0-stage → 0.1.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/LICENSE +29 -0
- package/README.md +175 -2
- package/dist/attachment-links.d.ts +20 -0
- package/dist/attachment-links.d.ts.map +1 -0
- package/dist/attachment-links.js +27 -0
- package/dist/attachment-links.js.map +1 -0
- package/dist/content.d.ts +31 -0
- package/dist/content.d.ts.map +1 -0
- package/dist/content.js +78 -0
- package/dist/content.js.map +1 -0
- package/dist/embeds.d.ts +18 -0
- package/dist/embeds.d.ts.map +1 -0
- package/dist/embeds.js +59 -0
- package/dist/embeds.js.map +1 -0
- package/dist/external-links.d.ts +23 -0
- package/dist/external-links.d.ts.map +1 -0
- package/dist/external-links.js +43 -0
- package/dist/external-links.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/markdown-processor.d.ts +23 -0
- package/dist/lib/markdown-processor.d.ts.map +1 -0
- package/dist/lib/markdown-processor.js +35 -0
- package/dist/lib/markdown-processor.js.map +1 -0
- package/dist/lib/rehype-details.d.ts +13 -0
- package/dist/lib/rehype-details.d.ts.map +1 -0
- package/dist/lib/rehype-details.js +256 -0
- package/dist/lib/rehype-details.js.map +1 -0
- package/dist/lib/rehype-source-lines.d.ts +23 -0
- package/dist/lib/rehype-source-lines.d.ts.map +1 -0
- package/dist/lib/rehype-source-lines.js +77 -0
- package/dist/lib/rehype-source-lines.js.map +1 -0
- package/dist/lib/remark-frontmatter-table.d.ts +119 -0
- package/dist/lib/remark-frontmatter-table.d.ts.map +1 -0
- package/dist/lib/remark-frontmatter-table.js +212 -0
- package/dist/lib/remark-frontmatter-table.js.map +1 -0
- package/dist/lib/remark-rejected-urls.d.ts +23 -0
- package/dist/lib/remark-rejected-urls.d.ts.map +1 -0
- package/dist/lib/remark-rejected-urls.js +79 -0
- package/dist/lib/remark-rejected-urls.js.map +1 -0
- package/dist/markdown.d.ts +30 -0
- package/dist/markdown.d.ts.map +1 -0
- package/dist/markdown.js +146 -0
- package/dist/markdown.js.map +1 -0
- package/dist/plugin.d.ts +39 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +2 -0
- package/dist/plugin.js.map +1 -0
- package/package.json +66 -3
- package/styles.css +293 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import remarkFrontmatter from "remark-frontmatter";
|
|
2
|
+
import remarkGfm from "remark-gfm";
|
|
3
|
+
import remarkParse from "remark-parse";
|
|
4
|
+
import { unified } from "unified";
|
|
5
|
+
import { FRONTMATTER_FLAVOURS, remarkFrontmatterTable, } from "./remark-frontmatter-table.js";
|
|
6
|
+
import { remarkRejectedUrlsAsText } from "./remark-rejected-urls.js";
|
|
7
|
+
/**
|
|
8
|
+
* Static Markdown syntax shared by every parser and renderer.
|
|
9
|
+
*
|
|
10
|
+
* Keep runtime-dependent plugins (references, say) in the caller: their
|
|
11
|
+
* options belong to the app's context. Keeping
|
|
12
|
+
* this array at module scope also lets renderers retain plugin-list identity
|
|
13
|
+
* instead of rebuilding the invariant prefix on every render.
|
|
14
|
+
*
|
|
15
|
+
* The order is significant. Rejected URLs are restored to source text before
|
|
16
|
+
* later tokenizers see them, and `remarkFrontmatterTable` consumes the nodes
|
|
17
|
+
* produced by `remarkFrontmatter`. Without that tokenizer, frontmatter lines
|
|
18
|
+
* that begin with digits are parsed as ordered-list items (T-383).
|
|
19
|
+
*/
|
|
20
|
+
export const MARKDOWN_SYNTAX_PLUGINS = [
|
|
21
|
+
remarkGfm,
|
|
22
|
+
remarkRejectedUrlsAsText,
|
|
23
|
+
[remarkFrontmatter, FRONTMATTER_FLAVOURS],
|
|
24
|
+
remarkFrontmatterTable,
|
|
25
|
+
];
|
|
26
|
+
const processor = unified().use(remarkParse).use(MARKDOWN_SYNTAX_PLUGINS);
|
|
27
|
+
/**
|
|
28
|
+
* `runSync`, not `parse` alone: `parse` stops at the tokenizer and runs no
|
|
29
|
+
* transformer, so `remarkFrontmatterTable` — which is one — would never fire
|
|
30
|
+
* and the caller would hold the `yaml` leaf while the DOM held a table.
|
|
31
|
+
*/
|
|
32
|
+
export function parseMarkdown(source) {
|
|
33
|
+
return processor.runSync(processor.parse(source), source);
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=markdown-processor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown-processor.js","sourceRoot":"","sources":["../../src/lib/markdown-processor.ts"],"names":[],"mappings":"AACA,OAAO,iBAAiB,MAAM,oBAAoB,CAAC;AACnD,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,WAAW,MAAM,cAAc,CAAC;AACvC,OAAO,EAAsB,OAAO,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EACL,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAErE;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAkB;IACpD,SAAS;IACT,wBAAwB;IACxB,CAAC,iBAAiB,EAAE,oBAAoB,CAAC;IACzC,sBAAsB;CACvB,CAAC;AACF,MAAM,SAAS,GAAG,OAAO,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;AAE1E;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,OAAO,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,CAAU,CAAC;AACrE,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Root } from "hast";
|
|
2
|
+
/**
|
|
3
|
+
* Cut exactly two tags out of the escaping that raw HTML otherwise gets.
|
|
4
|
+
*
|
|
5
|
+
* react-markdown replaces every `raw` node with a text node holding its
|
|
6
|
+
* source, which is why an unsupported tag reaches the reader as the characters
|
|
7
|
+
* they typed. This plugin runs in the window before that, turns the literals
|
|
8
|
+
* of `MARKER` into elements it builds from constants, and leaves every other
|
|
9
|
+
* `raw` node exactly where it was — so the set of elements the document can
|
|
10
|
+
* hold grows by `details` and `summary` and by nothing else.
|
|
11
|
+
*/
|
|
12
|
+
export declare function rehypeDetails(): (tree: Root) => void;
|
|
13
|
+
//# sourceMappingURL=rehype-details.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rehype-details.d.ts","sourceRoot":"","sources":["../../src/lib/rehype-details.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA2B,IAAI,EAAe,MAAM,MAAM,CAAC;AAmQvE;;;;;;;;;GASG;AACH,wBAAgB,aAAa,WACb,IAAI,UA+BnB"}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML whitespace, spelled out instead of `\s`: that class also matches NBSP
|
|
3
|
+
* and the Unicode space separators, and every character this recogniser
|
|
4
|
+
* accepts has to be one a reviewer can read off the grammar.
|
|
5
|
+
*/
|
|
6
|
+
const S = "[ \\t\\n\\f\\r]";
|
|
7
|
+
/**
|
|
8
|
+
* The five literals the recogniser knows, and the whole of what it knows.
|
|
9
|
+
* Everything else — `<details class="x">`, `<details open="open">`,
|
|
10
|
+
* `<details/>`, `<detailsfoo>`, `< details>` — falls through untouched and
|
|
11
|
+
* reaches the reader as escaped source, which is what raw HTML has always
|
|
12
|
+
* done here.
|
|
13
|
+
*
|
|
14
|
+
* Alternatives rather than one pattern with an optional attribute, so that
|
|
15
|
+
* `open` is decided by *which literal matched* and never by reading the
|
|
16
|
+
* match: see `foldElement`.
|
|
17
|
+
*/
|
|
18
|
+
const MARKER = new RegExp([
|
|
19
|
+
`(?<detailsOpen><details${S}*>)`,
|
|
20
|
+
`(?<detailsOpenExpanded><details${S}+open${S}*>)`,
|
|
21
|
+
`(?<detailsClose></details${S}*>)`,
|
|
22
|
+
`(?<summaryOpen><summary${S}*>)`,
|
|
23
|
+
`(?<summaryClose></summary${S}*>)`,
|
|
24
|
+
].join("|"), "gi");
|
|
25
|
+
function isMarker(piece) {
|
|
26
|
+
return "marker" in piece;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The only path from a recognised literal to an element. Its arguments are the
|
|
30
|
+
* two constants the recogniser chose between; the matched source is not one of
|
|
31
|
+
* them and cannot be reached from in here. That is what makes "no byte of the
|
|
32
|
+
* document ever becomes an attribute value" a property of this signature
|
|
33
|
+
* rather than of how tightly the regex above is written — a regex loose enough
|
|
34
|
+
* to accept `<details onclick=x>` would still produce nothing but an empty
|
|
35
|
+
* `<details>`.
|
|
36
|
+
*/
|
|
37
|
+
function foldElement(tagName, open) {
|
|
38
|
+
return {
|
|
39
|
+
type: "element",
|
|
40
|
+
tagName,
|
|
41
|
+
properties: open ? { open: true } : {},
|
|
42
|
+
children: [],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Where `consumed` ends, counted from `start`. Lines come out exact wherever
|
|
47
|
+
* this is used, because a raw node's value holds one newline per source line
|
|
48
|
+
* it spans; columns and offsets are short by whatever a container strips off
|
|
49
|
+
* the front of each line (`> ` in a blockquote), and the fold, stamp and
|
|
50
|
+
* decoration passes all read lines.
|
|
51
|
+
*/
|
|
52
|
+
function advance(start, consumed) {
|
|
53
|
+
const lines = consumed.split("\n");
|
|
54
|
+
const breaks = lines.length - 1;
|
|
55
|
+
return {
|
|
56
|
+
line: start.line + breaks,
|
|
57
|
+
column: breaks === 0
|
|
58
|
+
? start.column + consumed.length
|
|
59
|
+
: (lines[breaks]?.length ?? 0) + 1,
|
|
60
|
+
offset: start.offset === undefined ? undefined : start.offset + consumed.length,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function rawAt(value, start) {
|
|
64
|
+
if (start === undefined)
|
|
65
|
+
return { type: "raw", value };
|
|
66
|
+
return {
|
|
67
|
+
type: "raw",
|
|
68
|
+
value,
|
|
69
|
+
position: { start, end: advance(start, value) },
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function kindOf(groups) {
|
|
73
|
+
if (groups.detailsClose !== undefined)
|
|
74
|
+
return "details-close";
|
|
75
|
+
if (groups.summaryOpen !== undefined)
|
|
76
|
+
return "summary-open";
|
|
77
|
+
if (groups.summaryClose !== undefined)
|
|
78
|
+
return "summary-close";
|
|
79
|
+
return "details-open";
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* One raw node cut into the literals it holds and the text between them, or
|
|
83
|
+
* null when it holds none — a node nothing matched keeps its identity, which
|
|
84
|
+
* is the same rule `<Markdown>` follows for its component map (T-60).
|
|
85
|
+
*
|
|
86
|
+
* Matching starts anywhere inside the value rather than at its front: a single
|
|
87
|
+
* raw node routinely carries a whole block of HTML, and
|
|
88
|
+
* `<details><summary>t</summary><script>alert(1)</script></details>` has to
|
|
89
|
+
* come apart into two elements and one string of escaped source.
|
|
90
|
+
*/
|
|
91
|
+
function splitRaw(node) {
|
|
92
|
+
MARKER.lastIndex = 0;
|
|
93
|
+
let match = MARKER.exec(node.value);
|
|
94
|
+
if (match === null)
|
|
95
|
+
return null;
|
|
96
|
+
const start = node.position?.start;
|
|
97
|
+
const pointAt = (index) => start === undefined
|
|
98
|
+
? undefined
|
|
99
|
+
: index === 0
|
|
100
|
+
? start
|
|
101
|
+
: advance(start, node.value.slice(0, index));
|
|
102
|
+
const pieces = [];
|
|
103
|
+
let consumed = 0;
|
|
104
|
+
while (match !== null) {
|
|
105
|
+
if (match.index > consumed) {
|
|
106
|
+
pieces.push(rawAt(node.value.slice(consumed, match.index), pointAt(consumed)));
|
|
107
|
+
}
|
|
108
|
+
const groups = match.groups ?? {};
|
|
109
|
+
const whole = match.index === 0 && match[0].length === node.value.length ? node : null;
|
|
110
|
+
pieces.push({
|
|
111
|
+
marker: kindOf(groups),
|
|
112
|
+
open: groups.detailsOpenExpanded !== undefined,
|
|
113
|
+
source: whole ?? rawAt(match[0], pointAt(match.index)),
|
|
114
|
+
});
|
|
115
|
+
consumed = match.index + match[0].length;
|
|
116
|
+
match = MARKER.exec(node.value);
|
|
117
|
+
}
|
|
118
|
+
if (consumed < node.value.length) {
|
|
119
|
+
pieces.push(rawAt(node.value.slice(consumed), pointAt(consumed)));
|
|
120
|
+
}
|
|
121
|
+
return pieces;
|
|
122
|
+
}
|
|
123
|
+
/** Every marker nothing paired with goes back to being raw, i.e. to source text. */
|
|
124
|
+
function resolve(pieces) {
|
|
125
|
+
return pieces.map((piece) => (isMarker(piece) ? piece.source : piece));
|
|
126
|
+
}
|
|
127
|
+
function spanning(open, close) {
|
|
128
|
+
const start = open.position?.start;
|
|
129
|
+
const end = close.position?.end;
|
|
130
|
+
return start === undefined || end === undefined ? undefined : { start, end };
|
|
131
|
+
}
|
|
132
|
+
/** The `details-close` that matches the open marker at `from`; -1 for none. */
|
|
133
|
+
function matchingClose(pieces, from) {
|
|
134
|
+
let depth = 0;
|
|
135
|
+
for (let i = from + 1; i < pieces.length; i++) {
|
|
136
|
+
const piece = pieces[i];
|
|
137
|
+
if (piece === undefined || !isMarker(piece))
|
|
138
|
+
continue;
|
|
139
|
+
if (piece.marker === "details-open")
|
|
140
|
+
depth++;
|
|
141
|
+
else if (piece.marker === "details-close") {
|
|
142
|
+
if (depth === 0)
|
|
143
|
+
return i;
|
|
144
|
+
depth--;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return -1;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* The first `<summary>…</summary>` of a fold, as its own element. A second one
|
|
151
|
+
* in the same fold, and any that sits outside a fold altogether, keeps its
|
|
152
|
+
* markers and so reaches the reader as source — without this pass, a rejected
|
|
153
|
+
* `<details onclick=x>` would leave its `<summary>` behind as a stray element
|
|
154
|
+
* floating in the document, which is the one place "a rejected tag shows up
|
|
155
|
+
* whole, as text" would otherwise have an exception.
|
|
156
|
+
*/
|
|
157
|
+
function pairSummary(pieces) {
|
|
158
|
+
const open = pieces.findIndex((piece) => isMarker(piece) && piece.marker === "summary-open");
|
|
159
|
+
if (open < 0)
|
|
160
|
+
return pieces;
|
|
161
|
+
const close = pieces.findIndex((piece, i) => i > open && isMarker(piece) && piece.marker === "summary-close");
|
|
162
|
+
if (close < 0)
|
|
163
|
+
return pieces;
|
|
164
|
+
const openMarker = pieces[open];
|
|
165
|
+
const closeMarker = pieces[close];
|
|
166
|
+
if (openMarker === undefined ||
|
|
167
|
+
closeMarker === undefined ||
|
|
168
|
+
!isMarker(openMarker) ||
|
|
169
|
+
!isMarker(closeMarker)) {
|
|
170
|
+
return pieces;
|
|
171
|
+
}
|
|
172
|
+
const element = foldElement("summary", false);
|
|
173
|
+
element.children = resolve(pieces.slice(open + 1, close));
|
|
174
|
+
element.position = spanning(openMarker.source, closeMarker.source);
|
|
175
|
+
return [...pieces.slice(0, open), element, ...pieces.slice(close + 1)];
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Pair the `details` literals of one children array, innermost pairs last.
|
|
179
|
+
* A marker whose partner sits in another parent — `<details>` opened in a list
|
|
180
|
+
* item and closed at the top level — finds nothing here and stays source.
|
|
181
|
+
*/
|
|
182
|
+
function pairDetails(pieces, made) {
|
|
183
|
+
const out = [];
|
|
184
|
+
for (let i = 0; i < pieces.length; i++) {
|
|
185
|
+
const piece = pieces[i];
|
|
186
|
+
if (piece === undefined)
|
|
187
|
+
continue;
|
|
188
|
+
if (!isMarker(piece) || piece.marker !== "details-open") {
|
|
189
|
+
out.push(piece);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
const close = matchingClose(pieces, i);
|
|
193
|
+
const closeMarker = close < 0 ? undefined : pieces[close];
|
|
194
|
+
if (closeMarker === undefined || !isMarker(closeMarker)) {
|
|
195
|
+
out.push(piece);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
const element = foldElement("details", piece.open);
|
|
199
|
+
element.children = resolve(pairSummary(pairDetails(pieces.slice(i + 1, close), made)));
|
|
200
|
+
element.position = spanning(piece.source, closeMarker.source);
|
|
201
|
+
made.add(element);
|
|
202
|
+
out.push(element);
|
|
203
|
+
i = close;
|
|
204
|
+
}
|
|
205
|
+
return out;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Where a fold may be recognised. `p` and the phrasing elements are absent on
|
|
209
|
+
* purpose: HTML forbids `<details>` inside `<p>`, and CommonMark gives only
|
|
210
|
+
* the block form a node of its own, so `a <details>b</details> c` is a line
|
|
211
|
+
* whose author meant those characters.
|
|
212
|
+
*/
|
|
213
|
+
const FLOW_PARENTS = new Set(["blockquote", "li", "td", "th"]);
|
|
214
|
+
/**
|
|
215
|
+
* Cut exactly two tags out of the escaping that raw HTML otherwise gets.
|
|
216
|
+
*
|
|
217
|
+
* react-markdown replaces every `raw` node with a text node holding its
|
|
218
|
+
* source, which is why an unsupported tag reaches the reader as the characters
|
|
219
|
+
* they typed. This plugin runs in the window before that, turns the literals
|
|
220
|
+
* of `MARKER` into elements it builds from constants, and leaves every other
|
|
221
|
+
* `raw` node exactly where it was — so the set of elements the document can
|
|
222
|
+
* hold grows by `details` and `summary` and by nothing else.
|
|
223
|
+
*/
|
|
224
|
+
export function rehypeDetails() {
|
|
225
|
+
return (tree) => {
|
|
226
|
+
const made = new Set();
|
|
227
|
+
const visit = (parent, recognise) => {
|
|
228
|
+
if (recognise) {
|
|
229
|
+
const pieces = [];
|
|
230
|
+
let cut = false;
|
|
231
|
+
for (const child of parent.children) {
|
|
232
|
+
const parts = child.type === "raw" ? splitRaw(child) : null;
|
|
233
|
+
if (parts === null) {
|
|
234
|
+
pieces.push(child);
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
pieces.push(...parts);
|
|
238
|
+
cut = true;
|
|
239
|
+
}
|
|
240
|
+
if (cut) {
|
|
241
|
+
parent.children = resolve(pairDetails(pieces, made));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
for (const child of parent.children) {
|
|
245
|
+
if (child.type !== "element")
|
|
246
|
+
continue;
|
|
247
|
+
// A fold this pass built has had its own children paired already, and
|
|
248
|
+
// re-reading them would let a rejected literal that is now plain text
|
|
249
|
+
// pair with something it never stood beside.
|
|
250
|
+
visit(child, !made.has(child) && FLOW_PARENTS.has(child.tagName));
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
visit(tree, true);
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=rehype-details.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rehype-details.js","sourceRoot":"","sources":["../../src/lib/rehype-details.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Root } from "hast";
|
|
2
|
+
export declare const SOURCE_LINE_ATTR = "data-loc";
|
|
3
|
+
/**
|
|
4
|
+
* Where a code block's contents begin in the source (T-52). The stamped
|
|
5
|
+
* range starts at the opening fence for fenced blocks but at the first code
|
|
6
|
+
* line for indented ones; the renderer records the resolved start so
|
|
7
|
+
* selection anchoring can map a code renderer's per-line rows back to source lines
|
|
8
|
+
* without re-deriving markdown syntax.
|
|
9
|
+
*/
|
|
10
|
+
export declare const CODE_CONTENT_START_ATTR = "data-loc-content-start";
|
|
11
|
+
/** `data-loc="start-end"` → 1-based inclusive source line range. */
|
|
12
|
+
export declare function parseSourceLoc(value: string | null | undefined): {
|
|
13
|
+
start: number;
|
|
14
|
+
end: number;
|
|
15
|
+
} | null;
|
|
16
|
+
/**
|
|
17
|
+
* Rehype plugin stamping every block element with the markdown source line
|
|
18
|
+
* range it was rendered from. This is the whole bridge between "what the
|
|
19
|
+
* reader selected in the rendered document" and "which source lines the
|
|
20
|
+
* anchor stores" — remark/rehype keep positions intact, so no offset math.
|
|
21
|
+
*/
|
|
22
|
+
export declare function rehypeSourceLines(): (tree: Root) => void;
|
|
23
|
+
//# sourceMappingURL=rehype-source-lines.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rehype-source-lines.d.ts","sourceRoot":"","sources":["../../src/lib/rehype-source-lines.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,IAAI,EAAE,MAAM,MAAM,CAAC;AAmC1C,eAAO,MAAM,gBAAgB,aAAa,CAAC;AAE3C;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,2BAA2B,CAAC;AAEhE,oEAAoE;AACpE,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAKvC;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,WACjB,IAAI,UAmBnB"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Block-level tags that make useful annotation anchors. Inline elements are
|
|
3
|
+
* skipped: a selection maps to its closest block, which is the granularity
|
|
4
|
+
* an annotation anchors at.
|
|
5
|
+
*
|
|
6
|
+
* `tr` is on the list even though it nests inside `table` (T-142). A GFM
|
|
7
|
+
* table row is exactly one source line, so stamping rows is what lets one
|
|
8
|
+
* edited cell highlight its own row instead of washing the whole table
|
|
9
|
+
* green; the table keeps its stamp as the fallback for table-wide
|
|
10
|
+
* selections. Consumers resolve nesting by taking the innermost match.
|
|
11
|
+
*
|
|
12
|
+
* `details` is here for a second reason on top of that one (T-385): a reader
|
|
13
|
+
* who closes a fold leaves every annotation inside it with a zero rect, and
|
|
14
|
+
* whatever lays out marks beside them has to walk up the stamped ancestors to
|
|
15
|
+
* find something it can still measure — the fold's own header.
|
|
16
|
+
*/
|
|
17
|
+
const BLOCK_TAGS = new Set([
|
|
18
|
+
"p",
|
|
19
|
+
"h1",
|
|
20
|
+
"h2",
|
|
21
|
+
"h3",
|
|
22
|
+
"h4",
|
|
23
|
+
"h5",
|
|
24
|
+
"h6",
|
|
25
|
+
"li",
|
|
26
|
+
"pre",
|
|
27
|
+
"blockquote",
|
|
28
|
+
"details",
|
|
29
|
+
"table",
|
|
30
|
+
"tr",
|
|
31
|
+
"hr",
|
|
32
|
+
]);
|
|
33
|
+
export const SOURCE_LINE_ATTR = "data-loc";
|
|
34
|
+
/**
|
|
35
|
+
* Where a code block's contents begin in the source (T-52). The stamped
|
|
36
|
+
* range starts at the opening fence for fenced blocks but at the first code
|
|
37
|
+
* line for indented ones; the renderer records the resolved start so
|
|
38
|
+
* selection anchoring can map a code renderer's per-line rows back to source lines
|
|
39
|
+
* without re-deriving markdown syntax.
|
|
40
|
+
*/
|
|
41
|
+
export const CODE_CONTENT_START_ATTR = "data-loc-content-start";
|
|
42
|
+
/** `data-loc="start-end"` → 1-based inclusive source line range. */
|
|
43
|
+
export function parseSourceLoc(value) {
|
|
44
|
+
if (!value)
|
|
45
|
+
return null;
|
|
46
|
+
const match = /^(\d+)-(\d+)$/.exec(value);
|
|
47
|
+
if (!match)
|
|
48
|
+
return null;
|
|
49
|
+
return { start: Number(match[1]), end: Number(match[2]) };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Rehype plugin stamping every block element with the markdown source line
|
|
53
|
+
* range it was rendered from. This is the whole bridge between "what the
|
|
54
|
+
* reader selected in the rendered document" and "which source lines the
|
|
55
|
+
* anchor stores" — remark/rehype keep positions intact, so no offset math.
|
|
56
|
+
*/
|
|
57
|
+
export function rehypeSourceLines() {
|
|
58
|
+
return (tree) => {
|
|
59
|
+
const visit = (node) => {
|
|
60
|
+
if (node.type === "element" &&
|
|
61
|
+
BLOCK_TAGS.has(node.tagName) &&
|
|
62
|
+
node.position?.start.line !== undefined &&
|
|
63
|
+
node.position.end.line !== undefined) {
|
|
64
|
+
node.properties[SOURCE_LINE_ATTR] =
|
|
65
|
+
`${node.position.start.line}-${node.position.end.line}`;
|
|
66
|
+
}
|
|
67
|
+
if ("children" in node) {
|
|
68
|
+
for (const child of node.children) {
|
|
69
|
+
if (child.type === "element")
|
|
70
|
+
visit(child);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
visit(tree);
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=rehype-source-lines.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rehype-source-lines.js","sourceRoot":"","sources":["../../src/lib/rehype-source-lines.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC;IACzB,GAAG;IACH,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,KAAK;IACL,YAAY;IACZ,SAAS;IACT,OAAO;IACP,IAAI;IACJ,IAAI;CACL,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG,UAAU,CAAC;AAE3C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,wBAAwB,CAAC;AAEhE,oEAAoE;AACpE,MAAM,UAAU,cAAc,CAC5B,KAAgC;IAEhC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1C,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC5D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,CAAC,IAAU,EAAE,EAAE;QACpB,MAAM,KAAK,GAAG,CAAC,IAAoB,EAAE,EAAE;YACrC,IACE,IAAI,CAAC,IAAI,KAAK,SAAS;gBACvB,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC;gBAC5B,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,KAAK,SAAS;gBACvC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,SAAS,EACpC,CAAC;gBACD,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC;oBAC/B,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YAC5D,CAAC;YACD,IAAI,UAAU,IAAI,IAAI,EAAE,CAAC;gBACvB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;oBAClC,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;wBAAE,KAAK,CAAC,KAAK,CAAC,CAAC;gBAC7C,CAAC;YACH,CAAC;QACH,CAAC,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,CAAC;IACd,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { Parent, Root, Text } from "mdast";
|
|
2
|
+
import type { Plugin } from "unified";
|
|
3
|
+
/**
|
|
4
|
+
* The subtree a frontmatter block becomes (T-240). Registered with mdast below
|
|
5
|
+
* rather than cast into place, so a walker over the mdast tree narrows on
|
|
6
|
+
* these types the way it does on a table's.
|
|
7
|
+
*/
|
|
8
|
+
export interface Frontmatter extends Parent {
|
|
9
|
+
type: "frontmatter";
|
|
10
|
+
children: FrontmatterBody[];
|
|
11
|
+
}
|
|
12
|
+
export interface FrontmatterBody extends Parent {
|
|
13
|
+
type: "frontmatterBody";
|
|
14
|
+
children: FrontmatterFieldNode[];
|
|
15
|
+
}
|
|
16
|
+
export interface FrontmatterFieldNode extends Parent {
|
|
17
|
+
type: "frontmatterField";
|
|
18
|
+
children: Array<FrontmatterKey | FrontmatterValue>;
|
|
19
|
+
}
|
|
20
|
+
export interface FrontmatterKey extends Parent {
|
|
21
|
+
type: "frontmatterKey";
|
|
22
|
+
children: Text[];
|
|
23
|
+
}
|
|
24
|
+
export interface FrontmatterValue extends Parent {
|
|
25
|
+
type: "frontmatterValue";
|
|
26
|
+
children: Text[];
|
|
27
|
+
}
|
|
28
|
+
declare module "mdast" {
|
|
29
|
+
interface RootContentMap {
|
|
30
|
+
frontmatter: Frontmatter;
|
|
31
|
+
frontmatterBody: FrontmatterBody;
|
|
32
|
+
frontmatterField: FrontmatterFieldNode;
|
|
33
|
+
frontmatterKey: FrontmatterKey;
|
|
34
|
+
frontmatterValue: FrontmatterValue;
|
|
35
|
+
/**
|
|
36
|
+
* `remark-frontmatter`'s other flavour. Core mdast registers `yaml` and
|
|
37
|
+
* leaves this one to whoever turns TOML on.
|
|
38
|
+
*/
|
|
39
|
+
toml: Omit<import("mdast").Yaml, "type"> & {
|
|
40
|
+
type: "toml";
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* One field of a frontmatter block: where its key and its value each sit in
|
|
46
|
+
* the source, as absolute half-open offsets. `key` is null for a line that has
|
|
47
|
+
* no key at all — TOML's `[section]`, or anything ahead of the first field.
|
|
48
|
+
*/
|
|
49
|
+
export type FrontmatterField = {
|
|
50
|
+
key: {
|
|
51
|
+
start: number;
|
|
52
|
+
end: number;
|
|
53
|
+
} | null;
|
|
54
|
+
value: {
|
|
55
|
+
start: number;
|
|
56
|
+
end: number;
|
|
57
|
+
};
|
|
58
|
+
/** 1-based source lines, inclusive; `endLine` grows with continuation lines. */
|
|
59
|
+
line: number;
|
|
60
|
+
endLine: number;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* The flavours `remark-frontmatter` is switched on for. A module constant because it is passed as a
|
|
64
|
+
* plugin option, and a fresh array on every render is a fresh plugin list:
|
|
65
|
+
* react-markdown rebuilds the whole document, and a rebuilt text node
|
|
66
|
+
* collapses whatever selection was sitting in it (T-60).
|
|
67
|
+
*/
|
|
68
|
+
export declare const FRONTMATTER_FLAVOURS: Array<"yaml" | "toml">;
|
|
69
|
+
/**
|
|
70
|
+
* Cut a frontmatter block into fields, one line at a time — deliberately not
|
|
71
|
+
* by parsing YAML (T-240).
|
|
72
|
+
*
|
|
73
|
+
* Decorating the rendered text by source offset rests on every rendered
|
|
74
|
+
* character sitting at a known source offset: a word-level highlight is mapped
|
|
75
|
+
* back onto the source by addition, a text node is entered only while its span
|
|
76
|
+
* and its value agree character for character, and a reader's line/column
|
|
77
|
+
* selection is turned into an offset. Parsing and rendering the
|
|
78
|
+
* *result* breaks all three at once — a key can be normalised, the order
|
|
79
|
+
* reshuffled, `~` spelled `null` — so what comes out of here is only ever a
|
|
80
|
+
* verbatim slice of the source. That is also what lets an annotation anchor on
|
|
81
|
+
* one key or one value, and what lets the page's own search find them.
|
|
82
|
+
*
|
|
83
|
+
* The price, stated where a reader will look for it: a `meta:` with indented
|
|
84
|
+
* children lands whole in one value cell, because a continuation line has
|
|
85
|
+
* nothing but its indentation to say it is one. `white-space: pre-wrap` on the
|
|
86
|
+
* cell keeps that readable; expanding a nested map into rows of its own needs
|
|
87
|
+
* real YAML semantics and is out of scope here.
|
|
88
|
+
*/
|
|
89
|
+
export declare function frontmatterFields(source: string, block: {
|
|
90
|
+
start: {
|
|
91
|
+
line: number;
|
|
92
|
+
};
|
|
93
|
+
end: {
|
|
94
|
+
line: number;
|
|
95
|
+
};
|
|
96
|
+
}, sep: string): FrontmatterField[];
|
|
97
|
+
/**
|
|
98
|
+
* remark plugin: swap each `yaml` / `toml` node for a two-column table of its
|
|
99
|
+
* fields (T-240). It consumes what `remark-frontmatter` produces, so it has to
|
|
100
|
+
* run after it.
|
|
101
|
+
*
|
|
102
|
+
* A table-shaped subtree rather than a bespoke one, because anything that
|
|
103
|
+
* already compares tables reads that shape: it knows `table` / `tableRow` /
|
|
104
|
+
* `tableCell`, pairs rows by their leftmost cell, and splices a removed row
|
|
105
|
+
* back where it stood. Frontmatter is a two-column table whose keys are the
|
|
106
|
+
* row keys, so nothing new has to be taught — the data is laid out at an
|
|
107
|
+
* entrance that exists.
|
|
108
|
+
*
|
|
109
|
+
* Custom node types plus `data.hName`, not real mdast `table` nodes: the
|
|
110
|
+
* `table` handler in `mdast-util-to-hast` makes row 0 a `<thead>` header, and
|
|
111
|
+
* frontmatter has no header — the first field would be drawn as one. `hName`
|
|
112
|
+
* renames an element after that handler has run and cannot undo what it did,
|
|
113
|
+
* so the unknown-node route is the only one that controls the output exactly.
|
|
114
|
+
* It also puts the `<tbody>` there explicitly, which is what keeps a pass
|
|
115
|
+
* that restores a removed field's stand-in row from appending it to the end of
|
|
116
|
+
* the table instead of into its body.
|
|
117
|
+
*/
|
|
118
|
+
export declare const remarkFrontmatterTable: Plugin<[], Root>;
|
|
119
|
+
//# sourceMappingURL=remark-frontmatter-table.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remark-frontmatter-table.d.ts","sourceRoot":"","sources":["../../src/lib/remark-frontmatter-table.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAQ,MAAM,EAAE,IAAI,EAAe,IAAI,EAAE,MAAM,OAAO,CAAC;AACnE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAKtC;;;;GAIG;AACH,MAAM,WAAW,WAAY,SAAQ,MAAM;IACzC,IAAI,EAAE,aAAa,CAAC;IACpB,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,eAAgB,SAAQ,MAAM;IAC7C,IAAI,EAAE,iBAAiB,CAAC;IACxB,QAAQ,EAAE,oBAAoB,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,oBAAqB,SAAQ,MAAM;IAClD,IAAI,EAAE,kBAAkB,CAAC;IACzB,QAAQ,EAAE,KAAK,CAAC,cAAc,GAAG,gBAAgB,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,cAAe,SAAQ,MAAM;IAC5C,IAAI,EAAE,gBAAgB,CAAC;IACvB,QAAQ,EAAE,IAAI,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,gBAAiB,SAAQ,MAAM;IAC9C,IAAI,EAAE,kBAAkB,CAAC;IACzB,QAAQ,EAAE,IAAI,EAAE,CAAC;CAClB;AAUD,OAAO,QAAQ,OAAO,CAAC;IACrB,UAAU,cAAc;QACtB,WAAW,EAAE,WAAW,CAAC;QACzB,eAAe,EAAE,eAAe,CAAC;QACjC,gBAAgB,EAAE,oBAAoB,CAAC;QACvC,cAAc,EAAE,cAAc,CAAC;QAC/B,gBAAgB,EAAE,gBAAgB,CAAC;QACnC;;;WAGG;QACH,IAAI,EAAE,IAAI,CAAC,OAAO,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC;KAC7D;CACF;AAED;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC3C,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAoB,CAAC;AAc7E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAAC,GAAG,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,EACzD,GAAG,EAAE,MAAM,GACV,gBAAgB,EAAE,CAkDpB;AAkID;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,EAAE,EAAE,IAAI,CAgBnD,CAAC"}
|