satteri 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -0
- package/dist/byte-writer.d.ts +31 -0
- package/dist/byte-writer.js +106 -0
- package/dist/child-stub.d.ts +17 -0
- package/dist/child-stub.js +57 -0
- package/dist/command-buffer.d.ts +23 -26
- package/dist/command-buffer.js +64 -132
- package/dist/compile.d.ts +5 -1
- package/dist/compile.js +40 -26
- package/dist/directive-types.d.ts +4 -2
- package/dist/generated/arena-layout.d.ts +37 -0
- package/dist/generated/arena-layout.js +41 -0
- package/dist/generated/wire-constants.d.ts +51 -0
- package/dist/generated/wire-constants.js +63 -0
- package/dist/handles.d.ts +21 -0
- package/dist/handles.js +1 -0
- package/dist/hast/child-stub.d.ts +18 -0
- package/dist/hast/child-stub.js +43 -0
- package/dist/hast/element-props.d.ts +2 -0
- package/dist/hast/element-props.js +23 -0
- package/dist/hast/generated/node-types.d.ts +14 -0
- package/dist/hast/generated/node-types.js +64 -0
- package/dist/hast/hast-materializer.d.ts +2 -1
- package/dist/hast/hast-materializer.js +9 -45
- package/dist/hast/hast-reader.d.ts +18 -23
- package/dist/hast/hast-reader.js +48 -115
- package/dist/hast/hast-visitor.d.ts +35 -13
- package/dist/hast/hast-visitor.js +395 -302
- package/dist/index.d.ts +10 -4
- package/dist/index.js +23 -1
- package/dist/lazy-child-resolver.d.ts +36 -0
- package/dist/lazy-child-resolver.js +95 -0
- package/dist/lazy-props.d.ts +1 -1
- package/dist/mdast/child-stub.d.ts +18 -0
- package/dist/mdast/child-stub.js +46 -0
- package/dist/mdast/column-align.d.ts +1 -0
- package/dist/mdast/column-align.js +6 -0
- package/dist/mdast/generated/layout.d.ts +19 -0
- package/dist/mdast/generated/layout.js +272 -0
- package/dist/mdast/generated/node-types.d.ts +58 -0
- package/dist/mdast/generated/node-types.js +266 -0
- package/dist/mdast/mdast-materializer.d.ts +3 -2
- package/dist/mdast/mdast-materializer.js +9 -93
- package/dist/mdast/mdast-reader.d.ts +4 -120
- package/dist/mdast/mdast-reader.js +45 -272
- package/dist/mdast/mdast-visitor.d.ts +52 -14
- package/dist/mdast/mdast-visitor.js +348 -427
- package/dist/mdx-attr.d.ts +2 -0
- package/dist/mdx-attr.js +20 -0
- package/dist/op-stream.d.ts +38 -0
- package/dist/op-stream.js +125 -0
- package/dist/phantom.d.ts +2 -0
- package/dist/phantom.js +13 -0
- package/dist/types.d.ts +34 -4
- package/dist/visitor-shared.d.ts +28 -0
- package/dist/visitor-shared.js +52 -0
- package/dist/wire-read.d.ts +27 -0
- package/dist/wire-read.js +59 -0
- package/index.js +52 -52
- package/package.json +6 -6
|
@@ -1,78 +1,60 @@
|
|
|
1
|
-
import { materializeNode
|
|
1
|
+
import { materializeNode } from "./mdast-materializer.js";
|
|
2
2
|
import { MdastReader } from "./mdast-reader.js";
|
|
3
3
|
import { CommandBuffer, classifyReturn } from "../command-buffer.js";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
AppendChild: "appendChild",
|
|
13
|
-
SetProperty: "setProperty",
|
|
14
|
-
};
|
|
15
|
-
const VISITOR_KEYS = new Set([
|
|
16
|
-
"paragraph",
|
|
17
|
-
"heading",
|
|
18
|
-
"thematicBreak",
|
|
19
|
-
"blockquote",
|
|
20
|
-
"list",
|
|
21
|
-
"listItem",
|
|
22
|
-
"html",
|
|
23
|
-
"code",
|
|
24
|
-
"definition",
|
|
25
|
-
"text",
|
|
26
|
-
"emphasis",
|
|
27
|
-
"strong",
|
|
28
|
-
"inlineCode",
|
|
29
|
-
"break",
|
|
30
|
-
"link",
|
|
31
|
-
"image",
|
|
32
|
-
"linkReference",
|
|
33
|
-
"imageReference",
|
|
34
|
-
"footnoteDefinition",
|
|
35
|
-
"footnoteReference",
|
|
36
|
-
"table",
|
|
37
|
-
"tableRow",
|
|
38
|
-
"tableCell",
|
|
39
|
-
"delete",
|
|
40
|
-
"yaml",
|
|
41
|
-
"toml",
|
|
42
|
-
"math",
|
|
43
|
-
"inlineMath",
|
|
44
|
-
"containerDirective",
|
|
45
|
-
"leafDirective",
|
|
46
|
-
"textDirective",
|
|
47
|
-
"mdxJsxFlowElement",
|
|
48
|
-
"mdxJsxTextElement",
|
|
49
|
-
"mdxFlowExpression",
|
|
50
|
-
"mdxTextExpression",
|
|
51
|
-
"mdxjsEsm",
|
|
52
|
-
]);
|
|
4
|
+
import { ru32, rstr, readPosition } from "../wire-read.js";
|
|
5
|
+
import { decodeMdastTypeData } from "./generated/layout.js";
|
|
6
|
+
import { TYPE_NAMES, NAME_TO_TYPE, VISITOR_KEYS, MDAST_OPSTREAM_TYPES, } from "./generated/node-types.js";
|
|
7
|
+
import { OpWriter, OF_VALUE, OF_URL, OF_TITLE, OF_ALT, OF_LANG, OF_META, OF_IDENTIFIER, OF_LABEL, OF_NAME, OF_REFERENCE_TYPE, OF_DEPTH, OF_CHECKED, OF_START, OF_ORDERED, OF_SPREAD, OF_EXPLICIT, PROP_STRING, emitMdxAttr, } from "../op-stream.js";
|
|
8
|
+
import { walkMdastHandle, mdastTextContentHandle } from "#binding";
|
|
9
|
+
import { asArray, makeRequireNid, mergeAndReset, unencodableContentError, } from "../visitor-shared.js";
|
|
10
|
+
import { LazyChildResolver } from "../lazy-child-resolver.js";
|
|
11
|
+
import { MdastChildStub } from "./child-stub.js";
|
|
53
12
|
/** Maps MdastNode objects to their arena node IDs without Object.defineProperty overhead. */
|
|
54
13
|
const mdastNodeIdMap = new WeakMap();
|
|
55
14
|
function nid(node) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
15
|
+
// Genuine stubs carry their id as a plain field; a spread copy is not
|
|
16
|
+
// `instanceof` and has no `_nodeId`, so it correctly reads as new content.
|
|
17
|
+
if (node instanceof MdastChildStub)
|
|
18
|
+
return node._id;
|
|
19
|
+
const id = mdastNodeIdMap.get(node);
|
|
20
|
+
if (id !== undefined)
|
|
21
|
+
return id;
|
|
22
|
+
// Plain objects are trusted only via the WeakMap or a NON-enumerable
|
|
23
|
+
// `_nodeId` (the materializer's convention, which spread cannot copy) — an
|
|
24
|
+
// enumerable one rode in on a copy and must read as new content.
|
|
25
|
+
const d = Object.getOwnPropertyDescriptor(node, "_nodeId");
|
|
26
|
+
return d !== undefined && !d.enumerable ? d.value : undefined;
|
|
60
27
|
}
|
|
28
|
+
const requireNid = makeRequireNid(nid);
|
|
61
29
|
export class MdastVisitorContext {
|
|
62
30
|
#commandBuffer = new CommandBuffer();
|
|
63
31
|
#diagnostics = [];
|
|
64
32
|
#handle;
|
|
65
33
|
#getSource;
|
|
34
|
+
#resolver;
|
|
35
|
+
/** One canonical object per parent id, so visitors can dedupe by identity.
|
|
36
|
+
* Null until the first `parent()` call; most passes never make one. */
|
|
37
|
+
#parentsById = null;
|
|
66
38
|
/**
|
|
67
39
|
* The URL of the document being processed (the compile `fileURL` option),
|
|
68
40
|
* or `undefined` when none was given. Use `fileURLToPath(ctx.fileURL)` for a
|
|
69
41
|
* decoded filesystem path.
|
|
70
42
|
*/
|
|
71
43
|
fileURL;
|
|
72
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Document-level data bag, shared across every plugin in the compile and
|
|
46
|
+
* across the mdast→hast phase boundary. Mutate keys directly
|
|
47
|
+
* (`ctx.data.foo = x`); the bag itself isn't reassignable. Values are kept
|
|
48
|
+
* on the JS side, so any value is allowed, including functions and class
|
|
49
|
+
* instances. Returned to the caller as `result.data`.
|
|
50
|
+
*/
|
|
51
|
+
data;
|
|
52
|
+
constructor(handle, getSource, fileURL, resolver, data) {
|
|
73
53
|
this.#handle = handle;
|
|
74
54
|
this.#getSource = getSource;
|
|
75
55
|
this.fileURL = fileURL;
|
|
56
|
+
this.#resolver = resolver;
|
|
57
|
+
this.data = data;
|
|
76
58
|
}
|
|
77
59
|
get source() {
|
|
78
60
|
const value = this.#getSource();
|
|
@@ -80,34 +62,35 @@ export class MdastVisitorContext {
|
|
|
80
62
|
return value;
|
|
81
63
|
}
|
|
82
64
|
removeNode(node) {
|
|
83
|
-
this.#commandBuffer.removeNode(
|
|
65
|
+
this.#commandBuffer.removeNode(requireNid(node, "removeNode"));
|
|
84
66
|
}
|
|
85
67
|
insertBefore(node, newNode) {
|
|
86
|
-
const id =
|
|
68
|
+
const id = requireNid(node, "insertBefore");
|
|
87
69
|
for (const n of asArray(newNode))
|
|
88
|
-
this.#commandBuffer
|
|
70
|
+
emitMdastTree(this.#commandBuffer, "insertBefore", id, n);
|
|
89
71
|
}
|
|
90
72
|
insertAfter(node, newNode) {
|
|
91
|
-
const id =
|
|
73
|
+
const id = requireNid(node, "insertAfter");
|
|
92
74
|
for (const n of asArray(newNode))
|
|
93
|
-
this.#commandBuffer
|
|
75
|
+
emitMdastTree(this.#commandBuffer, "insertAfter", id, n);
|
|
94
76
|
}
|
|
95
77
|
/**
|
|
96
78
|
* Wrap `node` in `parentNode`, making it `parentNode`'s first child. Any
|
|
97
79
|
* children `parentNode` declares are kept after it.
|
|
98
80
|
*/
|
|
99
81
|
wrapNode(node, parentNode) {
|
|
100
|
-
|
|
82
|
+
const id = requireNid(node, "wrapNode");
|
|
83
|
+
emitMdastTree(this.#commandBuffer, "wrapNode", id, parentNode);
|
|
101
84
|
}
|
|
102
85
|
prependChild(node, childNode) {
|
|
103
|
-
const id =
|
|
86
|
+
const id = requireNid(node, "prependChild");
|
|
104
87
|
for (const n of asArray(childNode))
|
|
105
|
-
this.#commandBuffer
|
|
88
|
+
emitMdastTree(this.#commandBuffer, "prependChild", id, n);
|
|
106
89
|
}
|
|
107
90
|
appendChild(node, childNode) {
|
|
108
|
-
const id =
|
|
91
|
+
const id = requireNid(node, "appendChild");
|
|
109
92
|
for (const n of asArray(childNode))
|
|
110
|
-
this.#commandBuffer
|
|
93
|
+
emitMdastTree(this.#commandBuffer, "appendChild", id, n);
|
|
111
94
|
}
|
|
112
95
|
/** Insert one node or an array at `index`; clamps (`0` or less prepends, past the end appends). */
|
|
113
96
|
insertChildAt(node, index, childNode) {
|
|
@@ -129,26 +112,49 @@ export class MdastVisitorContext {
|
|
|
129
112
|
this.removeNode(child);
|
|
130
113
|
}
|
|
131
114
|
replaceNode(node, newNode) {
|
|
132
|
-
|
|
115
|
+
const id = requireNid(node, "replaceNode");
|
|
116
|
+
emitMdastTree(this.#commandBuffer, "replace", id, newNode, true);
|
|
133
117
|
}
|
|
134
118
|
setProperty(node, key, value) {
|
|
135
119
|
if (key === "children") {
|
|
136
120
|
// children is structural: set-children keeps the node and swaps only its
|
|
137
121
|
// child list (reused children keep their id).
|
|
138
|
-
const
|
|
139
|
-
|
|
122
|
+
const id = requireNid(node, "setProperty");
|
|
123
|
+
const ops = compileMdastChildrenToOpstream(value);
|
|
124
|
+
if (!ops)
|
|
125
|
+
throw unencodableContentError(value);
|
|
126
|
+
this.#commandBuffer.setChildrenOpstream(id, ops);
|
|
140
127
|
return;
|
|
141
128
|
}
|
|
142
129
|
if (key === "data") {
|
|
143
130
|
// data is stored as JSON in the arena, serialize it for the command buffer
|
|
144
|
-
this.#commandBuffer.setProperty(
|
|
131
|
+
this.#commandBuffer.setProperty(requireNid(node, "setProperty"), key, value != null ? JSON.stringify(value) : null);
|
|
145
132
|
return;
|
|
146
133
|
}
|
|
147
|
-
this.#commandBuffer.setProperty(
|
|
134
|
+
this.#commandBuffer.setProperty(requireNid(node, "setProperty"), key, value);
|
|
148
135
|
}
|
|
149
136
|
/** Collect the concatenated text of all descendant text nodes (like mdast-util-to-string). */
|
|
150
137
|
textContent(node, options) {
|
|
151
|
-
return mdastTextContentHandle(this.#handle,
|
|
138
|
+
return mdastTextContentHandle(this.#handle, requireNid(node, "textContent"), options);
|
|
139
|
+
}
|
|
140
|
+
parent(node) {
|
|
141
|
+
const parentId = this.#resolver.parentIdOf(requireNid(node, "parent"));
|
|
142
|
+
if (parentId === undefined)
|
|
143
|
+
return undefined;
|
|
144
|
+
const byId = (this.#parentsById ??= new Map());
|
|
145
|
+
let parent = byId.get(parentId);
|
|
146
|
+
if (parent === undefined) {
|
|
147
|
+
parent = this.#resolver.materializeOne(parentId);
|
|
148
|
+
byId.set(parentId, parent);
|
|
149
|
+
}
|
|
150
|
+
return parent;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Index of `node` within its parent's children, or `undefined` at the root.
|
|
154
|
+
* Use this rather than `parent.children.indexOf(node)`, which won't find it.
|
|
155
|
+
*/
|
|
156
|
+
indexOf(node) {
|
|
157
|
+
return this.#resolver.indexInParent(requireNid(node, "indexOf"));
|
|
152
158
|
}
|
|
153
159
|
report({ message, node, severity = "error", }) {
|
|
154
160
|
this.#diagnostics.push({
|
|
@@ -166,32 +172,6 @@ export class MdastVisitorContext {
|
|
|
166
172
|
return this.#diagnostics;
|
|
167
173
|
}
|
|
168
174
|
}
|
|
169
|
-
/** Merge return-value + context command buffers and release internals. */
|
|
170
|
-
function mergeAndReset(returnBuffer, ctx) {
|
|
171
|
-
const ctxCmdBuf = ctx.getCommandBuffer();
|
|
172
|
-
const ctxBuf = ctxCmdBuf.getBuffer();
|
|
173
|
-
const retBuf = returnBuffer.getBuffer();
|
|
174
|
-
const totalLen = retBuf.length + ctxBuf.length;
|
|
175
|
-
let merged;
|
|
176
|
-
if (totalLen === 0) {
|
|
177
|
-
merged = new Uint8Array(0);
|
|
178
|
-
}
|
|
179
|
-
else {
|
|
180
|
-
merged = new Uint8Array(totalLen);
|
|
181
|
-
merged.set(retBuf, 0);
|
|
182
|
-
merged.set(ctxBuf, retBuf.length);
|
|
183
|
-
}
|
|
184
|
-
returnBuffer.reset();
|
|
185
|
-
ctxCmdBuf.reset();
|
|
186
|
-
return { merged, hasMutations: totalLen > 0 };
|
|
187
|
-
}
|
|
188
|
-
const textDecoder = new TextDecoder("utf-8");
|
|
189
|
-
/** Build name→nodeType map from TYPE_NAMES (reverse of TYPE_NAMES). */
|
|
190
|
-
const NAME_TO_TYPE = {};
|
|
191
|
-
for (const [num, name] of Object.entries(TYPE_NAMES)) {
|
|
192
|
-
NAME_TO_TYPE[name] = Number(num);
|
|
193
|
-
}
|
|
194
|
-
/** Resolve subscriptions from a plugin instance. */
|
|
195
175
|
export function resolveMdastSubscriptions(plugin) {
|
|
196
176
|
const subs = [];
|
|
197
177
|
for (const [name, fn] of Object.entries(plugin)) {
|
|
@@ -207,69 +187,61 @@ export function resolveMdastSubscriptions(plugin) {
|
|
|
207
187
|
}
|
|
208
188
|
return subs;
|
|
209
189
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
190
|
+
class MdastLazyChildResolver extends LazyChildResolver {
|
|
191
|
+
createReader(wire) {
|
|
192
|
+
return new MdastReader(wire);
|
|
193
|
+
}
|
|
194
|
+
materializeNode(reader, nodeId) {
|
|
195
|
+
return materializeNode(reader, nodeId);
|
|
196
|
+
}
|
|
197
|
+
readParentId(reader, nodeId) {
|
|
198
|
+
return reader.getParentId(nodeId);
|
|
199
|
+
}
|
|
200
|
+
readChildIds(reader, nodeId) {
|
|
201
|
+
return reader.getChildIds(nodeId);
|
|
202
|
+
}
|
|
217
203
|
}
|
|
218
|
-
/**
|
|
219
|
-
|
|
220
|
-
|
|
204
|
+
/** Build the child-stub list for a matched node from the wire's `[child_ids]
|
|
205
|
+
* [child_types]` blocks — no arena snapshot. The seal check still applies:
|
|
206
|
+
* post-pass ids are stale, and a stub built from them could later splice the
|
|
207
|
+
* wrong node as a ref. */
|
|
208
|
+
function readMdastChildStubs(view, buf, idsPos, typesPos, count, resolver) {
|
|
209
|
+
resolver.assertUnsealed();
|
|
210
|
+
const stubs = new Array(count);
|
|
211
|
+
for (let i = 0; i < count; i++) {
|
|
212
|
+
stubs[i] = new MdastChildStub(resolver, ru32(view, idsPos + i * 4), buf[typesPos + i]);
|
|
213
|
+
}
|
|
214
|
+
return stubs;
|
|
221
215
|
}
|
|
222
|
-
/**
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
#ensure() {
|
|
234
|
-
if (!this.#reader) {
|
|
235
|
-
this.#reader = new MdastReader(serializeHandle(this.#handle));
|
|
236
|
-
}
|
|
237
|
-
return this.#reader;
|
|
238
|
-
}
|
|
239
|
-
materializeChildren(childIds) {
|
|
240
|
-
const reader = this.#ensure();
|
|
241
|
-
const handle = this.#handle;
|
|
242
|
-
return childIds.map((id) => {
|
|
243
|
-
const node = materializeNode(reader, id);
|
|
244
|
-
Object.defineProperty(node, "data", {
|
|
245
|
-
get() {
|
|
246
|
-
const json = napiGetNodeData(handle, id);
|
|
247
|
-
const val = json ? JSON.parse(json) : null;
|
|
248
|
-
Object.defineProperty(this, "data", {
|
|
249
|
-
value: val,
|
|
250
|
-
writable: true,
|
|
251
|
-
enumerable: true,
|
|
252
|
-
configurable: true,
|
|
253
|
-
});
|
|
254
|
-
return val;
|
|
255
|
-
},
|
|
256
|
-
configurable: true,
|
|
216
|
+
/** Install `children` as an own enumerable getter (spread must carry it),
|
|
217
|
+
* self-replacing with the one stable stub array on first read. One closure
|
|
218
|
+
* and one define per node — installing the wire locals as hidden slots
|
|
219
|
+
* instead measurably regressed every matching pipeline. */
|
|
220
|
+
function makeLazyChildren(node, view, buf, childIdsPos, childTypesPos, childCount, resolver) {
|
|
221
|
+
Object.defineProperty(node, "children", {
|
|
222
|
+
get() {
|
|
223
|
+
const val = readMdastChildStubs(view, buf, childIdsPos, childTypesPos, childCount, resolver);
|
|
224
|
+
Object.defineProperty(this, "children", {
|
|
225
|
+
value: val,
|
|
226
|
+
writable: true,
|
|
257
227
|
enumerable: true,
|
|
228
|
+
configurable: true,
|
|
258
229
|
});
|
|
259
|
-
return
|
|
260
|
-
}
|
|
261
|
-
|
|
230
|
+
return val;
|
|
231
|
+
},
|
|
232
|
+
enumerable: true,
|
|
233
|
+
configurable: true,
|
|
234
|
+
});
|
|
262
235
|
}
|
|
263
236
|
/**
|
|
264
237
|
* Read an MDAST node from the inline data in a match buffer entry.
|
|
265
238
|
*
|
|
266
239
|
* Inline format (from Rust serialize_mdast_node_inline):
|
|
267
|
-
* [node_data: u32+bytes][position: 6×u32 = 24B][child_count:
|
|
240
|
+
* [node_data: u32+bytes][position: 6×u32 = 24B][child_count: u32][child_ids: N×u32]
|
|
241
|
+
* [child_types: N×u8][type-specific data]
|
|
268
242
|
*/
|
|
269
|
-
const encoder = new TextEncoder();
|
|
270
243
|
function readMdastMatchedNode(view, buf, dataOffset, nodeId, nodeType, resolver) {
|
|
271
244
|
let pos = dataOffset;
|
|
272
|
-
// Node data (JSON bytes), always first
|
|
273
245
|
const dataJsonLen = ru32(view, pos);
|
|
274
246
|
pos += 4;
|
|
275
247
|
let initialData = null;
|
|
@@ -278,272 +250,54 @@ function readMdastMatchedNode(view, buf, dataOffset, nodeId, nodeType, resolver)
|
|
|
278
250
|
try {
|
|
279
251
|
initialData = JSON.parse(jsonStr);
|
|
280
252
|
}
|
|
281
|
-
catch {
|
|
282
|
-
|
|
253
|
+
catch (err) {
|
|
254
|
+
if (process.env.NODE_ENV !== "production") {
|
|
255
|
+
console.warn(`readMdastMatchedNode: malformed node_data for nodeId=${nodeId}`, err);
|
|
256
|
+
}
|
|
283
257
|
}
|
|
284
258
|
pos += dataJsonLen;
|
|
285
259
|
}
|
|
286
|
-
|
|
287
|
-
const position = {
|
|
288
|
-
start: { offset: ru32(view, pos), line: ru32(view, pos + 8), column: ru32(view, pos + 12) },
|
|
289
|
-
end: { offset: ru32(view, pos + 4), line: ru32(view, pos + 16), column: ru32(view, pos + 20) },
|
|
290
|
-
};
|
|
260
|
+
const position = readPosition(view, pos);
|
|
291
261
|
pos += 24;
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
262
|
+
const childCount = ru32(view, pos);
|
|
263
|
+
pos += 4;
|
|
264
|
+
// Ids/types decode lazily with `.children` — most matched nodes never read them.
|
|
265
|
+
const childIdsPos = pos;
|
|
266
|
+
pos += childCount * 4;
|
|
267
|
+
const childTypesPos = pos;
|
|
268
|
+
pos += childCount;
|
|
300
269
|
const typeName = TYPE_NAMES[nodeType] ?? `unknown(${nodeType})`;
|
|
301
|
-
|
|
302
|
-
|
|
270
|
+
const node = { type: typeName };
|
|
271
|
+
if (position !== undefined)
|
|
272
|
+
node.position = position;
|
|
303
273
|
if (childCount > 0) {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
}
|
|
319
|
-
switch (nodeType) {
|
|
320
|
-
case 2: {
|
|
321
|
-
// heading
|
|
322
|
-
node.depth = buf[pos];
|
|
323
|
-
break;
|
|
324
|
-
}
|
|
325
|
-
case 10:
|
|
326
|
-
case 13:
|
|
327
|
-
case 7:
|
|
328
|
-
case 25:
|
|
329
|
-
case 26: {
|
|
330
|
-
// text, inlineCode, html, yaml, toml
|
|
331
|
-
const vlen = ru32(view, pos);
|
|
332
|
-
node.value = rstr(buf, pos + 4, vlen);
|
|
333
|
-
break;
|
|
334
|
-
}
|
|
335
|
-
case 8: {
|
|
336
|
-
// code
|
|
337
|
-
const langLen = ru16(view, pos);
|
|
338
|
-
pos += 2;
|
|
339
|
-
node.lang = langLen > 0 ? rstr(buf, pos, langLen) : null;
|
|
340
|
-
pos += langLen;
|
|
341
|
-
const metaLen = ru16(view, pos);
|
|
342
|
-
pos += 2;
|
|
343
|
-
node.meta = metaLen > 0 ? rstr(buf, pos, metaLen) : null;
|
|
344
|
-
pos += metaLen;
|
|
345
|
-
const valLen = ru32(view, pos);
|
|
346
|
-
pos += 4;
|
|
347
|
-
node.value = rstr(buf, pos, valLen);
|
|
348
|
-
break;
|
|
349
|
-
}
|
|
350
|
-
case 27:
|
|
351
|
-
case 28: {
|
|
352
|
-
// math, inlineMath
|
|
353
|
-
const metaLen = ru16(view, pos);
|
|
354
|
-
pos += 2;
|
|
355
|
-
node.meta = metaLen > 0 ? rstr(buf, pos, metaLen) : null;
|
|
356
|
-
pos += metaLen;
|
|
357
|
-
const valLen = ru32(view, pos);
|
|
358
|
-
pos += 4;
|
|
359
|
-
node.value = rstr(buf, pos, valLen);
|
|
360
|
-
break;
|
|
361
|
-
}
|
|
362
|
-
case 15: {
|
|
363
|
-
// link
|
|
364
|
-
const urlLen = ru16(view, pos);
|
|
365
|
-
pos += 2;
|
|
366
|
-
node.url = rstr(buf, pos, urlLen);
|
|
367
|
-
pos += urlLen;
|
|
368
|
-
const titleLen = ru16(view, pos);
|
|
369
|
-
pos += 2;
|
|
370
|
-
node.title = titleLen > 0 ? rstr(buf, pos, titleLen) : null;
|
|
371
|
-
break;
|
|
372
|
-
}
|
|
373
|
-
case 16: {
|
|
374
|
-
// image
|
|
375
|
-
const urlLen = ru16(view, pos);
|
|
376
|
-
pos += 2;
|
|
377
|
-
node.url = rstr(buf, pos, urlLen);
|
|
378
|
-
pos += urlLen;
|
|
379
|
-
const altLen = ru16(view, pos);
|
|
380
|
-
pos += 2;
|
|
381
|
-
node.alt = rstr(buf, pos, altLen);
|
|
382
|
-
pos += altLen;
|
|
383
|
-
const titleLen = ru16(view, pos);
|
|
384
|
-
pos += 2;
|
|
385
|
-
node.title = titleLen > 0 ? rstr(buf, pos, titleLen) : null;
|
|
386
|
-
break;
|
|
387
|
-
}
|
|
388
|
-
case 9: {
|
|
389
|
-
// definition
|
|
390
|
-
const urlLen = ru16(view, pos);
|
|
391
|
-
pos += 2;
|
|
392
|
-
node.url = rstr(buf, pos, urlLen);
|
|
393
|
-
pos += urlLen;
|
|
394
|
-
const titleLen = ru16(view, pos);
|
|
395
|
-
pos += 2;
|
|
396
|
-
node.title = titleLen > 0 ? rstr(buf, pos, titleLen) : null;
|
|
397
|
-
pos += titleLen;
|
|
398
|
-
const idLen = ru16(view, pos);
|
|
399
|
-
pos += 2;
|
|
400
|
-
node.identifier = rstr(buf, pos, idLen);
|
|
401
|
-
pos += idLen;
|
|
402
|
-
const labelLen = ru16(view, pos);
|
|
403
|
-
pos += 2;
|
|
404
|
-
node.label = rstr(buf, pos, labelLen);
|
|
405
|
-
break;
|
|
406
|
-
}
|
|
407
|
-
case 5: {
|
|
408
|
-
// list
|
|
409
|
-
node.start = ru32(view, pos);
|
|
410
|
-
node.ordered = buf[pos + 4] !== 0;
|
|
411
|
-
node.spread = buf[pos + 5] !== 0;
|
|
412
|
-
if (!node.ordered)
|
|
413
|
-
node.start = null;
|
|
414
|
-
break;
|
|
415
|
-
}
|
|
416
|
-
case 6: {
|
|
417
|
-
// listItem
|
|
418
|
-
const checked = buf[pos];
|
|
419
|
-
node.checked = checked === 2 ? null : checked === 1;
|
|
420
|
-
node.spread = buf[pos + 1] !== 0;
|
|
421
|
-
break;
|
|
422
|
-
}
|
|
423
|
-
case 17:
|
|
424
|
-
case 18:
|
|
425
|
-
case 20: {
|
|
426
|
-
// linkReference, imageReference, footnoteReference
|
|
427
|
-
const idLen = ru16(view, pos);
|
|
428
|
-
pos += 2;
|
|
429
|
-
node.identifier = rstr(buf, pos, idLen);
|
|
430
|
-
pos += idLen;
|
|
431
|
-
const labelLen = ru16(view, pos);
|
|
432
|
-
pos += 2;
|
|
433
|
-
node.label = rstr(buf, pos, labelLen);
|
|
434
|
-
pos += labelLen;
|
|
435
|
-
const kind = buf[pos];
|
|
436
|
-
pos += 1;
|
|
437
|
-
// Only link/image references carry `referenceType`; the mdast spec
|
|
438
|
-
// defines it for those two, not for `footnoteReference`.
|
|
439
|
-
if (nodeType !== 20) {
|
|
440
|
-
node.referenceType = ["shortcut", "collapsed", "full"][kind] ?? "shortcut";
|
|
441
|
-
}
|
|
442
|
-
// imageReference also carries `alt` (the serializer appends it).
|
|
443
|
-
if (nodeType === 18) {
|
|
444
|
-
const altLen = ru16(view, pos);
|
|
445
|
-
pos += 2;
|
|
446
|
-
node.alt = rstr(buf, pos, altLen);
|
|
447
|
-
}
|
|
448
|
-
break;
|
|
449
|
-
}
|
|
450
|
-
case 19: {
|
|
451
|
-
// footnoteDefinition
|
|
452
|
-
const idLen = ru16(view, pos);
|
|
453
|
-
pos += 2;
|
|
454
|
-
node.identifier = rstr(buf, pos, idLen);
|
|
455
|
-
pos += idLen;
|
|
456
|
-
const labelLen = ru16(view, pos);
|
|
457
|
-
pos += 2;
|
|
458
|
-
node.label = rstr(buf, pos, labelLen);
|
|
459
|
-
break;
|
|
460
|
-
}
|
|
461
|
-
case 21: {
|
|
462
|
-
// table
|
|
463
|
-
const count = ru16(view, pos);
|
|
464
|
-
pos += 2;
|
|
465
|
-
const alignNames = [null, "left", "right", "center"];
|
|
466
|
-
node.align = Array.from({ length: count }, (_, i) => alignNames[buf[pos + i]] ?? null);
|
|
467
|
-
break;
|
|
468
|
-
}
|
|
469
|
-
case 30:
|
|
470
|
-
case 31:
|
|
471
|
-
case 32: {
|
|
472
|
-
// containerDirective, leafDirective, textDirective
|
|
473
|
-
const nameLen = ru16(view, pos);
|
|
474
|
-
pos += 2;
|
|
475
|
-
node.name = rstr(buf, pos, nameLen);
|
|
476
|
-
pos += nameLen;
|
|
477
|
-
const attrCount = ru16(view, pos);
|
|
478
|
-
pos += 2;
|
|
479
|
-
const attributes = {};
|
|
480
|
-
for (let i = 0; i < attrCount; i++) {
|
|
481
|
-
const keyLen = ru16(view, pos);
|
|
482
|
-
pos += 2;
|
|
483
|
-
const key = rstr(buf, pos, keyLen);
|
|
484
|
-
pos += keyLen;
|
|
485
|
-
const valLen = ru16(view, pos);
|
|
486
|
-
pos += 2;
|
|
487
|
-
const val = rstr(buf, pos, valLen);
|
|
488
|
-
pos += valLen;
|
|
489
|
-
attributes[key] = val;
|
|
274
|
+
makeLazyChildren(node, view, buf, childIdsPos, childTypesPos, childCount, resolver);
|
|
275
|
+
}
|
|
276
|
+
// Fixed-field types decode from the generated layout table; the rest
|
|
277
|
+
// (variable-length / cross-field) stay in the hand-written switch.
|
|
278
|
+
if (!decodeMdastTypeData(view, buf, pos, nodeType, node)) {
|
|
279
|
+
switch (nodeType) {
|
|
280
|
+
case 5: {
|
|
281
|
+
// list
|
|
282
|
+
node.start = ru32(view, pos);
|
|
283
|
+
node.ordered = buf[pos + 4] !== 0;
|
|
284
|
+
node.spread = buf[pos + 5] !== 0;
|
|
285
|
+
if (!node.ordered)
|
|
286
|
+
node.start = null;
|
|
287
|
+
break;
|
|
490
288
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
const nameLen = ru16(view, pos);
|
|
498
|
-
pos += 2;
|
|
499
|
-
node.name = nameLen > 0 ? rstr(buf, pos, nameLen) : null;
|
|
500
|
-
pos += nameLen;
|
|
501
|
-
const attrCount = ru16(view, pos);
|
|
502
|
-
pos += 2;
|
|
503
|
-
const attributes = [];
|
|
504
|
-
for (let i = 0; i < attrCount; i++) {
|
|
505
|
-
const kind = buf[pos];
|
|
506
|
-
pos += 1;
|
|
507
|
-
const anLen = ru16(view, pos);
|
|
508
|
-
pos += 2;
|
|
509
|
-
const an = rstr(buf, pos, anLen);
|
|
510
|
-
pos += anLen;
|
|
511
|
-
const avLen = ru32(view, pos);
|
|
512
|
-
pos += 4;
|
|
513
|
-
const av = rstr(buf, pos, avLen);
|
|
514
|
-
pos += avLen;
|
|
515
|
-
switch (kind) {
|
|
516
|
-
case 0:
|
|
517
|
-
attributes.push({ type: "mdxJsxAttribute", name: an, value: null });
|
|
518
|
-
break;
|
|
519
|
-
case 1:
|
|
520
|
-
attributes.push({ type: "mdxJsxAttribute", name: an, value: av });
|
|
521
|
-
break;
|
|
522
|
-
case 2:
|
|
523
|
-
attributes.push({
|
|
524
|
-
type: "mdxJsxAttribute",
|
|
525
|
-
name: an,
|
|
526
|
-
value: { type: "mdxJsxAttributeValueExpression", value: av },
|
|
527
|
-
});
|
|
528
|
-
break;
|
|
529
|
-
case 3:
|
|
530
|
-
attributes.push({ type: "mdxJsxExpressionAttribute", value: av });
|
|
531
|
-
break;
|
|
532
|
-
}
|
|
289
|
+
case 6: {
|
|
290
|
+
// listItem
|
|
291
|
+
const checked = buf[pos];
|
|
292
|
+
node.checked = checked === 2 ? null : checked === 1;
|
|
293
|
+
node.spread = buf[pos + 1] !== 0;
|
|
294
|
+
break;
|
|
533
295
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
case 103:
|
|
539
|
-
case 104: {
|
|
540
|
-
// mdxFlowExpression, mdxTextExpression, mdxjsEsm
|
|
541
|
-
const vlen = ru32(view, pos);
|
|
542
|
-
node.value = rstr(buf, pos + 4, vlen);
|
|
543
|
-
break;
|
|
296
|
+
// table (21), directives (30/31/32) and mdxJsx elements (100/101) are
|
|
297
|
+
// decoded by the generated `decodeMdastTypeData` from their tails.
|
|
298
|
+
// root(0), paragraph(1), thematicBreak(3), blockquote(4), emphasis(11),
|
|
299
|
+
// strong(12), break(14), tableRow(22), tableCell(23), delete(24): no extra data
|
|
544
300
|
}
|
|
545
|
-
// root(0), paragraph(1), thematicBreak(3), blockquote(4), emphasis(11),
|
|
546
|
-
// strong(12), break(14), tableRow(22), tableCell(23), delete(24): no extra data
|
|
547
301
|
}
|
|
548
302
|
mdastNodeIdMap.set(node, nodeId);
|
|
549
303
|
if (initialData) {
|
|
@@ -551,9 +305,6 @@ function readMdastMatchedNode(view, buf, dataOffset, nodeId, nodeType, resolver)
|
|
|
551
305
|
}
|
|
552
306
|
return node;
|
|
553
307
|
}
|
|
554
|
-
/** Apply a sync visitor result to the return buffer.
|
|
555
|
-
* If the result is the same object as the input node, treat it as a no-op
|
|
556
|
-
* so that context mutations (e.g. setProperty) are not clobbered. */
|
|
557
308
|
/** The arena id of a node if it is an existing (materialized) node, else
|
|
558
309
|
* undefined for a freshly-built one. */
|
|
559
310
|
function reusedId(node) {
|
|
@@ -562,28 +313,193 @@ function reusedId(node) {
|
|
|
562
313
|
const id = nid(node);
|
|
563
314
|
return typeof id === "number" ? id : undefined;
|
|
564
315
|
}
|
|
316
|
+
// Reused across every replacement in a pass: compile is synchronous and its
|
|
317
|
+
// result is copied into the command buffer before the next call, so a single
|
|
318
|
+
// writer is safe and avoids a 512-byte allocation per built node.
|
|
319
|
+
const mdastWriter = new OpWriter();
|
|
565
320
|
/**
|
|
566
|
-
*
|
|
567
|
-
*
|
|
568
|
-
* rebuild splices
|
|
569
|
-
*
|
|
570
|
-
*
|
|
571
|
-
* it is the new shape replacing the visited node.
|
|
321
|
+
* Compile a declarative MDAST replacement tree to the op-stream — the only
|
|
322
|
+
* structural encoding. Reused nodes (those still carrying an arena id) become
|
|
323
|
+
* `ref`s so the rebuild splices the original back in place. Returns null when
|
|
324
|
+
* the replay can't reproduce the tree identically (unsupported node type or
|
|
325
|
+
* out-of-range numeric field); the caller turns that into a hard error.
|
|
572
326
|
*/
|
|
573
|
-
function
|
|
327
|
+
function compileMdastToOpstream(root, forReplace = false) {
|
|
328
|
+
mdastWriter.begin();
|
|
329
|
+
try {
|
|
330
|
+
if (!emitMdastOp(mdastWriter, root, true, forReplace))
|
|
331
|
+
return null;
|
|
332
|
+
return mdastWriter.take();
|
|
333
|
+
}
|
|
334
|
+
finally {
|
|
335
|
+
mdastWriter.end();
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
/** Compile a set-children payload: a root-wrapped child list, the shape
|
|
339
|
+
* `Patch::SetChildren` splices in. Reused children become refs. */
|
|
340
|
+
function compileMdastChildrenToOpstream(children) {
|
|
341
|
+
if (!Array.isArray(children))
|
|
342
|
+
return null;
|
|
343
|
+
mdastWriter.begin();
|
|
344
|
+
try {
|
|
345
|
+
mdastWriter.open(NAME_TO_TYPE.root);
|
|
346
|
+
for (const c of children) {
|
|
347
|
+
if (!emitMdastOp(mdastWriter, c, false, false))
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
mdastWriter.close();
|
|
351
|
+
return mdastWriter.take();
|
|
352
|
+
}
|
|
353
|
+
finally {
|
|
354
|
+
mdastWriter.end();
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
function emitMdastOp(w, node, isRoot, forReplace) {
|
|
574
358
|
if (node === null || typeof node !== "object")
|
|
575
|
-
return
|
|
359
|
+
return false;
|
|
576
360
|
if (!isRoot) {
|
|
577
361
|
const id = reusedId(node);
|
|
578
|
-
if (id !== undefined)
|
|
579
|
-
|
|
362
|
+
if (id !== undefined) {
|
|
363
|
+
w.ref(id);
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
580
366
|
}
|
|
581
|
-
const
|
|
582
|
-
|
|
583
|
-
|
|
367
|
+
const n = node;
|
|
368
|
+
const type = MDAST_OPSTREAM_TYPES[n.type];
|
|
369
|
+
if (type === undefined)
|
|
370
|
+
return false;
|
|
371
|
+
w.open(type);
|
|
372
|
+
if (typeof n.value === "string")
|
|
373
|
+
w.str(OF_VALUE, n.value);
|
|
374
|
+
if (typeof n.url === "string")
|
|
375
|
+
w.str(OF_URL, n.url);
|
|
376
|
+
if (typeof n.title === "string")
|
|
377
|
+
w.str(OF_TITLE, n.title);
|
|
378
|
+
if (typeof n.alt === "string")
|
|
379
|
+
w.str(OF_ALT, n.alt);
|
|
380
|
+
if (typeof n.lang === "string")
|
|
381
|
+
w.str(OF_LANG, n.lang);
|
|
382
|
+
if (typeof n.meta === "string")
|
|
383
|
+
w.str(OF_META, n.meta);
|
|
384
|
+
if (typeof n.identifier === "string")
|
|
385
|
+
w.str(OF_IDENTIFIER, n.identifier);
|
|
386
|
+
if (typeof n.label === "string")
|
|
387
|
+
w.str(OF_LABEL, n.label);
|
|
388
|
+
if (typeof n.referenceType === "string")
|
|
389
|
+
w.str(OF_REFERENCE_TYPE, n.referenceType);
|
|
390
|
+
// Out-of-range numbers compile to null and the caller throws — a visible
|
|
391
|
+
// error instead of silently masking the bits.
|
|
392
|
+
if (typeof n.depth === "number") {
|
|
393
|
+
if (!Number.isInteger(n.depth) || n.depth < 0 || n.depth > 255)
|
|
394
|
+
return false;
|
|
395
|
+
w.u8(OF_DEPTH, n.depth);
|
|
396
|
+
}
|
|
397
|
+
if (typeof n.checked === "boolean")
|
|
398
|
+
w.u8(OF_CHECKED, n.checked ? 1 : 0);
|
|
399
|
+
if (typeof n.start === "number") {
|
|
400
|
+
if (!Number.isInteger(n.start) || n.start < 0 || n.start > 4294967295)
|
|
401
|
+
return false;
|
|
402
|
+
w.u32(OF_START, n.start);
|
|
403
|
+
}
|
|
404
|
+
if (typeof n.ordered === "boolean")
|
|
405
|
+
w.bool(OF_ORDERED, n.ordered);
|
|
406
|
+
if (typeof n.spread === "boolean")
|
|
407
|
+
w.bool(OF_SPREAD, n.spread);
|
|
408
|
+
if (typeof n.name === "string")
|
|
409
|
+
w.str(OF_NAME, n.name);
|
|
410
|
+
const attrs = n.attributes;
|
|
411
|
+
if (Array.isArray(attrs)) {
|
|
412
|
+
for (const a of attrs)
|
|
413
|
+
emitMdxAttr(w, a);
|
|
414
|
+
}
|
|
415
|
+
else if (attrs !== null && typeof attrs === "object") {
|
|
416
|
+
// Directive attributes: a string→string map; non-string values are
|
|
417
|
+
// dropped, since the stored form holds only strings.
|
|
418
|
+
for (const key in attrs) {
|
|
419
|
+
const v = attrs[key];
|
|
420
|
+
if (typeof v === "string")
|
|
421
|
+
w.prop(key, PROP_STRING, v);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
if (Array.isArray(n.align))
|
|
425
|
+
w.align(n.align.map(alignCode));
|
|
426
|
+
if (n.data?._mdxExplicitJsx === true) {
|
|
427
|
+
w.bool(OF_EXPLICIT, true);
|
|
428
|
+
}
|
|
429
|
+
if (n.data != null)
|
|
430
|
+
w.data(n.data);
|
|
431
|
+
if (isRoot && forReplace && n._keepChildren === true) {
|
|
432
|
+
// Replace splices the target's original children, discarding any the
|
|
433
|
+
// replacement declares.
|
|
434
|
+
w.keepChildren();
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
// `_keepChildren` only applies to replace; other ops ignore the marker
|
|
438
|
+
// and emit the declared children.
|
|
439
|
+
const children = n.children;
|
|
440
|
+
if (Array.isArray(children)) {
|
|
441
|
+
for (const c of children)
|
|
442
|
+
if (!emitMdastOp(w, c, false, forReplace))
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
w.close();
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
449
|
+
/** Map a table `align` entry to its arena code (none=0). */
|
|
450
|
+
function alignCode(a) {
|
|
451
|
+
return a === "left" ? 1 : a === "right" ? 2 : a === "center" ? 3 : 0;
|
|
452
|
+
}
|
|
453
|
+
/** True for the `{raw}` / `{rawHtml}` escape hatches — re-parsed by Rust rather
|
|
454
|
+
* than compiled to an op-stream, so they ride the RAW_MARKDOWN / RAW_HTML
|
|
455
|
+
* payloads instead of the declarative encoder. */
|
|
456
|
+
function isRawMdastContent(content) {
|
|
457
|
+
const c = content;
|
|
458
|
+
return typeof c.raw === "string" || typeof c.rawHtml === "string";
|
|
459
|
+
}
|
|
460
|
+
/** Encode `content` as the `op` structural command. Declarative nodes compile
|
|
461
|
+
* to the op-stream; the `{raw}`/`{rawHtml}` escape hatches ride the raw
|
|
462
|
+
* re-parse payloads. Anything that compiles to neither is a hard error — the
|
|
463
|
+
* op-stream is the only declarative encoding. The switches stay inline so the
|
|
464
|
+
* buffer calls are monomorphic (computed method names defeat inline caches on
|
|
465
|
+
* this warm path). */
|
|
466
|
+
function emitMdastTree(buffer, op, id, content, forReplace = false) {
|
|
467
|
+
if (isRawMdastContent(content)) {
|
|
468
|
+
switch (op) {
|
|
469
|
+
case "replace":
|
|
470
|
+
return buffer.replace(id, content);
|
|
471
|
+
case "insertBefore":
|
|
472
|
+
return buffer.insertBefore(id, content);
|
|
473
|
+
case "insertAfter":
|
|
474
|
+
return buffer.insertAfter(id, content);
|
|
475
|
+
case "prependChild":
|
|
476
|
+
return buffer.prependChild(id, content);
|
|
477
|
+
case "appendChild":
|
|
478
|
+
return buffer.appendChild(id, content);
|
|
479
|
+
case "wrapNode":
|
|
480
|
+
return buffer.wrapNode(id, content);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const ops = compileMdastToOpstream(content, forReplace);
|
|
484
|
+
if (ops === null)
|
|
485
|
+
throw unencodableContentError(content);
|
|
486
|
+
switch (op) {
|
|
487
|
+
case "replace":
|
|
488
|
+
return buffer.replaceOpstream(id, ops);
|
|
489
|
+
case "insertBefore":
|
|
490
|
+
return buffer.insertBeforeOpstream(id, ops);
|
|
491
|
+
case "insertAfter":
|
|
492
|
+
return buffer.insertAfterOpstream(id, ops);
|
|
493
|
+
case "prependChild":
|
|
494
|
+
return buffer.prependChildOpstream(id, ops);
|
|
495
|
+
case "appendChild":
|
|
496
|
+
return buffer.appendChildOpstream(id, ops);
|
|
497
|
+
case "wrapNode":
|
|
498
|
+
return buffer.wrapNodeOpstream(id, ops);
|
|
584
499
|
}
|
|
585
|
-
return node;
|
|
586
500
|
}
|
|
501
|
+
/** A result that is the same object as the input node is a no-op, so context
|
|
502
|
+
* mutations (e.g. setProperty) are not clobbered. */
|
|
587
503
|
function applyMdastVisitResult(result, nodeId, returnBuffer, originalNode) {
|
|
588
504
|
if (result === undefined || result === null)
|
|
589
505
|
return;
|
|
@@ -598,7 +514,7 @@ function applyMdastVisitResult(result, nodeId, returnBuffer, originalNode) {
|
|
|
598
514
|
returnBuffer.replace(nodeId, result);
|
|
599
515
|
break;
|
|
600
516
|
case "structured_node":
|
|
601
|
-
returnBuffer
|
|
517
|
+
emitMdastTree(returnBuffer, "replace", nodeId, result, true);
|
|
602
518
|
break;
|
|
603
519
|
}
|
|
604
520
|
}
|
|
@@ -609,18 +525,18 @@ function applyMdastVisitResult(result, nodeId, returnBuffer, originalNode) {
|
|
|
609
525
|
* Returns MdastVisitResult synchronously if all visitors are sync,
|
|
610
526
|
* or Promise<MdastVisitResult> if any visitor is async.
|
|
611
527
|
*/
|
|
612
|
-
export function visitMdastHandle(handle, plugin, subs, source, fileURL) {
|
|
528
|
+
export function visitMdastHandle(handle, plugin, subs, source, fileURL, data = {}) {
|
|
613
529
|
const getSource = typeof source === "function" ? source : () => source;
|
|
614
|
-
const context = new MdastVisitorContext(handle, getSource, fileURL);
|
|
615
|
-
const returnBuffer = new CommandBuffer();
|
|
616
530
|
const resolver = new MdastLazyChildResolver(handle);
|
|
531
|
+
const context = new MdastVisitorContext(handle, getSource, fileURL, resolver, data);
|
|
532
|
+
const returnBuffer = new CommandBuffer();
|
|
617
533
|
const rustSubs = subs.map((s) => ({ nodeType: s.nodeType, tagFilter: [] }));
|
|
618
534
|
const matchBuf = walkMdastHandle(handle, rustSubs);
|
|
619
535
|
const matchView = new DataView(matchBuf.buffer, matchBuf.byteOffset, matchBuf.byteLength);
|
|
620
536
|
const matchCount = ru32(matchView, 0);
|
|
621
537
|
let deferred = null;
|
|
622
538
|
for (let i = 0; i < matchCount; i++) {
|
|
623
|
-
const indexBase = 4 + i *
|
|
539
|
+
const indexBase = 4 + i * 10;
|
|
624
540
|
const nodeId = ru32(matchView, indexBase);
|
|
625
541
|
const subIndex = matchBuf[indexBase + 4];
|
|
626
542
|
const dataOffset = ru32(matchView, indexBase + 6);
|
|
@@ -640,9 +556,14 @@ export function visitMdastHandle(handle, plugin, subs, source, fileURL) {
|
|
|
640
556
|
for (const { nodeId, result, originalNode } of results) {
|
|
641
557
|
applyMdastVisitResult(result, nodeId, returnBuffer, originalNode);
|
|
642
558
|
}
|
|
559
|
+
// End of the pass — the caller applies the returned command buffer next,
|
|
560
|
+
// renumbering the arena, so later snapshots would resolve match-time
|
|
561
|
+
// child ids against wrong nodes. This is the last point we control.
|
|
562
|
+
resolver.seal();
|
|
643
563
|
return finalizeMdastVisit(handle, context, returnBuffer);
|
|
644
564
|
});
|
|
645
565
|
}
|
|
566
|
+
resolver.seal();
|
|
646
567
|
return finalizeMdastVisit(handle, context, returnBuffer);
|
|
647
568
|
}
|
|
648
569
|
function finalizeMdastVisit(handle, context, returnBuffer) {
|