satteri 0.10.5 → 0.11.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/dist/arena-reader.d.ts +13 -0
- package/dist/arena-reader.js +104 -0
- package/dist/binding.browser.d.ts +1 -1
- package/dist/binding.browser.js +1 -1
- package/dist/binding.d.ts +1 -1
- package/dist/binding.js +1 -1
- package/dist/byte-writer.d.ts +0 -18
- package/dist/byte-writer.js +6 -30
- package/dist/child-stub.d.ts +0 -17
- package/dist/child-stub.js +6 -22
- package/dist/command-buffer.d.ts +5 -26
- package/dist/command-buffer.js +28 -39
- package/dist/compile-options.d.ts +11 -0
- package/dist/compile-options.js +101 -0
- package/dist/compile-types.d.ts +342 -0
- package/dist/compile-types.js +1 -0
- package/dist/compile.d.ts +22 -328
- package/dist/compile.js +133 -383
- package/dist/directive-types.d.ts +1 -0
- package/dist/directive-types.js +1 -6
- package/dist/generated/arena-layout.d.ts +6 -4
- package/dist/generated/arena-layout.js +6 -7
- package/dist/generated/fused-wire.d.ts +3 -0
- package/dist/generated/fused-wire.js +229 -0
- package/dist/generated/parse-options.d.ts +1 -0
- package/dist/generated/parse-options.js +1 -5
- package/dist/generated/wire-constants.d.ts +4 -0
- package/dist/generated/wire-constants.js +34 -42
- package/dist/handles.d.ts +0 -7
- package/dist/hast/child-stub.d.ts +1 -8
- package/dist/hast/child-stub.js +14 -13
- package/dist/hast/element-props.d.ts +13 -0
- package/dist/hast/element-props.js +52 -6
- package/dist/hast/generated/node-types.d.ts +1 -9
- package/dist/hast/generated/node-types.js +1 -9
- package/dist/hast/generated/walk-decode.d.ts +10 -0
- package/dist/hast/generated/walk-decode.js +61 -0
- package/dist/hast/hast-materializer.d.ts +3 -10
- package/dist/hast/hast-materializer.js +73 -47
- package/dist/hast/hast-reader.d.ts +9 -38
- package/dist/hast/hast-reader.js +82 -271
- package/dist/hast/hast-visitor.d.ts +29 -16
- package/dist/hast/hast-visitor.js +200 -508
- package/dist/hast/hast-walk.d.ts +18 -0
- package/dist/hast/hast-walk.js +264 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.js +4 -12
- package/dist/lazy-child-resolver.d.ts +1 -21
- package/dist/lazy-child-resolver.js +11 -32
- package/dist/materializer-cache.d.ts +5 -29
- package/dist/materializer-cache.js +29 -101
- package/dist/mdast/child-stub.d.ts +1 -12
- package/dist/mdast/child-stub.js +18 -28
- package/dist/mdast/column-align.d.ts +1 -1
- package/dist/mdast/column-align.js +0 -2
- package/dist/mdast/generated/layout.d.ts +0 -16
- package/dist/mdast/generated/layout.js +6 -47
- package/dist/mdast/generated/node-types.d.ts +1 -9
- package/dist/mdast/generated/node-types.js +3 -9
- package/dist/mdast/mdast-materializer.d.ts +3 -11
- package/dist/mdast/mdast-materializer.js +91 -63
- package/dist/mdast/mdast-reader.d.ts +15 -63
- package/dist/mdast/mdast-reader.js +132 -269
- package/dist/mdast/mdast-visitor.d.ts +23 -15
- package/dist/mdast/mdast-visitor.js +189 -294
- package/dist/mdast/mdast-walk.d.ts +13 -0
- package/dist/mdast/mdast-walk.js +144 -0
- package/dist/mdx-attr.js +0 -3
- package/dist/mdx-types.js +1 -6
- package/dist/op-stream.d.ts +4 -15
- package/dist/op-stream.js +31 -20
- package/dist/phantom.d.ts +0 -1
- package/dist/phantom.js +1 -9
- package/dist/plugin-pipeline.d.ts +32 -0
- package/dist/plugin-pipeline.js +177 -0
- package/dist/plugin.d.ts +14 -8
- package/dist/plugin.js +5 -10
- package/dist/types.d.ts +0 -1
- package/dist/visitor-node.d.ts +12 -0
- package/dist/visitor-node.js +23 -0
- package/dist/visitor-shared.d.ts +26 -30
- package/dist/visitor-shared.js +34 -36
- package/dist/wire-read.d.ts +0 -22
- package/dist/wire-read.js +3 -24
- package/index.d.ts +19 -10
- package/index.js +56 -53
- package/package.json +6 -12
- package/dist/string-pool.d.ts +0 -13
- package/dist/string-pool.js +0 -91
package/dist/compile.js
CHANGED
|
@@ -1,310 +1,36 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
import { MdastReader } from "./mdast/mdast-reader.js";
|
|
7
|
-
import { materializeMdastTree } from "./mdast/mdast-materializer.js";
|
|
8
|
-
import { markHandleMutated } from "./lazy-child-resolver.js";
|
|
9
|
-
import { HastReader } from "./hast/hast-reader.js";
|
|
1
|
+
import { encodeHastDocument } from "./hast/hast-visitor.js";
|
|
2
|
+
import { featuresToNative, mdxOptionsToNative } from "./compile-options.js";
|
|
3
|
+
import { createHastHandleFromMdast, EMPTY_COMMAND_BUFFER, releaseHandle, runHastPluginsCollectLast, runMdastPluginsOnHandle, warnIfDroppedTransforms, } from "./plugin-pipeline.js";
|
|
4
|
+
export { DEFAULT_PARSE_OPTIONS, featuresToNative } from "./compile-options.js";
|
|
5
|
+
import { applyCommandsAndCompileHandle, applyCommandsAndRenderHandle, applyCommandsToHandle, applyMdastCommandsAndConvertAndCompile, applyMdastCommandsAndConvertAndRender, compileHandle, createHastHandleFromHtml, createMdastHandle, createMdxMdastHandle, markdownToHtmlFast, markdownToJsFast, mdxToJsFast, parseHastWire, parseMdastWire, renderHandle, renderHastOpstream, serializeHandle, } from "#binding";
|
|
10
6
|
import { materializeHastTree } from "./hast/hast-materializer.js";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
function featuresToParseOptions(features) {
|
|
16
|
-
let options = 0;
|
|
17
|
-
const { gfm } = features;
|
|
18
|
-
if (typeof gfm === "object") {
|
|
19
|
-
options |= GFM_PARSE_OPTIONS;
|
|
20
|
-
if (gfm.footnotes !== false)
|
|
21
|
-
options |= ENABLE_FOOTNOTES;
|
|
22
|
-
}
|
|
23
|
-
else if (gfm ?? true) {
|
|
24
|
-
options |= GFM_PARSE_OPTIONS | ENABLE_FOOTNOTES;
|
|
25
|
-
}
|
|
26
|
-
if (features.frontmatter ?? true)
|
|
27
|
-
options |= FRONTMATTER_PARSE_OPTIONS;
|
|
28
|
-
// Opting out of single-dollar math sets the multi-dollar sub-flag so the parser skips lone `$`.
|
|
29
|
-
const { math } = features;
|
|
30
|
-
if (typeof math === "object") {
|
|
31
|
-
options |= math.singleDollarTextMath === false ? ENABLE_MATH_MULTI_DOLLAR : ENABLE_MATH;
|
|
32
|
-
}
|
|
33
|
-
else if (math) {
|
|
34
|
-
options |= ENABLE_MATH;
|
|
35
|
-
}
|
|
36
|
-
if (features.headingAttributes)
|
|
37
|
-
options |= ENABLE_HEADING_ATTRIBUTES;
|
|
38
|
-
if (features.directive)
|
|
39
|
-
options |= ENABLE_DIRECTIVE;
|
|
40
|
-
if (features.superscript)
|
|
41
|
-
options |= ENABLE_SUPERSCRIPT;
|
|
42
|
-
if (features.subscript)
|
|
43
|
-
options |= ENABLE_SUBSCRIPT;
|
|
44
|
-
if (features.wikilinks)
|
|
45
|
-
options |= ENABLE_WIKILINKS;
|
|
46
|
-
if (features.definitionList)
|
|
47
|
-
options |= ENABLE_DEFINITION_LIST;
|
|
48
|
-
const smartPunctuation = features.smartPunctuation;
|
|
49
|
-
if (typeof smartPunctuation === "object") {
|
|
50
|
-
if (smartPunctuation.quotes ?? true)
|
|
51
|
-
options |= ENABLE_SMART_QUOTES;
|
|
52
|
-
if (smartPunctuation.dashes ?? true)
|
|
53
|
-
options |= ENABLE_SMART_DASHES;
|
|
54
|
-
if (smartPunctuation.ellipses ?? true)
|
|
55
|
-
options |= ENABLE_SMART_ELLIPSES;
|
|
56
|
-
}
|
|
57
|
-
else if (smartPunctuation) {
|
|
58
|
-
options |= ENABLE_SMART_PUNCTUATION;
|
|
59
|
-
}
|
|
60
|
-
return options;
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Split the user-facing `Features` into the packed parser options plus the
|
|
64
|
-
* conversion-side `JsConvertOptions` carrying the footnote i18n strings and the
|
|
65
|
-
* raw-HTML reparse. The public API only exposes `features`; both halves are
|
|
66
|
-
* routed to napi internally.
|
|
67
|
-
*/
|
|
68
|
-
export function featuresToNative(features) {
|
|
69
|
-
if (!features)
|
|
70
|
-
return { parseOptions: DEFAULT_PARSE_OPTIONS, convertOptions: undefined };
|
|
71
|
-
let convertOptions;
|
|
72
|
-
const { gfm } = features;
|
|
73
|
-
if (typeof gfm === "object" && typeof gfm.footnotes === "object") {
|
|
74
|
-
const { label, backContent, backLabel, clobberPrefix } = gfm.footnotes;
|
|
75
|
-
convertOptions = {};
|
|
76
|
-
if (label !== undefined)
|
|
77
|
-
convertOptions.footnoteLabel = label;
|
|
78
|
-
if (backContent !== undefined)
|
|
79
|
-
convertOptions.footnoteBackContent = backContent;
|
|
80
|
-
if (backLabel !== undefined)
|
|
81
|
-
convertOptions.footnoteBackLabel = backLabel;
|
|
82
|
-
if (clobberPrefix !== undefined)
|
|
83
|
-
convertOptions.clobberPrefix = clobberPrefix;
|
|
84
|
-
}
|
|
85
|
-
if (features.rawHtml !== undefined) {
|
|
86
|
-
// Routed to convert options so every pipeline (fast paths, plugin paths,
|
|
87
|
-
// *ToHast) applies the reparse.
|
|
88
|
-
convertOptions = convertOptions ?? {};
|
|
89
|
-
convertOptions.rawHtml = features.rawHtml;
|
|
90
|
-
}
|
|
91
|
-
return { parseOptions: featuresToParseOptions(features), convertOptions };
|
|
92
|
-
}
|
|
93
|
-
/** Free a handle's arena. A plugin's visitor can hand child stubs to user code;
|
|
94
|
-
* pass `invalidateStubs` so the epoch bump makes any stub retained past this
|
|
95
|
-
* point hit the designed retention error instead of snapshotting the freed arena. */
|
|
96
|
-
function releaseHandle(handle, invalidateStubs) {
|
|
97
|
-
if (invalidateStubs)
|
|
98
|
-
markHandleMutated(handle);
|
|
99
|
-
dropHandle(handle);
|
|
100
|
-
}
|
|
101
|
-
function warnDroppedTransforms(plugin, dropped, kind) {
|
|
102
|
-
const name = plugin.name ?? "<anonymous>";
|
|
103
|
-
const noun = dropped === 1 ? "transform" : "transforms";
|
|
104
|
-
console.warn(`satteri: plugin "${name}" queued ${dropped} ${kind} ${noun} on node(s) that a plugin had ` +
|
|
105
|
-
`already removed or replaced; ${dropped === 1 ? "it was" : "they were"} dropped.`);
|
|
106
|
-
}
|
|
107
|
-
function settle(value, fn) {
|
|
108
|
-
if (value instanceof Promise)
|
|
109
|
-
return value.then(fn);
|
|
110
|
-
fn(value);
|
|
111
|
-
return undefined;
|
|
112
|
-
}
|
|
113
|
-
/** Stays synchronous until a step actually returns a promise. */
|
|
114
|
-
function sequence(steps) {
|
|
115
|
-
let pending;
|
|
116
|
-
for (const step of steps) {
|
|
117
|
-
if (step === undefined)
|
|
118
|
-
continue;
|
|
119
|
-
pending = pending === undefined ? step() : pending.then(step);
|
|
120
|
-
}
|
|
121
|
-
return pending;
|
|
122
|
-
}
|
|
123
|
-
function runMdastPluginsOnHandle(handle, plugins, fileURL, data, sourceFormat, collectLast = false) {
|
|
124
|
-
const out = { handle };
|
|
125
|
-
// A plugin's visitors run once over the tree. A transform that passes a child
|
|
126
|
-
// through (returning it inside the replacement) keeps that child's identity,
|
|
127
|
-
// so a patch the same pass queued on it still applies: nesting composes in
|
|
128
|
-
// one pass. Visitor-built nodes are not re-walked; transform them up front,
|
|
129
|
-
// or hand off to a later plugin that sees the materialized tree.
|
|
130
|
-
const runPlugin = (plugin, isLastPlugin) => {
|
|
131
|
-
const subs = resolveMdastSubscriptions(plugin);
|
|
132
|
-
const apply = (r, isFinalPass) => {
|
|
133
|
-
if (!r.hasMutations)
|
|
134
|
-
return;
|
|
135
|
-
if (collectLast && isLastPlugin && isFinalPass) {
|
|
136
|
-
out.pendingCommands = r.commandBuffer;
|
|
137
|
-
out.lastPlugin = plugin;
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
markHandleMutated(handle);
|
|
141
|
-
const dropped = applyCommandsToMdastHandle(handle, r.commandBuffer);
|
|
142
|
-
if (dropped)
|
|
143
|
-
warnDroppedTransforms(plugin, dropped, "mdast");
|
|
144
|
-
};
|
|
145
|
-
const before = typeof plugin.before === "function" ? plugin.before : undefined;
|
|
146
|
-
const after = typeof plugin.after === "function" ? plugin.after : undefined;
|
|
147
|
-
const hasVisitors = subs.length > 0;
|
|
148
|
-
// Threaded like `data`: one context per pass, since a resolver is epoch-bound.
|
|
149
|
-
const diagnostics = [];
|
|
150
|
-
const runHook = (hook, isFinalPass) => () => settle(visitMdastHook(handle, plugin, hook, () => getHandleSource(handle), fileURL, data, sourceFormat, diagnostics), (r) => apply(r, isFinalPass));
|
|
151
|
-
const runVisitors = (isFinalPass) => () => settle(visitMdastHandle(handle, plugin, subs, () => getHandleSource(handle), fileURL, data, sourceFormat, diagnostics), (r) => apply(r, isFinalPass));
|
|
152
|
-
return sequence([
|
|
153
|
-
before ? runHook(before, !hasVisitors && after === undefined) : undefined,
|
|
154
|
-
hasVisitors ? runVisitors(after === undefined) : undefined,
|
|
155
|
-
after ? runHook(after, true) : undefined,
|
|
156
|
-
]);
|
|
157
|
-
};
|
|
158
|
-
let i = 0;
|
|
159
|
-
const runNext = () => {
|
|
160
|
-
for (;;) {
|
|
161
|
-
const plugin = plugins[i];
|
|
162
|
-
if (plugin === undefined)
|
|
163
|
-
break;
|
|
164
|
-
i++;
|
|
165
|
-
const r = runPlugin(plugin, i === plugins.length);
|
|
166
|
-
if (r instanceof Promise)
|
|
167
|
-
return r.then(runNext);
|
|
168
|
-
}
|
|
169
|
-
return out;
|
|
170
|
-
};
|
|
171
|
-
return runNext();
|
|
172
|
-
}
|
|
173
|
-
const EMPTY_COMMAND_BUFFER = new Uint8Array(0);
|
|
174
|
-
const NO_HAST_COMMANDS = {
|
|
175
|
-
commands: EMPTY_COMMAND_BUFFER,
|
|
176
|
-
lastPlugin: null,
|
|
177
|
-
};
|
|
178
|
-
/** Run every HAST plugin except the last one normally (each applies its
|
|
179
|
-
* commands inline), then return the last plugin's commands without applying.
|
|
180
|
-
* Lets the caller fuse the final apply with the downstream NAPI step
|
|
181
|
-
* (`render` or `compile`), saving one NAPI roundtrip per compile. */
|
|
182
|
-
function runHastPluginsCollectLast(handle, plugins, source, fileURL, data, sourceFormat) {
|
|
183
|
-
let i = 0;
|
|
184
|
-
const runNext = () => {
|
|
185
|
-
for (;;) {
|
|
186
|
-
const plugin = plugins[i];
|
|
187
|
-
if (plugin === undefined)
|
|
188
|
-
break;
|
|
189
|
-
const isLastPlugin = i === plugins.length - 1;
|
|
190
|
-
i++;
|
|
191
|
-
const subs = resolveSubscriptions(plugin);
|
|
192
|
-
const { before, after } = plugin;
|
|
193
|
-
const passes = [];
|
|
194
|
-
if (typeof before === "function")
|
|
195
|
-
passes.push(before);
|
|
196
|
-
if (subs.length > 0)
|
|
197
|
-
passes.push("visitors");
|
|
198
|
-
if (typeof after === "function")
|
|
199
|
-
passes.push(after);
|
|
200
|
-
const finalPass = passes.pop();
|
|
201
|
-
if (finalPass === undefined)
|
|
202
|
-
continue;
|
|
203
|
-
const warnIfDropped = (dropped) => {
|
|
204
|
-
if (dropped)
|
|
205
|
-
warnDroppedTransforms(plugin, dropped, "hast");
|
|
206
|
-
};
|
|
207
|
-
// Threaded like `data`: one context per pass, since a resolver is epoch-bound.
|
|
208
|
-
const diagnostics = [];
|
|
209
|
-
const runApplied = (pass) => () => settle(pass === "visitors"
|
|
210
|
-
? visitHastHandle(handle, plugin, subs, source, fileURL, data, sourceFormat, diagnostics)
|
|
211
|
-
: visitHastHook(handle, plugin, pass, source, fileURL, data, sourceFormat, diagnostics), warnIfDropped);
|
|
212
|
-
const collectFinal = () => {
|
|
213
|
-
const collected = finalPass === "visitors"
|
|
214
|
-
? visitHastHandleCollect(handle, plugin, subs, source, fileURL, data, sourceFormat, diagnostics)
|
|
215
|
-
: visitHastHookCollect(handle, plugin, finalPass, source, fileURL, data, sourceFormat, diagnostics);
|
|
216
|
-
return collected instanceof Promise
|
|
217
|
-
? collected.then((commands) => ({ commands, lastPlugin: plugin }))
|
|
218
|
-
: { commands: collected, lastPlugin: plugin };
|
|
219
|
-
};
|
|
220
|
-
const head = sequence(passes.map(runApplied));
|
|
221
|
-
if (isLastPlugin) {
|
|
222
|
-
return head instanceof Promise ? head.then(collectFinal) : collectFinal();
|
|
223
|
-
}
|
|
224
|
-
const tail = head instanceof Promise ? head.then(runApplied(finalPass)) : runApplied(finalPass)();
|
|
225
|
-
if (tail instanceof Promise)
|
|
226
|
-
return tail.then(runNext);
|
|
227
|
-
}
|
|
228
|
-
return NO_HAST_COMMANDS;
|
|
229
|
-
};
|
|
230
|
-
return runNext();
|
|
231
|
-
}
|
|
232
|
-
// Public API
|
|
233
|
-
function mdxOptionsToNative(opts) {
|
|
234
|
-
const hasAny = opts.optimizeStatic ||
|
|
235
|
-
opts.jsxImportSource !== undefined ||
|
|
236
|
-
opts.jsx !== undefined ||
|
|
237
|
-
opts.jsxRuntime !== undefined ||
|
|
238
|
-
opts.development !== undefined ||
|
|
239
|
-
opts.providerImportSource !== undefined ||
|
|
240
|
-
opts.pragma !== undefined ||
|
|
241
|
-
opts.pragmaFrag !== undefined ||
|
|
242
|
-
opts.pragmaImportSource !== undefined ||
|
|
243
|
-
opts.outputFormat !== undefined ||
|
|
244
|
-
opts.elementAttributeNameCase !== undefined ||
|
|
245
|
-
opts.stylePropertyNameCase !== undefined;
|
|
246
|
-
if (!hasAny)
|
|
247
|
-
return undefined;
|
|
248
|
-
const result = {};
|
|
249
|
-
if (opts.optimizeStatic)
|
|
250
|
-
result.optimizeStatic = opts.optimizeStatic;
|
|
251
|
-
if (opts.jsxImportSource !== undefined)
|
|
252
|
-
result.jsxImportSource = opts.jsxImportSource;
|
|
253
|
-
if (opts.jsx !== undefined)
|
|
254
|
-
result.jsx = opts.jsx;
|
|
255
|
-
if (opts.jsxRuntime !== undefined)
|
|
256
|
-
result.jsxRuntime = opts.jsxRuntime;
|
|
257
|
-
if (opts.development !== undefined)
|
|
258
|
-
result.development = opts.development;
|
|
259
|
-
if (opts.providerImportSource !== undefined)
|
|
260
|
-
result.providerImportSource = opts.providerImportSource;
|
|
261
|
-
if (opts.pragma !== undefined)
|
|
262
|
-
result.pragma = opts.pragma;
|
|
263
|
-
if (opts.pragmaFrag !== undefined)
|
|
264
|
-
result.pragmaFrag = opts.pragmaFrag;
|
|
265
|
-
if (opts.pragmaImportSource !== undefined)
|
|
266
|
-
result.pragmaImportSource = opts.pragmaImportSource;
|
|
267
|
-
if (opts.outputFormat !== undefined)
|
|
268
|
-
result.outputFormat = opts.outputFormat;
|
|
269
|
-
if (opts.elementAttributeNameCase !== undefined)
|
|
270
|
-
result.elementAttributeNameCase = opts.elementAttributeNameCase;
|
|
271
|
-
if (opts.stylePropertyNameCase !== undefined)
|
|
272
|
-
result.stylePropertyNameCase = opts.stylePropertyNameCase;
|
|
273
|
-
return result;
|
|
274
|
-
}
|
|
7
|
+
import { HastReader } from "./hast/hast-reader.js";
|
|
8
|
+
import { materializeMdastTree } from "./mdast/mdast-materializer.js";
|
|
9
|
+
import { MdastReader } from "./mdast/mdast-reader.js";
|
|
10
|
+
import { normalizePlugins } from "./plugin.js";
|
|
275
11
|
export function markdownToHtml(source, options = {}) {
|
|
276
12
|
const { features, fileURL, data = {} } = options;
|
|
277
|
-
const mdastPlugins = normalizePlugins(options.mdastPlugins ?? [], "mdastPlugins", source, fileURL, "markdown", data);
|
|
278
|
-
const hastPlugins = normalizePlugins(options.hastPlugins ?? [], "hastPlugins", source, fileURL, "markdown", data);
|
|
13
|
+
const mdastPlugins = normalizePlugins(options.mdastPlugins ?? [], "mdastPlugins", source, fileURL, "markdown", data, features);
|
|
14
|
+
const hastPlugins = normalizePlugins(options.hastPlugins ?? [], "hastPlugins", source, fileURL, "markdown", data, features);
|
|
279
15
|
const hastMayHaveStubs = hastPlugins.length > 0;
|
|
280
16
|
const { parseOptions, convertOptions: nativeConvertOptions } = featuresToNative(features);
|
|
281
|
-
//
|
|
282
|
-
// inside a single NAPI call. Skips 5 handle-NAPI roundtrips that the
|
|
283
|
-
// plugin-capable path needs to keep the arena live across passes.
|
|
17
|
+
// Without plugins, one NAPI call avoids the handle lifecycle’s extra boundary crossings.
|
|
284
18
|
if (mdastPlugins.length === 0 && hastPlugins.length === 0) {
|
|
285
19
|
const { html, frontmatter } = markdownToHtmlFast(source, parseOptions, nativeConvertOptions);
|
|
286
20
|
return { html, frontmatter: frontmatter ?? null, data };
|
|
287
21
|
}
|
|
288
|
-
//
|
|
289
|
-
// otherwise the parse skips the LineIndex build and per-node line/column lookups.
|
|
22
|
+
// Skip position tracking unless requested to avoid line-index construction and per-node lookups.
|
|
290
23
|
const trackPositions = mdastPlugins.some((p) => p.options?.position) || hastPlugins.some((p) => p.options?.position);
|
|
291
|
-
//
|
|
292
|
-
// plugin pass returns its pending commands, apply + convert + render all
|
|
293
|
-
// happen inside a single NAPI roundtrip. Saves the convert-to-hast handle
|
|
294
|
-
// create + render + drop crossings the generic path makes separately.
|
|
24
|
+
// Without HAST plugins, fuse apply, conversion, and rendering to avoid extra NAPI crossings.
|
|
295
25
|
if (hastPlugins.length === 0) {
|
|
296
26
|
const mdastHandle = createMdastHandle(source, parseOptions, trackPositions);
|
|
297
27
|
try {
|
|
298
28
|
const mdastResult = runMdastPluginsOnHandle(mdastHandle, mdastPlugins, fileURL, data, "markdown", true);
|
|
299
29
|
const finishMdast = (r) => {
|
|
300
30
|
try {
|
|
301
|
-
// Fused tail: apply pending commands (empty buffer is a Rust no-op),
|
|
302
|
-
// extract frontmatter post-mutation, convert, render, all in one
|
|
303
|
-
// NAPI roundtrip.
|
|
304
31
|
const commands = r.pendingCommands ?? EMPTY_COMMAND_BUFFER;
|
|
305
32
|
const { html, frontmatter, droppedTransforms } = applyMdastCommandsAndConvertAndRender(r.handle, commands, nativeConvertOptions);
|
|
306
|
-
|
|
307
|
-
warnDroppedTransforms(r.lastPlugin, droppedTransforms, "mdast");
|
|
33
|
+
warnIfDroppedTransforms(droppedTransforms, r.lastPlugin, "mdast");
|
|
308
34
|
return {
|
|
309
35
|
html,
|
|
310
36
|
frontmatter: frontmatter ?? null,
|
|
@@ -330,9 +56,6 @@ export function markdownToHtml(source, options = {}) {
|
|
|
330
56
|
}
|
|
331
57
|
const result = createHastHandleFromMdast(source, mdastPlugins, false, fileURL, parseOptions, nativeConvertOptions, data, trackPositions);
|
|
332
58
|
const runHastThenRender = (r) => {
|
|
333
|
-
// Run all but the last HAST plugin normally (each one applies its
|
|
334
|
-
// commands inline); collect the last plugin's commands and fuse apply +
|
|
335
|
-
// render + handle-drop into one NAPI call.
|
|
336
59
|
let collected;
|
|
337
60
|
try {
|
|
338
61
|
collected = runHastPluginsCollectLast(r.hastHandle, hastPlugins, source, fileURL, data, "markdown");
|
|
@@ -353,10 +76,9 @@ export function markdownToHtml(source, options = {}) {
|
|
|
353
76
|
try {
|
|
354
77
|
let html;
|
|
355
78
|
if (collected.commands.length > 0) {
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
html = result.html;
|
|
79
|
+
const transformed = applyCommandsAndRenderHandle(h, collected.commands);
|
|
80
|
+
warnIfDroppedTransforms(transformed.droppedTransforms, collected.lastPlugin, "hast");
|
|
81
|
+
html = transformed.html;
|
|
360
82
|
}
|
|
361
83
|
else {
|
|
362
84
|
html = renderHandle(h);
|
|
@@ -377,26 +99,21 @@ export function mdxToJs(source, options = {}) {
|
|
|
377
99
|
export function markdownToJs(source, options = {}) {
|
|
378
100
|
return toJsImpl(source, options, false);
|
|
379
101
|
}
|
|
380
|
-
/** `mdx` picks the parser; the pipeline is identical from MDAST on. */
|
|
381
102
|
function toJsImpl(source, options, mdx) {
|
|
382
103
|
const { mdastPlugins: mdastInput = [], hastPlugins: hastInput = [], features, fileURL, data = {}, ...mdxFields } = options;
|
|
383
104
|
const sourceFormat = mdx ? "mdx" : "markdown";
|
|
384
|
-
const mdastPlugins = normalizePlugins(mdastInput, "mdastPlugins", source, fileURL, sourceFormat, data);
|
|
385
|
-
const hastPlugins = normalizePlugins(hastInput, "hastPlugins", source, fileURL, sourceFormat, data);
|
|
105
|
+
const mdastPlugins = normalizePlugins(mdastInput, "mdastPlugins", source, fileURL, sourceFormat, data, features);
|
|
106
|
+
const hastPlugins = normalizePlugins(hastInput, "hastPlugins", source, fileURL, sourceFormat, data, features);
|
|
386
107
|
const hastMayHaveStubs = hastPlugins.length > 0;
|
|
387
108
|
const mdxOptions = mdxOptionsToNative(mdxFields);
|
|
388
109
|
const { parseOptions, convertOptions: nativeConvertOptions } = featuresToNative(features);
|
|
389
|
-
//
|
|
390
|
-
// frontmatter extraction all happen inside a single NAPI call. Skips 5 of
|
|
391
|
-
// the 6 handle-based crossings the plugin-capable path needs.
|
|
110
|
+
// Without plugins, one NAPI call avoids the handle lifecycle’s extra boundary crossings.
|
|
392
111
|
if (mdastPlugins.length === 0 && hastPlugins.length === 0) {
|
|
393
112
|
const { code, frontmatter } = (mdx ? mdxToJsFast : markdownToJsFast)(source, parseOptions, mdxOptions, nativeConvertOptions);
|
|
394
113
|
return { code, frontmatter: frontmatter ?? null, data };
|
|
395
114
|
}
|
|
396
|
-
// Track positions only when a plugin opts in (see `markdownToHtml`).
|
|
397
115
|
const trackPositions = mdastPlugins.some((p) => p.options?.position) || hastPlugins.some((p) => p.options?.position);
|
|
398
|
-
//
|
|
399
|
-
// frontmatter + convert + simplify + compile happen in one NAPI call.
|
|
116
|
+
// Without HAST plugins, fuse apply, conversion, and compilation to avoid extra NAPI crossings.
|
|
400
117
|
if (hastPlugins.length === 0) {
|
|
401
118
|
const mdastHandle = mdx
|
|
402
119
|
? createMdxMdastHandle(source, parseOptions, trackPositions)
|
|
@@ -407,8 +124,7 @@ function toJsImpl(source, options, mdx) {
|
|
|
407
124
|
try {
|
|
408
125
|
const commands = r.pendingCommands ?? EMPTY_COMMAND_BUFFER;
|
|
409
126
|
const { code, frontmatter, droppedTransforms } = applyMdastCommandsAndConvertAndCompile(r.handle, commands, mdxOptions, nativeConvertOptions);
|
|
410
|
-
|
|
411
|
-
warnDroppedTransforms(r.lastPlugin, droppedTransforms, "mdast");
|
|
127
|
+
warnIfDroppedTransforms(droppedTransforms, r.lastPlugin, "mdast");
|
|
412
128
|
return {
|
|
413
129
|
code,
|
|
414
130
|
frontmatter: frontmatter ?? null,
|
|
@@ -454,10 +170,9 @@ function toJsImpl(source, options, mdx) {
|
|
|
454
170
|
try {
|
|
455
171
|
let code;
|
|
456
172
|
if (collected.commands.length > 0) {
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
code = result.code;
|
|
173
|
+
const transformed = applyCommandsAndCompileHandle(h, collected.commands, mdxOptions);
|
|
174
|
+
warnIfDroppedTransforms(transformed.droppedTransforms, collected.lastPlugin, "hast");
|
|
175
|
+
code = transformed.code;
|
|
461
176
|
}
|
|
462
177
|
else {
|
|
463
178
|
code = compileHandle(h, mdxOptions);
|
|
@@ -492,99 +207,111 @@ export function evaluate(source, options) {
|
|
|
492
207
|
}
|
|
493
208
|
return new Function(result.code)(runtime);
|
|
494
209
|
}
|
|
495
|
-
function
|
|
496
|
-
|
|
497
|
-
|
|
210
|
+
function materializeMdastHandle(handle) {
|
|
211
|
+
try {
|
|
212
|
+
return materializeMdastTree(new MdastReader(serializeHandle(handle)));
|
|
213
|
+
}
|
|
214
|
+
finally {
|
|
215
|
+
releaseHandle(handle, true);
|
|
216
|
+
}
|
|
498
217
|
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
function createHastHandleFromMdast(source, mdastPlugins, mdx, fileURL, parseOptions, nativeConvertOptions, data, trackPositions) {
|
|
503
|
-
if (mdastPlugins.length === 0) {
|
|
504
|
-
const [hastHandle, raw] = mdx
|
|
505
|
-
? createMdxHastHandleWithFrontmatter(source, parseOptions, nativeConvertOptions, trackPositions)
|
|
506
|
-
: createHastHandleWithFrontmatter(source, parseOptions, nativeConvertOptions, trackPositions);
|
|
507
|
-
return {
|
|
508
|
-
hastHandle,
|
|
509
|
-
frontmatter: raw ? { kind: raw.kind === "toml" ? "toml" : "yaml", value: raw.value } : null,
|
|
510
|
-
};
|
|
218
|
+
function materializeHastHandle(handle, invalidateStubs) {
|
|
219
|
+
try {
|
|
220
|
+
return materializeHastTree(new HastReader(serializeHandle(handle)));
|
|
511
221
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
// on success, but if any step here throws the handle would otherwise leak.
|
|
519
|
-
const finalize = (r) => {
|
|
222
|
+
finally {
|
|
223
|
+
releaseHandle(handle, invalidateStubs);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
function applyCollectedHastCommands(handle, collected, invalidateStubs) {
|
|
227
|
+
if (collected.commands.length > 0) {
|
|
520
228
|
try {
|
|
521
|
-
const
|
|
522
|
-
|
|
523
|
-
if (mdastMayHaveStubs)
|
|
524
|
-
markHandleMutated(r.handle);
|
|
525
|
-
const hastHandle = convertMdastToHastHandle(r.handle, nativeConvertOptions);
|
|
526
|
-
return { hastHandle, frontmatter };
|
|
229
|
+
const dropped = applyCommandsToHandle(handle, collected.commands);
|
|
230
|
+
warnIfDroppedTransforms(dropped, collected.lastPlugin, "hast");
|
|
527
231
|
}
|
|
528
|
-
|
|
529
|
-
releaseHandle(
|
|
232
|
+
catch (err) {
|
|
233
|
+
releaseHandle(handle, invalidateStubs);
|
|
234
|
+
throw err;
|
|
530
235
|
}
|
|
531
|
-
}
|
|
236
|
+
}
|
|
237
|
+
return materializeHastHandle(handle, invalidateStubs);
|
|
238
|
+
}
|
|
239
|
+
function toMdastImpl(source, options, mdx) {
|
|
240
|
+
const { features, fileURL, data = {} } = options;
|
|
241
|
+
const sourceFormat = mdx ? "mdx" : "markdown";
|
|
242
|
+
const mdastPlugins = normalizePlugins(options.mdastPlugins ?? [], "mdastPlugins", source, fileURL, sourceFormat, data, features);
|
|
243
|
+
const { parseOptions } = featuresToNative(features);
|
|
244
|
+
if (mdastPlugins.length === 0) {
|
|
245
|
+
const wire = parseMdastWire(source, parseOptions, mdx, options.position);
|
|
246
|
+
return materializeMdastTree(new MdastReader(wire));
|
|
247
|
+
}
|
|
248
|
+
// Tree callers control positions independently of plugin requirements.
|
|
249
|
+
const trackPositions = options.position ?? true;
|
|
250
|
+
const handle = mdx
|
|
251
|
+
? createMdxMdastHandle(source, parseOptions, trackPositions)
|
|
252
|
+
: createMdastHandle(source, parseOptions, trackPositions);
|
|
532
253
|
try {
|
|
533
|
-
const
|
|
534
|
-
if (
|
|
535
|
-
return
|
|
536
|
-
releaseHandle(
|
|
254
|
+
const result = runMdastPluginsOnHandle(handle, mdastPlugins, fileURL, data, sourceFormat);
|
|
255
|
+
if (result instanceof Promise) {
|
|
256
|
+
return result.then((r) => materializeMdastHandle(r.handle), (err) => {
|
|
257
|
+
releaseHandle(handle, true);
|
|
537
258
|
throw err;
|
|
538
259
|
});
|
|
539
260
|
}
|
|
540
|
-
return
|
|
261
|
+
return materializeMdastHandle(result.handle);
|
|
541
262
|
}
|
|
542
263
|
catch (err) {
|
|
543
|
-
releaseHandle(
|
|
264
|
+
releaseHandle(handle, true);
|
|
544
265
|
throw err;
|
|
545
266
|
}
|
|
546
267
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
|
|
268
|
+
function toHastImpl(source, options, mdx) {
|
|
269
|
+
const { features, fileURL, data = {} } = options;
|
|
270
|
+
const sourceFormat = mdx ? "mdx" : "markdown";
|
|
271
|
+
const mdastPlugins = normalizePlugins(options.mdastPlugins ?? [], "mdastPlugins", source, fileURL, sourceFormat, data, features);
|
|
272
|
+
const hastPlugins = normalizePlugins(options.hastPlugins ?? [], "hastPlugins", source, fileURL, sourceFormat, data, features);
|
|
273
|
+
const { parseOptions, convertOptions } = featuresToNative(features);
|
|
274
|
+
if (mdastPlugins.length === 0 && hastPlugins.length === 0) {
|
|
275
|
+
const wire = parseHastWire(source, parseOptions, convertOptions, mdx, options.position);
|
|
276
|
+
return materializeHastTree(new HastReader(wire));
|
|
555
277
|
}
|
|
278
|
+
const trackPositions = options.position ?? true;
|
|
279
|
+
const hastMayHaveStubs = hastPlugins.length > 0;
|
|
280
|
+
const runHastPluginsAndMaterialize = (handle) => {
|
|
281
|
+
if (hastPlugins.length === 0)
|
|
282
|
+
return materializeHastHandle(handle, hastMayHaveStubs);
|
|
283
|
+
let collected;
|
|
284
|
+
try {
|
|
285
|
+
collected = runHastPluginsCollectLast(handle, hastPlugins, source, fileURL, data, sourceFormat);
|
|
286
|
+
}
|
|
287
|
+
catch (err) {
|
|
288
|
+
releaseHandle(handle, hastMayHaveStubs);
|
|
289
|
+
throw err;
|
|
290
|
+
}
|
|
291
|
+
if (collected instanceof Promise) {
|
|
292
|
+
return collected.then((c) => applyCollectedHastCommands(handle, c, hastMayHaveStubs), (err) => {
|
|
293
|
+
releaseHandle(handle, hastMayHaveStubs);
|
|
294
|
+
throw err;
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
return applyCollectedHastCommands(handle, collected, hastMayHaveStubs);
|
|
298
|
+
};
|
|
299
|
+
const result = createHastHandleFromMdast(source, mdastPlugins, mdx, fileURL, parseOptions, convertOptions, data, trackPositions);
|
|
300
|
+
if (result instanceof Promise)
|
|
301
|
+
return result.then((r) => runHastPluginsAndMaterialize(r.hastHandle));
|
|
302
|
+
return runHastPluginsAndMaterialize(result.hastHandle);
|
|
303
|
+
}
|
|
304
|
+
export function markdownToMdast(source, options = {}) {
|
|
305
|
+
return toMdastImpl(source, options, false);
|
|
556
306
|
}
|
|
557
|
-
/** Parse MDX source into a materialized mdast tree. */
|
|
558
307
|
export function mdxToMdast(source, options = {}) {
|
|
559
|
-
|
|
560
|
-
try {
|
|
561
|
-
return materializeMdastTree(new MdastReader(serializeHandle(handle)));
|
|
562
|
-
}
|
|
563
|
-
finally {
|
|
564
|
-
releaseHandle(handle, true);
|
|
565
|
-
}
|
|
308
|
+
return toMdastImpl(source, options, true);
|
|
566
309
|
}
|
|
567
|
-
/** Convert Markdown source to a materialized hast tree. */
|
|
568
310
|
export function markdownToHast(source, options = {}) {
|
|
569
|
-
|
|
570
|
-
const handle = createHastHandle(source, parseOptions, convertOptions, options.position);
|
|
571
|
-
try {
|
|
572
|
-
return materializeHastTree(new HastReader(serializeHandle(handle)));
|
|
573
|
-
}
|
|
574
|
-
finally {
|
|
575
|
-
releaseHandle(handle, true);
|
|
576
|
-
}
|
|
311
|
+
return toHastImpl(source, options, false);
|
|
577
312
|
}
|
|
578
|
-
/** Convert MDX source to a materialized hast tree. */
|
|
579
313
|
export function mdxToHast(source, options = {}) {
|
|
580
|
-
|
|
581
|
-
const handle = createMdxHastHandle(source, parseOptions, convertOptions, options.position);
|
|
582
|
-
try {
|
|
583
|
-
return materializeHastTree(new HastReader(serializeHandle(handle)));
|
|
584
|
-
}
|
|
585
|
-
finally {
|
|
586
|
-
releaseHandle(handle, true);
|
|
587
|
-
}
|
|
314
|
+
return toHastImpl(source, options, true);
|
|
588
315
|
}
|
|
589
316
|
/**
|
|
590
317
|
* Parse an HTML string into a materialized hast tree: a `root` whose children
|
|
@@ -601,3 +328,26 @@ export function htmlToHast(html, options = {}) {
|
|
|
601
328
|
releaseHandle(handle, true);
|
|
602
329
|
}
|
|
603
330
|
}
|
|
331
|
+
/**
|
|
332
|
+
* Serialize a HAST tree to an HTML string: a `root` renders its children, any
|
|
333
|
+
* other node renders itself, and a list renders in order. MDX nodes have no
|
|
334
|
+
* HTML representation and are skipped; `raw` nodes are emitted verbatim.
|
|
335
|
+
*
|
|
336
|
+
* The result is the tree's exact serialization, with no trailing newline (the
|
|
337
|
+
* one `markdownToHtml` ends its document with).
|
|
338
|
+
*/
|
|
339
|
+
export function hastToHtml(tree) {
|
|
340
|
+
return encodeHastDocument(documentNodes(tree), renderHastOpstream);
|
|
341
|
+
}
|
|
342
|
+
/** A `root` contributes its children, wherever it sits, so a list of trees
|
|
343
|
+
* serializes as the concatenation of what each renders alone. */
|
|
344
|
+
function documentNodes(tree) {
|
|
345
|
+
if (!Array.isArray(tree)) {
|
|
346
|
+
const node = tree;
|
|
347
|
+
return node.type === "root" ? (node.children ?? []) : [node];
|
|
348
|
+
}
|
|
349
|
+
const nodes = tree;
|
|
350
|
+
return nodes.some((node) => node.type === "root")
|
|
351
|
+
? nodes.flatMap((node) => (node.type === "root" ? (node.children ?? []) : [node]))
|
|
352
|
+
: nodes;
|
|
353
|
+
}
|
package/dist/directive-types.js
CHANGED
|
@@ -1,7 +1,2 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
// These replicate the type definitions from mdast-util-directive so we can
|
|
4
|
-
// avoid pulling in that package (and its transitive deps) just for the
|
|
5
|
-
// node interfaces. The shapes match what `getDirectiveData` returns from
|
|
6
|
-
// the Rust arena: `name: string`, `attributes: Record<string, string>`.
|
|
1
|
+
// Local interfaces avoid a runtime dependency on mdast-util-directive.
|
|
7
2
|
export {};
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
/** `b"MDAR"` magic, read as a little-endian u32. */
|
|
2
1
|
export declare const ARENA_MAGIC = 1380009037;
|
|
3
|
-
/** `Arena<K>` kind tags carried in the header's `kind` field. */
|
|
4
2
|
export declare const KIND_MDAST = 1;
|
|
5
3
|
export declare const KIND_HAST = 2;
|
|
6
|
-
/** `ArenaNode` `#[repr(C)]` field byte offsets (u32 fields; `node_type` is a u8). */
|
|
7
4
|
export declare const FIELD: {
|
|
8
5
|
readonly id: 0;
|
|
9
6
|
readonly node_type: 4;
|
|
@@ -19,7 +16,12 @@ export declare const FIELD: {
|
|
|
19
16
|
readonly data_offset: 44;
|
|
20
17
|
readonly data_len: 48;
|
|
21
18
|
};
|
|
22
|
-
|
|
19
|
+
export declare const W_PARENT = 2;
|
|
20
|
+
export declare const W_START_OFFSET = 3;
|
|
21
|
+
export declare const W_CHILDREN_START = 9;
|
|
22
|
+
export declare const W_CHILDREN_COUNT = 10;
|
|
23
|
+
export declare const W_DATA_OFFSET = 11;
|
|
24
|
+
export declare const W_DATA_LEN = 12;
|
|
23
25
|
export declare const HEADER: {
|
|
24
26
|
readonly magic: 0;
|
|
25
27
|
readonly kind: 4;
|