@ox-content/unplugin 2.9.0 → 2.10.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/dist/esbuild.d.mts +2 -2
- package/dist/esbuild.mjs +2 -2
- package/dist/index.d.mts +199 -7
- package/dist/index.mjs +2 -2
- package/dist/index2.d.mts +2 -2
- package/dist/rollup.d.mts +2 -2
- package/dist/rollup.mjs +2 -2
- package/dist/rspack.d.mts +2 -2
- package/dist/rspack.mjs +2 -2
- package/dist/src.mjs +1076 -20
- package/dist/vite.d.mts +2 -2
- package/dist/vite.mjs +2 -2
- package/dist/webpack.d.mts +2 -2
- package/dist/webpack.mjs +2 -2
- package/package.json +16 -3
package/dist/src.mjs
CHANGED
|
@@ -1,48 +1,824 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
1
2
|
import { createUnplugin } from "unplugin";
|
|
2
3
|
import { createFilter } from "@rollup/pluginutils";
|
|
4
|
+
import MarkdownIt from "markdown-it";
|
|
5
|
+
import rehypeParse from "rehype-parse";
|
|
6
|
+
import rehypeRemark from "rehype-remark";
|
|
7
|
+
import rehypeStringify from "rehype-stringify";
|
|
8
|
+
import remarkParse from "remark-parse";
|
|
9
|
+
import remarkRehype from "remark-rehype";
|
|
10
|
+
import { unified } from "unified";
|
|
11
|
+
const utf8Decoder = new TextDecoder("utf-8");
|
|
12
|
+
function parseTransferEnvelope(buffer) {
|
|
13
|
+
if (buffer.byteLength < 24) return null;
|
|
14
|
+
const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
15
|
+
if (view.getUint32(0, true) !== 1381259343) return null;
|
|
16
|
+
if (view.getUint16(4, true) !== 1) throw new Error("[ox-content] Unsupported transfer buffer version.");
|
|
17
|
+
const kind = view.getUint16(6, true);
|
|
18
|
+
const payloadVersion = view.getUint32(8, true);
|
|
19
|
+
const sectionCount = view.getUint32(12, true);
|
|
20
|
+
const rootHandle = view.getUint32(16, true);
|
|
21
|
+
if (24 + sectionCount * 12 > buffer.byteLength) throw new Error("[ox-content] Transfer buffer section table is truncated.");
|
|
22
|
+
const sections = /* @__PURE__ */ new Map();
|
|
23
|
+
for (let index = 0; index < sectionCount; index += 1) {
|
|
24
|
+
const base = 24 + index * 12;
|
|
25
|
+
const id = view.getUint32(base, true);
|
|
26
|
+
const offset = view.getUint32(base + 4, true);
|
|
27
|
+
const len = view.getUint32(base + 8, true);
|
|
28
|
+
if (offset + len > buffer.byteLength) throw new Error("[ox-content] Transfer buffer references an invalid section.");
|
|
29
|
+
sections.set(id, {
|
|
30
|
+
id,
|
|
31
|
+
offset,
|
|
32
|
+
len
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
kind,
|
|
37
|
+
payloadVersion,
|
|
38
|
+
rootHandle,
|
|
39
|
+
sections
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function requireTransferSection(envelope, id, message) {
|
|
43
|
+
const section = envelope.sections.get(id);
|
|
44
|
+
if (!section) throw new Error(message);
|
|
45
|
+
return section;
|
|
46
|
+
}
|
|
47
|
+
function sliceTransferSection(buffer, section) {
|
|
48
|
+
return buffer.subarray(section.offset, section.offset + section.len);
|
|
49
|
+
}
|
|
50
|
+
function decodeTransferSectionUtf8(buffer, envelope, id, message) {
|
|
51
|
+
return utf8Decoder.decode(sliceTransferSection(buffer, requireTransferSection(envelope, id, message)));
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region src/mdast-raw.ts
|
|
55
|
+
const LEGACY_MAGIC = 827475021;
|
|
56
|
+
const LEGACY_VERSION = 1;
|
|
57
|
+
const LEGACY_HEADER_LEN = 28;
|
|
58
|
+
const NODE_RECORD_LEN = 60;
|
|
59
|
+
const NONE_U32 = 4294967295;
|
|
60
|
+
const MDAST_PAYLOAD_VERSION = 1;
|
|
61
|
+
const MDAST_SECTION_NODES = 1;
|
|
62
|
+
const MDAST_SECTION_CHILD_INDICES = 2;
|
|
63
|
+
const MDAST_SECTION_ALIGNS = 3;
|
|
64
|
+
const MDAST_SECTION_STRINGS = 4;
|
|
65
|
+
const MDAST_SECTION_SOURCE_ORIGIN$1 = 7;
|
|
66
|
+
const KIND_ROOT = 0;
|
|
67
|
+
const KIND_PARAGRAPH = 1;
|
|
68
|
+
const KIND_HEADING = 2;
|
|
69
|
+
const KIND_THEMATIC_BREAK = 3;
|
|
70
|
+
const KIND_BLOCKQUOTE = 4;
|
|
71
|
+
const KIND_LIST = 5;
|
|
72
|
+
const KIND_LIST_ITEM = 6;
|
|
73
|
+
const KIND_CODE = 7;
|
|
74
|
+
const KIND_HTML = 8;
|
|
75
|
+
const KIND_TABLE = 9;
|
|
76
|
+
const KIND_TABLE_ROW = 10;
|
|
77
|
+
const KIND_TABLE_CELL = 11;
|
|
78
|
+
const KIND_TEXT = 12;
|
|
79
|
+
const KIND_EMPHASIS = 13;
|
|
80
|
+
const KIND_STRONG = 14;
|
|
81
|
+
const KIND_INLINE_CODE = 15;
|
|
82
|
+
const KIND_BREAK = 16;
|
|
83
|
+
const KIND_LINK = 17;
|
|
84
|
+
const KIND_IMAGE = 18;
|
|
85
|
+
const KIND_DELETE = 19;
|
|
86
|
+
const KIND_FOOTNOTE_REFERENCE = 20;
|
|
87
|
+
const KIND_DEFINITION = 21;
|
|
88
|
+
const KIND_FOOTNOTE_DEFINITION = 22;
|
|
89
|
+
const FLAG_ORDERED = 1;
|
|
90
|
+
const FLAG_SPREAD = 2;
|
|
91
|
+
const FLAG_CHECKED_PRESENT = 4;
|
|
92
|
+
const FLAG_CHECKED_VALUE = 8;
|
|
93
|
+
function deserializeMdastFromRaw(buffer, source) {
|
|
94
|
+
const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
95
|
+
const decoder = new TextDecoder("utf-8");
|
|
96
|
+
const { nodeCount, childCount, alignCount, rootIndex, nodesOffset, childIndicesOffset, alignsOffset, stringsOffset, stringsEnd } = resolveMdastLayout(buffer, view);
|
|
97
|
+
if (stringsEnd > buffer.byteLength) throw new Error("[ox-content] mdast raw transfer buffer is truncated.");
|
|
98
|
+
const sourceIndex = buildSourceIndex(source);
|
|
99
|
+
const sourceOrigin = readSourceOrigin(buffer, view);
|
|
100
|
+
const readString = (offset, len) => {
|
|
101
|
+
if (offset === NONE_U32) return;
|
|
102
|
+
const start = stringsOffset + offset;
|
|
103
|
+
const end = start + len;
|
|
104
|
+
if (end > stringsEnd) throw new Error("[ox-content] mdast raw transfer buffer references an invalid string.");
|
|
105
|
+
return decoder.decode(buffer.subarray(start, end));
|
|
106
|
+
};
|
|
107
|
+
const readPosition = (startOffset, endOffset) => {
|
|
108
|
+
const start = pointForByteOffset(startOffset, sourceIndex);
|
|
109
|
+
const end = pointForByteOffset(endOffset, sourceIndex);
|
|
110
|
+
if (!sourceOrigin) return {
|
|
111
|
+
start,
|
|
112
|
+
end
|
|
113
|
+
};
|
|
114
|
+
return {
|
|
115
|
+
start: applySourceOrigin(start, sourceOrigin),
|
|
116
|
+
end: applySourceOrigin(end, sourceOrigin)
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
const readChildIndex = (index) => {
|
|
120
|
+
if (index >= childCount) throw new Error("[ox-content] mdast raw transfer buffer references an invalid child index.");
|
|
121
|
+
return view.getUint32(childIndicesOffset + index * 4, true);
|
|
122
|
+
};
|
|
123
|
+
const readChildren = (start, len) => {
|
|
124
|
+
if (start + len > childCount) throw new Error("[ox-content] mdast raw transfer buffer has an invalid child range.");
|
|
125
|
+
const children = [];
|
|
126
|
+
for (let index = 0; index < len; index += 1) children.push(readNode(readChildIndex(start + index)));
|
|
127
|
+
return children;
|
|
128
|
+
};
|
|
129
|
+
const readAlign = (start, len) => {
|
|
130
|
+
if (start + len > alignCount) throw new Error("[ox-content] mdast raw transfer buffer has an invalid table alignment range.");
|
|
131
|
+
const align = [];
|
|
132
|
+
for (let index = 0; index < len; index += 1) {
|
|
133
|
+
const value = buffer[alignsOffset + start + index];
|
|
134
|
+
align.push(value === 1 ? "left" : value === 2 ? "center" : value === 3 ? "right" : null);
|
|
135
|
+
}
|
|
136
|
+
return align;
|
|
137
|
+
};
|
|
138
|
+
const readNode = (index) => {
|
|
139
|
+
if (index >= nodeCount) throw new Error("[ox-content] mdast raw transfer buffer references an invalid node.");
|
|
140
|
+
const base = nodesOffset + index * NODE_RECORD_LEN;
|
|
141
|
+
const kind = view.getUint8(base);
|
|
142
|
+
const flags = view.getUint8(base + 1);
|
|
143
|
+
const spanStart = view.getUint32(base + 4, true);
|
|
144
|
+
const spanEnd = view.getUint32(base + 8, true);
|
|
145
|
+
const childStart = view.getUint32(base + 12, true);
|
|
146
|
+
const childLen = view.getUint32(base + 16, true);
|
|
147
|
+
const num0 = view.getUint32(base + 20, true);
|
|
148
|
+
const num1 = view.getUint32(base + 24, true);
|
|
149
|
+
const str0 = readString(view.getUint32(base + 28, true), view.getUint32(base + 32, true));
|
|
150
|
+
const str1 = readString(view.getUint32(base + 36, true), view.getUint32(base + 40, true));
|
|
151
|
+
const str2 = readString(view.getUint32(base + 44, true), view.getUint32(base + 48, true));
|
|
152
|
+
const str3 = readString(view.getUint32(base + 52, true), view.getUint32(base + 56, true));
|
|
153
|
+
const children = childLen > 0 ? readChildren(childStart, childLen) : void 0;
|
|
154
|
+
const position = readPosition(spanStart, spanEnd);
|
|
155
|
+
switch (kind) {
|
|
156
|
+
case KIND_ROOT: return {
|
|
157
|
+
type: "root",
|
|
158
|
+
children: children ?? [],
|
|
159
|
+
position
|
|
160
|
+
};
|
|
161
|
+
case KIND_PARAGRAPH: return {
|
|
162
|
+
type: "paragraph",
|
|
163
|
+
children: children ?? [],
|
|
164
|
+
position
|
|
165
|
+
};
|
|
166
|
+
case KIND_HEADING: return {
|
|
167
|
+
type: "heading",
|
|
168
|
+
depth: num0,
|
|
169
|
+
children: children ?? [],
|
|
170
|
+
position
|
|
171
|
+
};
|
|
172
|
+
case KIND_THEMATIC_BREAK: return {
|
|
173
|
+
type: "thematicBreak",
|
|
174
|
+
position
|
|
175
|
+
};
|
|
176
|
+
case KIND_BLOCKQUOTE: return {
|
|
177
|
+
type: "blockquote",
|
|
178
|
+
children: children ?? [],
|
|
179
|
+
position
|
|
180
|
+
};
|
|
181
|
+
case KIND_LIST: {
|
|
182
|
+
const node = {
|
|
183
|
+
type: "list",
|
|
184
|
+
ordered: (flags & FLAG_ORDERED) !== 0,
|
|
185
|
+
spread: (flags & FLAG_SPREAD) !== 0,
|
|
186
|
+
children: children ?? [],
|
|
187
|
+
position
|
|
188
|
+
};
|
|
189
|
+
if (num0 !== NONE_U32) node.start = num0;
|
|
190
|
+
return node;
|
|
191
|
+
}
|
|
192
|
+
case KIND_LIST_ITEM: {
|
|
193
|
+
const node = {
|
|
194
|
+
type: "listItem",
|
|
195
|
+
spread: (flags & FLAG_SPREAD) !== 0,
|
|
196
|
+
children: children ?? [],
|
|
197
|
+
position
|
|
198
|
+
};
|
|
199
|
+
if ((flags & FLAG_CHECKED_PRESENT) !== 0) node.checked = (flags & FLAG_CHECKED_VALUE) !== 0;
|
|
200
|
+
return node;
|
|
201
|
+
}
|
|
202
|
+
case KIND_CODE: {
|
|
203
|
+
const node = {
|
|
204
|
+
type: "code",
|
|
205
|
+
value: str2 ?? "",
|
|
206
|
+
position
|
|
207
|
+
};
|
|
208
|
+
if (str0 !== void 0) node.lang = str0;
|
|
209
|
+
if (str1 !== void 0) node.meta = str1;
|
|
210
|
+
return node;
|
|
211
|
+
}
|
|
212
|
+
case KIND_HTML: return {
|
|
213
|
+
type: "html",
|
|
214
|
+
value: str0 ?? "",
|
|
215
|
+
position
|
|
216
|
+
};
|
|
217
|
+
case KIND_TABLE: return {
|
|
218
|
+
type: "table",
|
|
219
|
+
align: readAlign(num0, num1),
|
|
220
|
+
children: children ?? [],
|
|
221
|
+
position
|
|
222
|
+
};
|
|
223
|
+
case KIND_TABLE_ROW: return {
|
|
224
|
+
type: "tableRow",
|
|
225
|
+
children: children ?? [],
|
|
226
|
+
position
|
|
227
|
+
};
|
|
228
|
+
case KIND_TABLE_CELL: return {
|
|
229
|
+
type: "tableCell",
|
|
230
|
+
children: children ?? [],
|
|
231
|
+
position
|
|
232
|
+
};
|
|
233
|
+
case KIND_TEXT: return {
|
|
234
|
+
type: "text",
|
|
235
|
+
value: str0 ?? "",
|
|
236
|
+
position
|
|
237
|
+
};
|
|
238
|
+
case KIND_EMPHASIS: return {
|
|
239
|
+
type: "emphasis",
|
|
240
|
+
children: children ?? [],
|
|
241
|
+
position
|
|
242
|
+
};
|
|
243
|
+
case KIND_STRONG: return {
|
|
244
|
+
type: "strong",
|
|
245
|
+
children: children ?? [],
|
|
246
|
+
position
|
|
247
|
+
};
|
|
248
|
+
case KIND_INLINE_CODE: return {
|
|
249
|
+
type: "inlineCode",
|
|
250
|
+
value: str0 ?? "",
|
|
251
|
+
position
|
|
252
|
+
};
|
|
253
|
+
case KIND_BREAK: return {
|
|
254
|
+
type: "break",
|
|
255
|
+
position
|
|
256
|
+
};
|
|
257
|
+
case KIND_LINK: {
|
|
258
|
+
const node = {
|
|
259
|
+
type: "link",
|
|
260
|
+
url: str0 ?? "",
|
|
261
|
+
children: children ?? [],
|
|
262
|
+
position
|
|
263
|
+
};
|
|
264
|
+
if (str1 !== void 0) node.title = str1;
|
|
265
|
+
return node;
|
|
266
|
+
}
|
|
267
|
+
case KIND_IMAGE: {
|
|
268
|
+
const node = {
|
|
269
|
+
type: "image",
|
|
270
|
+
url: str0 ?? "",
|
|
271
|
+
position
|
|
272
|
+
};
|
|
273
|
+
if (str1 !== void 0) node.alt = str1;
|
|
274
|
+
if (str2 !== void 0) node.title = str2;
|
|
275
|
+
return node;
|
|
276
|
+
}
|
|
277
|
+
case KIND_DELETE: return {
|
|
278
|
+
type: "delete",
|
|
279
|
+
children: children ?? [],
|
|
280
|
+
position
|
|
281
|
+
};
|
|
282
|
+
case KIND_FOOTNOTE_REFERENCE: {
|
|
283
|
+
const node = {
|
|
284
|
+
type: "footnoteReference",
|
|
285
|
+
identifier: str0 ?? "",
|
|
286
|
+
position
|
|
287
|
+
};
|
|
288
|
+
if (str1 !== void 0) node.label = str1;
|
|
289
|
+
return node;
|
|
290
|
+
}
|
|
291
|
+
case KIND_DEFINITION: {
|
|
292
|
+
const node = {
|
|
293
|
+
type: "definition",
|
|
294
|
+
identifier: str0 ?? "",
|
|
295
|
+
url: str2 ?? "",
|
|
296
|
+
position
|
|
297
|
+
};
|
|
298
|
+
if (str1 !== void 0) node.label = str1;
|
|
299
|
+
if (str3 !== void 0) node.title = str3;
|
|
300
|
+
return node;
|
|
301
|
+
}
|
|
302
|
+
case KIND_FOOTNOTE_DEFINITION: {
|
|
303
|
+
const node = {
|
|
304
|
+
type: "footnoteDefinition",
|
|
305
|
+
identifier: str0 ?? "",
|
|
306
|
+
children: children ?? [],
|
|
307
|
+
position
|
|
308
|
+
};
|
|
309
|
+
if (str1 !== void 0) node.label = str1;
|
|
310
|
+
return node;
|
|
311
|
+
}
|
|
312
|
+
default: throw new Error(`[ox-content] Unsupported mdast raw node kind: ${kind}`);
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
const root = readNode(rootIndex);
|
|
316
|
+
if (root.type !== "root" || !Array.isArray(root.children)) throw new Error("[ox-content] Native parser returned an invalid mdast root.");
|
|
317
|
+
return root;
|
|
318
|
+
}
|
|
319
|
+
function readSourceOrigin(buffer, view) {
|
|
320
|
+
const section = parseTransferEnvelope(buffer)?.sections.get(MDAST_SECTION_SOURCE_ORIGIN$1);
|
|
321
|
+
if (!section) return;
|
|
322
|
+
if (section.len !== 16) throw new Error("[ox-content] mdast transfer source origin section is misaligned.");
|
|
323
|
+
return {
|
|
324
|
+
byteOffset: view.getUint32(section.offset, true),
|
|
325
|
+
offset: view.getUint32(section.offset + 4, true),
|
|
326
|
+
line: view.getUint32(section.offset + 8, true),
|
|
327
|
+
column: view.getUint32(section.offset + 12, true)
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
function resolveMdastLayout(buffer, view) {
|
|
331
|
+
const envelope = parseTransferEnvelope(buffer);
|
|
332
|
+
if (envelope) {
|
|
333
|
+
if (envelope.kind !== 1) throw new Error("[ox-content] Transfer buffer does not contain an mdast payload.");
|
|
334
|
+
if (envelope.payloadVersion !== MDAST_PAYLOAD_VERSION) throw new Error("[ox-content] Unsupported mdast transfer payload version.");
|
|
335
|
+
const nodesSection = requireTransferSection(envelope, MDAST_SECTION_NODES, "[ox-content] mdast transfer is missing the nodes section.");
|
|
336
|
+
const childIndicesSection = requireTransferSection(envelope, MDAST_SECTION_CHILD_INDICES, "[ox-content] mdast transfer is missing the child indices section.");
|
|
337
|
+
const alignsSection = requireTransferSection(envelope, MDAST_SECTION_ALIGNS, "[ox-content] mdast transfer is missing the table alignments section.");
|
|
338
|
+
const stringsSection = requireTransferSection(envelope, MDAST_SECTION_STRINGS, "[ox-content] mdast transfer is missing the string table section.");
|
|
339
|
+
if (nodesSection.len % NODE_RECORD_LEN !== 0) throw new Error("[ox-content] mdast transfer nodes section is misaligned.");
|
|
340
|
+
if (childIndicesSection.len % 4 !== 0) throw new Error("[ox-content] mdast transfer child indices section is misaligned.");
|
|
341
|
+
return {
|
|
342
|
+
nodeCount: nodesSection.len / NODE_RECORD_LEN,
|
|
343
|
+
childCount: childIndicesSection.len / 4,
|
|
344
|
+
alignCount: alignsSection.len,
|
|
345
|
+
rootIndex: envelope.rootHandle,
|
|
346
|
+
nodesOffset: nodesSection.offset,
|
|
347
|
+
childIndicesOffset: childIndicesSection.offset,
|
|
348
|
+
alignsOffset: alignsSection.offset,
|
|
349
|
+
stringsOffset: stringsSection.offset,
|
|
350
|
+
stringsEnd: stringsSection.offset + stringsSection.len
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
const magic = view.getUint32(0, true);
|
|
354
|
+
const version = view.getUint32(4, true);
|
|
355
|
+
if (magic !== LEGACY_MAGIC || version !== LEGACY_VERSION) throw new Error("[ox-content] Invalid mdast raw transfer buffer.");
|
|
356
|
+
const nodeCount = view.getUint32(8, true);
|
|
357
|
+
const childCount = view.getUint32(12, true);
|
|
358
|
+
const alignCount = view.getUint32(16, true);
|
|
359
|
+
const stringBytesLen = view.getUint32(20, true);
|
|
360
|
+
const rootIndex = view.getUint32(24, true);
|
|
361
|
+
const nodesOffset = LEGACY_HEADER_LEN;
|
|
362
|
+
const childIndicesOffset = nodesOffset + nodeCount * NODE_RECORD_LEN;
|
|
363
|
+
const alignsOffset = childIndicesOffset + childCount * 4;
|
|
364
|
+
const stringsOffset = alignsOffset + alignCount;
|
|
365
|
+
return {
|
|
366
|
+
nodeCount,
|
|
367
|
+
childCount,
|
|
368
|
+
alignCount,
|
|
369
|
+
rootIndex,
|
|
370
|
+
nodesOffset,
|
|
371
|
+
childIndicesOffset,
|
|
372
|
+
alignsOffset,
|
|
373
|
+
stringsOffset,
|
|
374
|
+
stringsEnd: stringsOffset + stringBytesLen
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
function buildSourceIndex(source) {
|
|
378
|
+
const points = [{
|
|
379
|
+
byteOffset: 0,
|
|
380
|
+
offset: 0,
|
|
381
|
+
line: 1,
|
|
382
|
+
column: 1
|
|
383
|
+
}];
|
|
384
|
+
let byteOffset = 0;
|
|
385
|
+
let offset = 0;
|
|
386
|
+
let line = 1;
|
|
387
|
+
let column = 1;
|
|
388
|
+
for (const character of source) {
|
|
389
|
+
byteOffset += utf8ByteLength(character);
|
|
390
|
+
offset += character.length;
|
|
391
|
+
if (character === "\n") {
|
|
392
|
+
line += 1;
|
|
393
|
+
column = 1;
|
|
394
|
+
} else column += 1;
|
|
395
|
+
points.push({
|
|
396
|
+
byteOffset,
|
|
397
|
+
offset,
|
|
398
|
+
line,
|
|
399
|
+
column
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
return points;
|
|
403
|
+
}
|
|
404
|
+
function pointForByteOffset(byteOffset, points) {
|
|
405
|
+
const maxByteOffset = points[points.length - 1]?.byteOffset ?? 0;
|
|
406
|
+
const clampedOffset = Math.max(0, Math.min(byteOffset, maxByteOffset));
|
|
407
|
+
let low = 0;
|
|
408
|
+
let high = points.length - 1;
|
|
409
|
+
while (low <= high) {
|
|
410
|
+
const middle = low + high >> 1;
|
|
411
|
+
const point = points[middle];
|
|
412
|
+
const nextPoint = points[middle + 1];
|
|
413
|
+
if (clampedOffset < point.byteOffset) high = middle - 1;
|
|
414
|
+
else if (nextPoint && clampedOffset >= nextPoint.byteOffset) low = middle + 1;
|
|
415
|
+
else return {
|
|
416
|
+
line: point.line,
|
|
417
|
+
column: point.column,
|
|
418
|
+
offset: point.offset
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
const point = points[points.length - 1] ?? {
|
|
422
|
+
byteOffset: 0,
|
|
423
|
+
offset: 0,
|
|
424
|
+
line: 1,
|
|
425
|
+
column: 1
|
|
426
|
+
};
|
|
427
|
+
return {
|
|
428
|
+
line: point.line,
|
|
429
|
+
column: point.column,
|
|
430
|
+
offset: point.offset
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
function applySourceOrigin(point, origin) {
|
|
434
|
+
return {
|
|
435
|
+
line: point.line + origin.line - 1,
|
|
436
|
+
column: point.line === 1 ? point.column + origin.column - 1 : point.column,
|
|
437
|
+
offset: point.offset + origin.offset
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
function utf8ByteLength(character) {
|
|
441
|
+
const codePoint = character.codePointAt(0);
|
|
442
|
+
if (codePoint === void 0) return 0;
|
|
443
|
+
if (codePoint <= 127) return 1;
|
|
444
|
+
if (codePoint <= 2047) return 2;
|
|
445
|
+
if (codePoint <= 65535) return 3;
|
|
446
|
+
return 4;
|
|
447
|
+
}
|
|
448
|
+
//#endregion
|
|
449
|
+
//#region src/mdast.ts
|
|
450
|
+
const require$1 = createRequire(import.meta.url);
|
|
451
|
+
let cachedNapiBindings$1;
|
|
452
|
+
const DEFAULT_MDAST_OPTIONS = {
|
|
453
|
+
gfm: true,
|
|
454
|
+
footnotes: true,
|
|
455
|
+
taskLists: true,
|
|
456
|
+
tables: true,
|
|
457
|
+
strikethrough: true,
|
|
458
|
+
autolinks: true
|
|
459
|
+
};
|
|
460
|
+
/**
|
|
461
|
+
* Unified parser plugin backed by Ox Content's native mdast parser.
|
|
462
|
+
*
|
|
463
|
+
* This lets existing remark/unified plugins run on top of Ox Content's parser
|
|
464
|
+
* while keeping a more mdast-native integration point.
|
|
465
|
+
*/
|
|
466
|
+
function oxContentMdast(options = {}) {
|
|
467
|
+
this.parser = (document) => parseMarkdownToMdast(document, options);
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Defines an Ox Content-native mdast plugin.
|
|
471
|
+
*/
|
|
472
|
+
function defineMdastPlugin(name, transform) {
|
|
473
|
+
return {
|
|
474
|
+
name,
|
|
475
|
+
transform
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Parses markdown source into an mdast-compatible tree using the native parser.
|
|
480
|
+
*/
|
|
481
|
+
function parseMarkdownToMdast(source, options = {}) {
|
|
482
|
+
const napi = loadNapiBindings$1();
|
|
483
|
+
const resolvedOptions = resolveMdastOptions(options);
|
|
484
|
+
const parserOptions = {
|
|
485
|
+
gfm: resolvedOptions.gfm,
|
|
486
|
+
footnotes: resolvedOptions.footnotes,
|
|
487
|
+
taskLists: resolvedOptions.taskLists,
|
|
488
|
+
tables: resolvedOptions.tables,
|
|
489
|
+
strikethrough: resolvedOptions.strikethrough,
|
|
490
|
+
autolinks: resolvedOptions.autolinks
|
|
491
|
+
};
|
|
492
|
+
return deserializeMdastFromRaw(requireNapiMethod$1(napi.parseTransferRaw, "parseTransferRaw")(source, "mdast", parserOptions), source);
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Wraps an Ox Content-native mdast plugin as a standard unified transformer.
|
|
496
|
+
*/
|
|
497
|
+
function toUnifiedMdastPlugin(plugin, context) {
|
|
498
|
+
return () => {
|
|
499
|
+
return async (tree) => {
|
|
500
|
+
return await plugin.transform(tree, context) ?? tree;
|
|
501
|
+
};
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Extracts a nested TOC tree from mdast after mdast-stage plugins have run.
|
|
506
|
+
*/
|
|
507
|
+
function extractTocFromMdast(root, maxDepth) {
|
|
508
|
+
const headings = [];
|
|
509
|
+
visitMdast(root, (node) => {
|
|
510
|
+
if (node.type !== "heading") return;
|
|
511
|
+
const depth = typeof node.depth === "number" ? node.depth : 0;
|
|
512
|
+
if (depth < 1 || depth > maxDepth) return;
|
|
513
|
+
const text = collectMdastText(node);
|
|
514
|
+
headings.push({
|
|
515
|
+
depth,
|
|
516
|
+
text,
|
|
517
|
+
slug: slugify$1(text),
|
|
518
|
+
children: []
|
|
519
|
+
});
|
|
520
|
+
});
|
|
521
|
+
return buildTocTree$1(headings);
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Creates the context passed to Ox Content-native mdast plugins.
|
|
525
|
+
*/
|
|
526
|
+
function createMdastPluginContext(filePath, source, frontmatter, options, sourceOffset) {
|
|
527
|
+
return {
|
|
528
|
+
filePath,
|
|
529
|
+
source,
|
|
530
|
+
frontmatter,
|
|
531
|
+
sourceOffset,
|
|
532
|
+
options
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
function loadNapiBindings$1() {
|
|
536
|
+
if (cachedNapiBindings$1) return cachedNapiBindings$1;
|
|
537
|
+
if (cachedNapiBindings$1 === null) throw new Error("[ox-content] Failed to load @ox-content/napi. Please ensure the NAPI module is built. Run: mise run build:napi");
|
|
538
|
+
try {
|
|
539
|
+
cachedNapiBindings$1 = require$1("@ox-content/napi");
|
|
540
|
+
return cachedNapiBindings$1;
|
|
541
|
+
} catch {
|
|
542
|
+
cachedNapiBindings$1 = null;
|
|
543
|
+
throw new Error("[ox-content] Failed to load @ox-content/napi. Please ensure the NAPI module is built. Run: mise run build:napi");
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
function requireNapiMethod$1(method, name) {
|
|
547
|
+
if (typeof method === "function") return method;
|
|
548
|
+
throw new Error(`[ox-content] @ox-content/napi is too old for mdast interop: missing ${name}(). Rebuild the NAPI module with \`vp run build:napi\`.`);
|
|
549
|
+
}
|
|
550
|
+
function resolveMdastOptions(options) {
|
|
551
|
+
return {
|
|
552
|
+
gfm: options.gfm ?? DEFAULT_MDAST_OPTIONS.gfm,
|
|
553
|
+
footnotes: options.footnotes ?? DEFAULT_MDAST_OPTIONS.footnotes,
|
|
554
|
+
taskLists: options.taskLists ?? DEFAULT_MDAST_OPTIONS.taskLists,
|
|
555
|
+
tables: options.tables ?? DEFAULT_MDAST_OPTIONS.tables,
|
|
556
|
+
strikethrough: options.strikethrough ?? DEFAULT_MDAST_OPTIONS.strikethrough,
|
|
557
|
+
autolinks: options.autolinks ?? DEFAULT_MDAST_OPTIONS.autolinks
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
function buildTocTree$1(entries) {
|
|
561
|
+
const result = [];
|
|
562
|
+
const stack = [];
|
|
563
|
+
for (const entry of entries) {
|
|
564
|
+
const tocEntry = {
|
|
565
|
+
depth: entry.depth,
|
|
566
|
+
text: entry.text,
|
|
567
|
+
slug: entry.slug,
|
|
568
|
+
children: []
|
|
569
|
+
};
|
|
570
|
+
while (stack.length > 0 && stack[stack.length - 1].depth >= entry.depth) stack.pop();
|
|
571
|
+
if (stack.length === 0) result.push(tocEntry);
|
|
572
|
+
else stack[stack.length - 1].children.push(tocEntry);
|
|
573
|
+
stack.push(tocEntry);
|
|
574
|
+
}
|
|
575
|
+
return result;
|
|
576
|
+
}
|
|
577
|
+
function visitMdast(node, visitor) {
|
|
578
|
+
if (!node || typeof node !== "object") return;
|
|
579
|
+
if ("type" in node && typeof node.type === "string") visitor(node);
|
|
580
|
+
if (!Array.isArray(node.children)) return;
|
|
581
|
+
for (const child of node.children) visitMdast(child, visitor);
|
|
582
|
+
}
|
|
583
|
+
function collectMdastText(node) {
|
|
584
|
+
if (!node || typeof node !== "object") return "";
|
|
585
|
+
if (typeof node.value === "string") return node.value;
|
|
586
|
+
if (!Array.isArray(node.children)) return "";
|
|
587
|
+
return node.children.map((child) => collectMdastText(child)).join("");
|
|
588
|
+
}
|
|
589
|
+
function slugify$1(text) {
|
|
590
|
+
return text.toLowerCase().replace(/[^\p{L}\p{N}\s-]+/gu, " ").trim().split(/\s+/).filter(Boolean).join("-");
|
|
591
|
+
}
|
|
592
|
+
//#endregion
|
|
3
593
|
//#region src/transform.ts
|
|
4
594
|
/**
|
|
595
|
+
* Markdown transformation logic for @ox-content/unplugin.
|
|
596
|
+
*
|
|
597
|
+
* Uses Rust-based parser via NAPI bindings for optimal performance.
|
|
598
|
+
*/
|
|
599
|
+
const require = createRequire(import.meta.url);
|
|
600
|
+
let cachedNapiBindings;
|
|
601
|
+
const MDAST_SECTION_CONTENT = 5;
|
|
602
|
+
const MDAST_SECTION_FRONTMATTER = 6;
|
|
603
|
+
const MDAST_SECTION_SOURCE_ORIGIN = 7;
|
|
604
|
+
const PREPARED_SOURCE_SECTION_CONTENT = 1;
|
|
605
|
+
const PREPARED_SOURCE_SECTION_FRONTMATTER = 2;
|
|
606
|
+
const PREPARED_SOURCE_SECTION_SOURCE_ORIGIN = 3;
|
|
607
|
+
/**
|
|
5
608
|
* Transforms Markdown content into a JavaScript module.
|
|
6
|
-
* Uses Rust-based
|
|
609
|
+
* Uses Rust-based parsing, and switches to a unified/mdast pipeline when
|
|
610
|
+
* JavaScript mdast/remark/rehype plugins are configured.
|
|
7
611
|
*
|
|
8
612
|
* Note: This requires the @ox-content/napi package to be built.
|
|
9
|
-
* NAPI bindings are loaded dynamically to support both Node.js and build-time execution.
|
|
10
613
|
*/
|
|
11
614
|
async function transformMarkdown(source, filePath, options) {
|
|
12
|
-
|
|
615
|
+
const transformed = hasMarkdownItPlugins(options) ? await transformWithMarkdownIt(source, filePath, options) : hasUnifiedPlugins(options) ? await transformWithUnified(source, filePath, options) : transformWithNativePipeline(loadNapiBindings(), source, options);
|
|
616
|
+
let nextHtml = transformed.html;
|
|
617
|
+
for (const plugin of options.plugin.oxContent) nextHtml = await plugin(nextHtml);
|
|
618
|
+
return {
|
|
619
|
+
code: generateModuleCode(nextHtml, transformed.frontmatter, transformed.toc, filePath),
|
|
620
|
+
html: nextHtml,
|
|
621
|
+
frontmatter: transformed.frontmatter,
|
|
622
|
+
toc: transformed.toc
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
function loadNapiBindings() {
|
|
626
|
+
if (cachedNapiBindings) return cachedNapiBindings;
|
|
627
|
+
if (cachedNapiBindings === null) throw new Error("[ox-content] Failed to load @ox-content/napi. Please ensure the NAPI module is built. Run: nix develop -c vp run build:napi");
|
|
13
628
|
try {
|
|
14
|
-
|
|
629
|
+
cachedNapiBindings = require("@ox-content/napi");
|
|
630
|
+
return cachedNapiBindings;
|
|
15
631
|
} catch {
|
|
16
|
-
|
|
632
|
+
cachedNapiBindings = null;
|
|
633
|
+
throw new Error("[ox-content] Failed to load @ox-content/napi. Please ensure the NAPI module is built. Run: mise run build:napi");
|
|
17
634
|
}
|
|
18
|
-
|
|
635
|
+
}
|
|
636
|
+
function createNapiTransformOptions(options) {
|
|
637
|
+
const codeAnnotations = options.codeAnnotations ?? {
|
|
638
|
+
enabled: false,
|
|
639
|
+
metaKey: "annotate"
|
|
640
|
+
};
|
|
641
|
+
return {
|
|
19
642
|
gfm: options.gfm,
|
|
20
643
|
footnotes: options.footnotes,
|
|
21
|
-
|
|
644
|
+
taskLists: options.taskLists,
|
|
22
645
|
tables: options.tables,
|
|
23
646
|
strikethrough: options.strikethrough,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
647
|
+
autolinks: options.gfm,
|
|
648
|
+
frontmatter: options.frontmatter,
|
|
649
|
+
tocMaxDepth: options.tocMaxDepth,
|
|
650
|
+
codeAnnotations: codeAnnotations.enabled,
|
|
651
|
+
codeAnnotationMetaKey: codeAnnotations.metaKey
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
function parseFrontmatterJson(json) {
|
|
655
|
+
if (json.length === 0) return {};
|
|
30
656
|
try {
|
|
31
|
-
|
|
657
|
+
const value = JSON.parse(json);
|
|
658
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
32
659
|
} catch {
|
|
33
|
-
|
|
660
|
+
return {};
|
|
34
661
|
}
|
|
662
|
+
}
|
|
663
|
+
function prepareSourceWithRust(napi, source, options) {
|
|
664
|
+
return deserializePreparedSource(requireNapiMethod(napi.prepareSourceRaw, "prepareSourceRaw")(source, { frontmatter: options.frontmatter }));
|
|
665
|
+
}
|
|
666
|
+
function hasUnifiedPlugins(options) {
|
|
667
|
+
return options.plugin.mdast.length > 0 || options.plugin.remark.length > 0 || options.plugin.rehype.length > 0;
|
|
668
|
+
}
|
|
669
|
+
function hasMarkdownItPlugins(options) {
|
|
670
|
+
return options.plugin.markdownIt.length > 0;
|
|
671
|
+
}
|
|
672
|
+
function hasMdastOrRemarkPlugins(options) {
|
|
673
|
+
return options.plugin.mdast.length > 0 || options.plugin.remark.length > 0;
|
|
674
|
+
}
|
|
675
|
+
function transformWithNativePipeline(napi, source, options) {
|
|
676
|
+
const result = napi.transform(source, createNapiTransformOptions(options));
|
|
677
|
+
if (result.errors.length > 0) console.warn("[ox-content] Transform warnings:", result.errors);
|
|
35
678
|
const flatToc = result.toc.map((item) => ({
|
|
36
679
|
...item,
|
|
37
680
|
children: []
|
|
38
681
|
}));
|
|
39
|
-
const toc = options.toc ? buildTocTree(flatToc) : [];
|
|
40
|
-
let html = result.html;
|
|
41
|
-
for (const plugin of options.plugin.oxContent) html = await plugin(html);
|
|
42
682
|
return {
|
|
43
|
-
|
|
683
|
+
html: result.html,
|
|
684
|
+
frontmatter: parseFrontmatterJson(result.frontmatter),
|
|
685
|
+
toc: options.toc ? buildTocTree(flatToc) : []
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
function transformWithNativeMdastPipeline(napi, source, options) {
|
|
689
|
+
return deserializeNativeMdastTransform(requireNapiMethod(napi.transformMdastRaw, "transformMdastRaw")(source, createNapiTransformOptions(options)));
|
|
690
|
+
}
|
|
691
|
+
function deserializeNativeMdastTransform(buffer) {
|
|
692
|
+
const envelope = parseTransferEnvelope(buffer);
|
|
693
|
+
if (!envelope) throw new Error("[ox-content] Native mdast transform buffer is not a transfer envelope.");
|
|
694
|
+
const content = decodeTransferSectionUtf8(buffer, envelope, MDAST_SECTION_CONTENT, "[ox-content] mdast transform transfer is missing the content section.");
|
|
695
|
+
const frontmatter = parseFrontmatterJson(decodeTransferSectionUtf8(buffer, envelope, MDAST_SECTION_FRONTMATTER, "[ox-content] mdast transform transfer is missing the frontmatter section."));
|
|
696
|
+
return {
|
|
697
|
+
tree: deserializeMdastFromRaw(buffer, content),
|
|
698
|
+
content,
|
|
699
|
+
frontmatter,
|
|
700
|
+
sourceOffset: readSourceOriginFromEnvelope(buffer, envelope, MDAST_SECTION_SOURCE_ORIGIN)
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
function deserializePreparedSource(buffer) {
|
|
704
|
+
const envelope = parseTransferEnvelope(buffer);
|
|
705
|
+
if (!envelope || envelope.kind !== 3) throw new Error("[ox-content] Prepared source buffer is not a transfer envelope.");
|
|
706
|
+
return {
|
|
707
|
+
content: decodeTransferSectionUtf8(buffer, envelope, PREPARED_SOURCE_SECTION_CONTENT, "[ox-content] prepared source transfer is missing the content section."),
|
|
708
|
+
frontmatter: parseFrontmatterJson(decodeTransferSectionUtf8(buffer, envelope, PREPARED_SOURCE_SECTION_FRONTMATTER, "[ox-content] prepared source transfer is missing the frontmatter section.")),
|
|
709
|
+
sourceOffset: readSourceOriginFromEnvelope(buffer, envelope, PREPARED_SOURCE_SECTION_SOURCE_ORIGIN)
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
async function transformWithMarkdownIt(fullSource, filePath, options) {
|
|
713
|
+
const { content: markdownContent, frontmatter, sourceOffset } = prepareSourceWithRust(loadNapiBindings(), fullSource, options);
|
|
714
|
+
const markdownIt = createMarkdownItRenderer(options);
|
|
715
|
+
applyMarkdownItPlugins(markdownIt, options.plugin.markdownIt);
|
|
716
|
+
const env = createMarkdownItEnv(filePath, fullSource, markdownContent, frontmatter, sourceOffset);
|
|
717
|
+
const tokens = markdownIt.parse(markdownContent, env);
|
|
718
|
+
const html = markdownIt.renderer.render(tokens, markdownIt.options, env);
|
|
719
|
+
const bridgeData = {
|
|
720
|
+
html,
|
|
721
|
+
tokens,
|
|
722
|
+
env
|
|
723
|
+
};
|
|
724
|
+
if (hasMdastOrRemarkPlugins(options)) return transformMarkdownItWithUnified(bridgeData, fullSource, markdownContent, filePath, frontmatter, options);
|
|
725
|
+
if (options.plugin.rehype.length > 0) return {
|
|
726
|
+
html: await transformHtmlWithRehype(html, filePath, fullSource, markdownContent, frontmatter, options, "markdown-it", bridgeData),
|
|
727
|
+
frontmatter,
|
|
728
|
+
toc: options.toc ? extractTocFromMarkdownItTokens(tokens, options.tocMaxDepth) : []
|
|
729
|
+
};
|
|
730
|
+
return {
|
|
44
731
|
html,
|
|
45
732
|
frontmatter,
|
|
733
|
+
toc: options.toc ? extractTocFromMarkdownItTokens(tokens, options.tocMaxDepth) : []
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
async function transformMarkdownItWithUnified(markdownIt, fullSource, markdownContent, filePath, frontmatter, options) {
|
|
737
|
+
const mdastContext = createMdastPluginContext(filePath, fullSource, frontmatter, options, markdownIt.env.oxContent.sourceOffset);
|
|
738
|
+
const { plugins: remarkPlugins, options: remarkRehypeOptions } = extractUnifiedPluginWithOptions(options.plugin.remark, remarkRehype);
|
|
739
|
+
const { plugins: rehypePlugins, options: rehypeStringifyOptions } = extractUnifiedPluginWithOptions(options.plugin.rehype, rehypeStringify);
|
|
740
|
+
const processor = unified();
|
|
741
|
+
processor.use(rehypeParse, { fragment: true });
|
|
742
|
+
processor.use(rehypeRemark);
|
|
743
|
+
applyUnifiedPlugins(processor, options.plugin.mdast.map((plugin) => isOxContentMdastPlugin(plugin) ? toUnifiedMdastPlugin(plugin, mdastContext) : plugin));
|
|
744
|
+
applyUnifiedPlugins(processor, remarkPlugins);
|
|
745
|
+
let toc = [];
|
|
746
|
+
processor.use(() => {
|
|
747
|
+
return (tree) => {
|
|
748
|
+
if (options.toc) toc = extractTocFromMdast(tree, options.tocMaxDepth);
|
|
749
|
+
return tree;
|
|
750
|
+
};
|
|
751
|
+
});
|
|
752
|
+
const frozenMdastProcessor = processor.freeze();
|
|
753
|
+
if (resolveUnifiedCompilerStrategy(frozenMdastProcessor) === "custom") {
|
|
754
|
+
const { processor: compiledProcessor, input } = processorFromMarkdownItProcessor(frozenMdastProcessor(), markdownIt, filePath, fullSource, markdownContent, frontmatter, "markdown-it");
|
|
755
|
+
return {
|
|
756
|
+
html: await processUnifiedFile(compiledProcessor, input),
|
|
757
|
+
frontmatter,
|
|
758
|
+
toc
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
const hastProcessor = frozenMdastProcessor();
|
|
762
|
+
hastProcessor.use(remarkRehype, {
|
|
763
|
+
allowDangerousHtml: true,
|
|
764
|
+
...remarkRehypeOptions
|
|
765
|
+
});
|
|
766
|
+
applyUnifiedPlugins(hastProcessor, rehypePlugins);
|
|
767
|
+
const frozenHastProcessor = hastProcessor.freeze();
|
|
768
|
+
const finalProcessor = frozenHastProcessor();
|
|
769
|
+
if (resolveUnifiedCompilerStrategy(frozenHastProcessor) === "default") finalProcessor.use(rehypeStringify, {
|
|
770
|
+
allowDangerousHtml: true,
|
|
771
|
+
...rehypeStringifyOptions
|
|
772
|
+
});
|
|
773
|
+
const { processor: compiledProcessor, input } = processorFromMarkdownItProcessor(finalProcessor, markdownIt, filePath, fullSource, markdownContent, frontmatter, "markdown-it");
|
|
774
|
+
return {
|
|
775
|
+
html: await processUnifiedFile(compiledProcessor, input),
|
|
776
|
+
frontmatter,
|
|
777
|
+
toc
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
async function transformWithUnified(fullSource, filePath, options) {
|
|
781
|
+
const { plugins: remarkPlugins, options: remarkRehypeOptions } = extractUnifiedPluginWithOptions(options.plugin.remark, remarkRehype);
|
|
782
|
+
const { plugins: rehypePlugins, options: rehypeStringifyOptions } = extractUnifiedPluginWithOptions(options.plugin.rehype, rehypeStringify);
|
|
783
|
+
const parserStrategy = detectUnifiedParserStrategy(fullSource, filePath, options, remarkPlugins);
|
|
784
|
+
const nativePayload = parserStrategy === "native" ? transformWithNativeMdastPipeline(loadNapiBindings(), fullSource, options) : null;
|
|
785
|
+
const preparedInput = parserStrategy === "native" ? null : prepareSourceWithRust(loadNapiBindings(), fullSource, options);
|
|
786
|
+
const markdownContent = nativePayload?.content ?? preparedInput?.content ?? fullSource;
|
|
787
|
+
const frontmatter = nativePayload?.frontmatter ?? preparedInput?.frontmatter ?? {};
|
|
788
|
+
const sourceOffset = nativePayload?.sourceOffset ?? preparedInput?.sourceOffset;
|
|
789
|
+
const mdastContext = createMdastPluginContext(filePath, fullSource, frontmatter, options, sourceOffset);
|
|
790
|
+
const processor = unified();
|
|
791
|
+
applyUnifiedPlugins(processor, options.plugin.mdast.map((plugin) => isOxContentMdastPlugin(plugin) ? toUnifiedMdastPlugin(plugin, mdastContext) : plugin));
|
|
792
|
+
applyUnifiedPlugins(processor, remarkPlugins);
|
|
793
|
+
installUnifiedParser(processor, parserStrategy, options, nativePayload?.tree);
|
|
794
|
+
let toc = [];
|
|
795
|
+
processor.use(() => {
|
|
796
|
+
return (tree) => {
|
|
797
|
+
if (options.toc) toc = extractTocFromMdast(tree, options.tocMaxDepth);
|
|
798
|
+
return tree;
|
|
799
|
+
};
|
|
800
|
+
});
|
|
801
|
+
const frozenMdastProcessor = processor.freeze();
|
|
802
|
+
if (resolveUnifiedCompilerStrategy(frozenMdastProcessor) === "custom") return {
|
|
803
|
+
html: await processUnifiedFile(frozenMdastProcessor(), createUnifiedFileInput(parserStrategy, fullSource, markdownContent, filePath, frontmatter, sourceOffset)),
|
|
804
|
+
frontmatter,
|
|
805
|
+
toc
|
|
806
|
+
};
|
|
807
|
+
const hastProcessor = frozenMdastProcessor();
|
|
808
|
+
hastProcessor.use(remarkRehype, {
|
|
809
|
+
allowDangerousHtml: true,
|
|
810
|
+
...remarkRehypeOptions
|
|
811
|
+
});
|
|
812
|
+
applyUnifiedPlugins(hastProcessor, rehypePlugins);
|
|
813
|
+
const frozenHastProcessor = hastProcessor.freeze();
|
|
814
|
+
const finalProcessor = frozenHastProcessor();
|
|
815
|
+
if (resolveUnifiedCompilerStrategy(frozenHastProcessor) === "default") finalProcessor.use(rehypeStringify, {
|
|
816
|
+
allowDangerousHtml: true,
|
|
817
|
+
...rehypeStringifyOptions
|
|
818
|
+
});
|
|
819
|
+
return {
|
|
820
|
+
html: await processUnifiedFile(finalProcessor, createUnifiedFileInput(parserStrategy, fullSource, markdownContent, filePath, frontmatter, sourceOffset)),
|
|
821
|
+
frontmatter,
|
|
46
822
|
toc
|
|
47
823
|
};
|
|
48
824
|
}
|
|
@@ -66,6 +842,285 @@ function buildTocTree(entries) {
|
|
|
66
842
|
}
|
|
67
843
|
return result;
|
|
68
844
|
}
|
|
845
|
+
function applyUnifiedPlugins(processor, plugins) {
|
|
846
|
+
for (const plugin of plugins) {
|
|
847
|
+
if (Array.isArray(plugin)) {
|
|
848
|
+
const [attacher, ...pluginOptions] = plugin;
|
|
849
|
+
if (pluginOptions.length === 0) processor.use(attacher);
|
|
850
|
+
else if (pluginOptions.length === 1) processor.use(attacher, pluginOptions[0]);
|
|
851
|
+
else processor.use(attacher, pluginOptions);
|
|
852
|
+
continue;
|
|
853
|
+
}
|
|
854
|
+
processor.use(plugin);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
function applyMarkdownItPlugins(markdownIt, plugins) {
|
|
858
|
+
for (const plugin of plugins) {
|
|
859
|
+
if (Array.isArray(plugin)) {
|
|
860
|
+
const [fn, ...pluginOptions] = plugin;
|
|
861
|
+
markdownIt.use(fn, ...pluginOptions);
|
|
862
|
+
continue;
|
|
863
|
+
}
|
|
864
|
+
markdownIt.use(plugin);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
function isOxContentMdastPlugin(plugin) {
|
|
868
|
+
return Boolean(plugin) && typeof plugin === "object" && "transform" in plugin;
|
|
869
|
+
}
|
|
870
|
+
function installUnifiedParser(processor, strategy, options, nativeTree) {
|
|
871
|
+
if (strategy === "custom") return;
|
|
872
|
+
if (strategy === "remark") {
|
|
873
|
+
processor.use(remarkParse);
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
if (nativeTree) {
|
|
877
|
+
processor.use(function usePreparsedMdast() {
|
|
878
|
+
this.parser = () => nativeTree;
|
|
879
|
+
});
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
processor.use(oxContentMdast, {
|
|
883
|
+
gfm: options.gfm,
|
|
884
|
+
footnotes: options.footnotes,
|
|
885
|
+
taskLists: options.taskLists,
|
|
886
|
+
tables: options.tables,
|
|
887
|
+
strikethrough: options.strikethrough,
|
|
888
|
+
autolinks: options.gfm
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
function resolveUnifiedParserStrategy(processor) {
|
|
892
|
+
if (hasUnifiedCustomParser(processor)) return "custom";
|
|
893
|
+
if (hasRemarkSyntaxExtensions(processor)) return "remark";
|
|
894
|
+
return "native";
|
|
895
|
+
}
|
|
896
|
+
function resolveUnifiedCompilerStrategy(processor) {
|
|
897
|
+
return hasUnifiedCustomCompiler(processor) ? "custom" : "default";
|
|
898
|
+
}
|
|
899
|
+
function detectUnifiedParserStrategy(fullSource, filePath, options, remarkPlugins) {
|
|
900
|
+
const stagedProcessor = unified();
|
|
901
|
+
const detectionContext = createMdastPluginContext(filePath, fullSource, {}, options);
|
|
902
|
+
applyUnifiedPlugins(stagedProcessor, options.plugin.mdast.map((plugin) => isOxContentMdastPlugin(plugin) ? toUnifiedMdastPlugin(plugin, detectionContext) : plugin));
|
|
903
|
+
applyUnifiedPlugins(stagedProcessor, remarkPlugins);
|
|
904
|
+
return resolveUnifiedParserStrategy(stagedProcessor.freeze());
|
|
905
|
+
}
|
|
906
|
+
function selectUnifiedInput(strategy, fullSource, markdownContent) {
|
|
907
|
+
return strategy === "native" ? markdownContent : fullSource;
|
|
908
|
+
}
|
|
909
|
+
function createUnifiedFileInput(strategy, fullSource, markdownContent, filePath, frontmatter, sourceOffset) {
|
|
910
|
+
return {
|
|
911
|
+
path: filePath,
|
|
912
|
+
value: strategy === "markdown-it" ? markdownContent : selectUnifiedInput(strategy, fullSource, markdownContent),
|
|
913
|
+
data: createUnifiedFileData(strategy, fullSource, markdownContent, frontmatter, { sourceOffset })
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
function createUnifiedFileData(parser, fullSource, markdownContent, frontmatter, extra) {
|
|
917
|
+
return {
|
|
918
|
+
frontmatter,
|
|
919
|
+
matter: frontmatter,
|
|
920
|
+
...extra?.sourceOffset ? { sourceOffset: extra.sourceOffset } : {},
|
|
921
|
+
oxContent: {
|
|
922
|
+
parser,
|
|
923
|
+
frontmatter,
|
|
924
|
+
source: fullSource,
|
|
925
|
+
content: markdownContent,
|
|
926
|
+
...extra?.sourceOffset ? { sourceOffset: extra.sourceOffset } : {},
|
|
927
|
+
...extra
|
|
928
|
+
}
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
function hasUnifiedCustomParser(processor) {
|
|
932
|
+
const candidate = processor;
|
|
933
|
+
return typeof candidate.Parser === "function" || typeof candidate.parser === "function";
|
|
934
|
+
}
|
|
935
|
+
function hasUnifiedCustomCompiler(processor) {
|
|
936
|
+
const candidate = processor;
|
|
937
|
+
return typeof candidate.Compiler === "function" || typeof candidate.compiler === "function";
|
|
938
|
+
}
|
|
939
|
+
async function processUnifiedFile(processor, input) {
|
|
940
|
+
const file = await processor.process(input);
|
|
941
|
+
return String(file);
|
|
942
|
+
}
|
|
943
|
+
function processorFromMarkdownItProcessor(processor, markdownIt, filePath, fullSource, markdownContent, frontmatter, parser) {
|
|
944
|
+
return {
|
|
945
|
+
processor,
|
|
946
|
+
input: {
|
|
947
|
+
path: filePath,
|
|
948
|
+
value: markdownIt.html,
|
|
949
|
+
data: createUnifiedFileData(parser, fullSource, markdownContent, frontmatter, {
|
|
950
|
+
html: markdownIt.html,
|
|
951
|
+
markdownIt: {
|
|
952
|
+
env: markdownIt.env,
|
|
953
|
+
tokens: markdownIt.tokens
|
|
954
|
+
}
|
|
955
|
+
})
|
|
956
|
+
}
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
function createMarkdownItRenderer(options) {
|
|
960
|
+
const markdownIt = new MarkdownIt({
|
|
961
|
+
html: true,
|
|
962
|
+
linkify: options.gfm
|
|
963
|
+
});
|
|
964
|
+
if (!options.tables) markdownIt.disable("table");
|
|
965
|
+
if (!options.strikethrough) markdownIt.disable("strikethrough");
|
|
966
|
+
return markdownIt;
|
|
967
|
+
}
|
|
968
|
+
function createMarkdownItEnv(filePath, fullSource, markdownContent, frontmatter, sourceOffset) {
|
|
969
|
+
return {
|
|
970
|
+
filePath,
|
|
971
|
+
frontmatter,
|
|
972
|
+
matter: frontmatter,
|
|
973
|
+
...sourceOffset ? { sourceOffset } : {},
|
|
974
|
+
oxContent: {
|
|
975
|
+
parser: "markdown-it",
|
|
976
|
+
frontmatter,
|
|
977
|
+
source: fullSource,
|
|
978
|
+
content: markdownContent,
|
|
979
|
+
...sourceOffset ? { sourceOffset } : {}
|
|
980
|
+
}
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
function extractTocFromMarkdownItTokens(tokens, maxDepth) {
|
|
984
|
+
const headings = [];
|
|
985
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
986
|
+
const openToken = tokens[index];
|
|
987
|
+
if (openToken?.type !== "heading_open") continue;
|
|
988
|
+
const depth = Number.parseInt(openToken.tag?.replace(/^h/i, "") ?? "", 10);
|
|
989
|
+
if (!Number.isFinite(depth) || depth < 1 || depth > maxDepth) continue;
|
|
990
|
+
const inlineToken = tokens[index + 1];
|
|
991
|
+
if (inlineToken?.type !== "inline") continue;
|
|
992
|
+
const text = collectMarkdownItText(inlineToken);
|
|
993
|
+
if (text.length === 0) continue;
|
|
994
|
+
headings.push({
|
|
995
|
+
depth,
|
|
996
|
+
text,
|
|
997
|
+
slug: getMarkdownItTokenAttr(openToken, "id") ?? slugify(text),
|
|
998
|
+
children: []
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
return buildTocTree(headings);
|
|
1002
|
+
}
|
|
1003
|
+
function collectMarkdownItText(token) {
|
|
1004
|
+
if (Array.isArray(token.children) && token.children.length > 0) return token.children.map((child) => collectMarkdownItText(child)).join("");
|
|
1005
|
+
return typeof token.content === "string" ? token.content : "";
|
|
1006
|
+
}
|
|
1007
|
+
function getMarkdownItTokenAttr(token, name) {
|
|
1008
|
+
if (!Array.isArray(token.attrs)) return;
|
|
1009
|
+
return token.attrs.find(([key]) => key === name)?.[1];
|
|
1010
|
+
}
|
|
1011
|
+
function readSourceOriginFromEnvelope(buffer, envelope, sectionId) {
|
|
1012
|
+
const section = envelope.sections.get(sectionId);
|
|
1013
|
+
if (!section) return;
|
|
1014
|
+
if (section.len !== 16) throw new Error("[ox-content] source origin transfer section is misaligned.");
|
|
1015
|
+
const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
1016
|
+
return {
|
|
1017
|
+
byteOffset: view.getUint32(section.offset, true),
|
|
1018
|
+
offset: view.getUint32(section.offset + 4, true),
|
|
1019
|
+
line: view.getUint32(section.offset + 8, true),
|
|
1020
|
+
column: view.getUint32(section.offset + 12, true)
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
function slugify(text) {
|
|
1024
|
+
return text.toLowerCase().replace(/[^\p{L}\p{N}\s-]+/gu, " ").trim().split(/\s+/).filter(Boolean).join("-");
|
|
1025
|
+
}
|
|
1026
|
+
function requireNapiMethod(method, name) {
|
|
1027
|
+
if (typeof method === "function") return method;
|
|
1028
|
+
throw new Error(`[ox-content] @ox-content/napi is too old for unified interop: missing ${name}(). Rebuild the NAPI module with \`vp run build:napi\`.`);
|
|
1029
|
+
}
|
|
1030
|
+
async function transformHtmlWithRehype(html, filePath, fullSource, markdownContent, frontmatter, options, parser, extra) {
|
|
1031
|
+
const { plugins: rehypePlugins, options: rehypeStringifyOptions } = extractUnifiedPluginWithOptions(options.plugin.rehype, rehypeStringify);
|
|
1032
|
+
const stagedProcessor = unified();
|
|
1033
|
+
stagedProcessor.use(rehypeParse, { fragment: true });
|
|
1034
|
+
applyUnifiedPlugins(stagedProcessor, rehypePlugins);
|
|
1035
|
+
const frozenProcessor = stagedProcessor.freeze();
|
|
1036
|
+
const processor = frozenProcessor();
|
|
1037
|
+
if (resolveUnifiedCompilerStrategy(frozenProcessor) === "default") processor.use(rehypeStringify, {
|
|
1038
|
+
allowDangerousHtml: true,
|
|
1039
|
+
...rehypeStringifyOptions
|
|
1040
|
+
});
|
|
1041
|
+
return processUnifiedFile(processor, {
|
|
1042
|
+
path: filePath,
|
|
1043
|
+
value: html,
|
|
1044
|
+
data: createUnifiedFileData(parser, fullSource, markdownContent, frontmatter, {
|
|
1045
|
+
html,
|
|
1046
|
+
...extra
|
|
1047
|
+
})
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
function hasRemarkSyntaxExtensions(processor) {
|
|
1051
|
+
const data = processor.data();
|
|
1052
|
+
return hasOwnDataField(data, "micromarkExtensions") || hasOwnDataField(data, "fromMarkdownExtensions");
|
|
1053
|
+
}
|
|
1054
|
+
function hasOwnDataField(data, key) {
|
|
1055
|
+
return Object.prototype.hasOwnProperty.call(data, key);
|
|
1056
|
+
}
|
|
1057
|
+
function extractUnifiedPluginWithOptions(plugins, targetPlugin) {
|
|
1058
|
+
let extractedOptions;
|
|
1059
|
+
const nextPlugins = [];
|
|
1060
|
+
for (const plugin of plugins) {
|
|
1061
|
+
const result = filterUnifiedPlugin(plugin, targetPlugin);
|
|
1062
|
+
extractedOptions = mergeUnifiedPluginOptions(extractedOptions, result.options);
|
|
1063
|
+
if (result.keep) nextPlugins.push(result.plugin);
|
|
1064
|
+
}
|
|
1065
|
+
return {
|
|
1066
|
+
plugins: nextPlugins,
|
|
1067
|
+
options: extractedOptions
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
function filterUnifiedPlugin(plugin, targetPlugin) {
|
|
1071
|
+
if (isUnifiedPreset(plugin)) {
|
|
1072
|
+
const { plugins: _plugins, ...rest } = plugin;
|
|
1073
|
+
let extractedOptions;
|
|
1074
|
+
const nextPlugins = [];
|
|
1075
|
+
for (const nestedPlugin of plugin.plugins ?? []) {
|
|
1076
|
+
const result = filterUnifiedPlugin(nestedPlugin, targetPlugin);
|
|
1077
|
+
extractedOptions = mergeUnifiedPluginOptions(extractedOptions, result.options);
|
|
1078
|
+
if (result.keep) nextPlugins.push(result.plugin);
|
|
1079
|
+
}
|
|
1080
|
+
if (nextPlugins.length === 0 && plugin.settings === void 0) return {
|
|
1081
|
+
keep: false,
|
|
1082
|
+
plugin,
|
|
1083
|
+
options: extractedOptions
|
|
1084
|
+
};
|
|
1085
|
+
return {
|
|
1086
|
+
keep: true,
|
|
1087
|
+
plugin: {
|
|
1088
|
+
...rest,
|
|
1089
|
+
...nextPlugins.length > 0 ? { plugins: nextPlugins } : {}
|
|
1090
|
+
},
|
|
1091
|
+
options: extractedOptions
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
const { attacher, options } = splitUnifiedPlugin(plugin);
|
|
1095
|
+
if (attacher === targetPlugin) return {
|
|
1096
|
+
keep: false,
|
|
1097
|
+
plugin,
|
|
1098
|
+
options
|
|
1099
|
+
};
|
|
1100
|
+
return {
|
|
1101
|
+
keep: true,
|
|
1102
|
+
plugin
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
function mergeUnifiedPluginOptions(left, right) {
|
|
1106
|
+
if (left === void 0) return right;
|
|
1107
|
+
if (right === void 0) return left;
|
|
1108
|
+
return {
|
|
1109
|
+
...left,
|
|
1110
|
+
...right
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
function isUnifiedPreset(plugin) {
|
|
1114
|
+
return Boolean(plugin) && typeof plugin === "object" && !Array.isArray(plugin) && ("plugins" in plugin || "settings" in plugin);
|
|
1115
|
+
}
|
|
1116
|
+
function splitUnifiedPlugin(plugin) {
|
|
1117
|
+
if (!Array.isArray(plugin)) return { attacher: plugin };
|
|
1118
|
+
const [attacher, firstOption] = plugin;
|
|
1119
|
+
return {
|
|
1120
|
+
attacher,
|
|
1121
|
+
options: firstOption && typeof firstOption === "object" ? firstOption : void 0
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
69
1124
|
/**
|
|
70
1125
|
* Generates the JavaScript module code.
|
|
71
1126
|
*/
|
|
@@ -180,6 +1235,7 @@ function resolveOptions(options) {
|
|
|
180
1235
|
plugin: {
|
|
181
1236
|
oxContent: options.plugin?.oxContent ?? [],
|
|
182
1237
|
markdownIt: options.plugin?.markdownIt ?? [],
|
|
1238
|
+
mdast: options.plugin?.mdast ?? [],
|
|
183
1239
|
remark: options.plugin?.remark ?? [],
|
|
184
1240
|
rehype: options.plugin?.rehype ?? []
|
|
185
1241
|
},
|
|
@@ -254,4 +1310,4 @@ const unpluginFactory = (rawOptions = {}) => {
|
|
|
254
1310
|
*/
|
|
255
1311
|
const unplugin = /* @__PURE__ */ createUnplugin(unpluginFactory);
|
|
256
1312
|
//#endregion
|
|
257
|
-
export { transformMarkdown as n, unplugin as t };
|
|
1313
|
+
export { extractTocFromMdast as a, toUnifiedMdastPlugin as c, defineMdastPlugin as i, transformMarkdown as n, oxContentMdast as o, createMdastPluginContext as r, parseMarkdownToMdast as s, unplugin as t };
|