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
|
@@ -1,42 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { acquireCommandBuffer, releaseCommandBuffer, classifyReturn, CommandBuffer, STRUCTURAL_CMD, } from "../command-buffer.js";
|
|
1
|
+
import { mdastTextContentHandle, walkMdastHandle } from "#binding";
|
|
2
|
+
import { acquireCommandBuffer, classifyReturn, CommandBuffer, STRUCTURAL_CMD, } from "../command-buffer.js";
|
|
4
3
|
import { CMD_SET_CHILDREN } from "../generated/wire-constants.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
// Genuine stubs carry their id as a plain field; a spread copy is not
|
|
15
|
-
// `instanceof` and has no `_nodeId`, so it correctly reads as new content.
|
|
16
|
-
if (node instanceof MdastChildStub)
|
|
17
|
-
return node._refs === refs ? node._id : FOREIGN_REF;
|
|
18
|
-
const id = refs.get(node);
|
|
19
|
-
if (id !== undefined)
|
|
20
|
-
return id;
|
|
21
|
-
// Plain objects are trusted only via this tree's refs or a NON-enumerable
|
|
22
|
-
// `_nodeId` (the materializer's convention, which spread cannot copy) — an
|
|
23
|
-
// enumerable one rode in on a copy and must read as new content.
|
|
24
|
-
const d = Object.getOwnPropertyDescriptor(node, "_nodeId");
|
|
25
|
-
if (d !== undefined && !d.enumerable)
|
|
26
|
-
return FOREIGN_REF;
|
|
27
|
-
return crossPipelineForeign(node);
|
|
28
|
-
}
|
|
29
|
-
const requireNid = makeRequireNid(nid);
|
|
4
|
+
import { LazyChildResolver } from "../lazy-child-resolver.js";
|
|
5
|
+
import { emitMdxAttr, OF_ALT, OF_CHECKED, OF_DEPTH, OF_EXPLICIT, OF_IDENTIFIER, OF_LABEL, OF_LANG, OF_META, OF_NAME, OF_ORDERED, OF_REFERENCE_TYPE, OF_SPREAD, OF_START, OF_TITLE, OF_URL, OF_VALUE, OpWriter, PROP_STRING, } from "../op-stream.js";
|
|
6
|
+
import { asArray, collectCommands, FOREIGN_REF, foreignContentError, makeRequireNid, requireRootReplacement, ROOT_NODE_ID, rootReplacementError, unencodableContentError, withMdxJsxAttribute, } from "../visitor-shared.js";
|
|
7
|
+
import { ru32 } from "../wire-read.js";
|
|
8
|
+
import { MDAST_OPSTREAM_TYPES, NAME_TO_TYPE, VISITOR_KEYS } from "./generated/node-types.js";
|
|
9
|
+
import { LEAF_TYPES } from "./mdast-materializer.js";
|
|
10
|
+
import { MdastReader } from "./mdast-reader.js";
|
|
11
|
+
import { getNodeId, MdastLazyChildResolver, readMdastMatchedNode } from "./mdast-walk.js";
|
|
12
|
+
const requireNid = makeRequireNid(getNodeId);
|
|
30
13
|
export class MdastVisitorContext {
|
|
31
14
|
#commandBuffer = acquireCommandBuffer();
|
|
32
15
|
#diagnostics;
|
|
16
|
+
#pendingNodes = new Map();
|
|
33
17
|
#handle;
|
|
34
18
|
#getSource;
|
|
35
19
|
#resolver;
|
|
36
20
|
#refs;
|
|
37
|
-
/** One canonical object per parent id, so visitors can dedupe by identity.
|
|
38
|
-
* Null until the first `parent()` call; most passes never make one. */
|
|
39
|
-
#parentsById = null;
|
|
40
21
|
/**
|
|
41
22
|
* The URL of the document being processed (the compile `fileURL` option),
|
|
42
23
|
* or `undefined` when none was given. Use `fileURLToPath(ctx.fileURL)` for a
|
|
@@ -73,17 +54,20 @@ export class MdastVisitorContext {
|
|
|
73
54
|
return value;
|
|
74
55
|
}
|
|
75
56
|
removeNode(node) {
|
|
76
|
-
|
|
57
|
+
const id = requireNid(node, "removeNode", this.#refs);
|
|
58
|
+
this.#commandBuffer.removeNode(id);
|
|
59
|
+
this.#pendingNodes.delete(id);
|
|
77
60
|
}
|
|
78
61
|
insertBefore(node, newNode) {
|
|
79
|
-
|
|
80
|
-
for (const n of asArray(newNode))
|
|
81
|
-
emitMdastTree(this.#commandBuffer, "insertBefore", id, n, false, this.#refs);
|
|
62
|
+
this.#splice(requireNid(node, "insertBefore", this.#refs), newNode, "insertBefore");
|
|
82
63
|
}
|
|
83
64
|
insertAfter(node, newNode) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
65
|
+
this.#splice(requireNid(node, "insertAfter", this.#refs), newNode, "insertAfter");
|
|
66
|
+
}
|
|
67
|
+
#splice(anchorId, content, op) {
|
|
68
|
+
for (const n of asArray(content)) {
|
|
69
|
+
emitMdastTree(this.#commandBuffer, op, anchorId, n, false, this.#refs, true);
|
|
70
|
+
}
|
|
87
71
|
}
|
|
88
72
|
/**
|
|
89
73
|
* Wrap `node` in `parentNode`, making it `parentNode`'s first child. Any
|
|
@@ -99,27 +83,20 @@ export class MdastVisitorContext {
|
|
|
99
83
|
emitMdastTree(this.#commandBuffer, "wrapNode", id, parentNode, false, this.#refs);
|
|
100
84
|
}
|
|
101
85
|
prependChild(node, childNode) {
|
|
102
|
-
|
|
103
|
-
for (const n of asArray(childNode))
|
|
104
|
-
emitMdastTree(this.#commandBuffer, "prependChild", id, n, false, this.#refs);
|
|
86
|
+
this.#splice(requireNid(node, "prependChild", this.#refs), childNode, "prependChild");
|
|
105
87
|
}
|
|
106
88
|
appendChild(node, childNode) {
|
|
107
|
-
|
|
108
|
-
for (const n of asArray(childNode))
|
|
109
|
-
emitMdastTree(this.#commandBuffer, "appendChild", id, n, false, this.#refs);
|
|
89
|
+
this.#splice(requireNid(node, "appendChild", this.#refs), childNode, "appendChild");
|
|
110
90
|
}
|
|
111
91
|
/** Insert one node or an array at `index`; clamps (`0` or less prepends, past the end appends). */
|
|
112
92
|
insertChildAt(node, index, childNode) {
|
|
113
93
|
const children = ("children" in node ? node.children : undefined) ?? [];
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
else {
|
|
121
|
-
this.insertBefore(children[index], childNode);
|
|
122
|
-
}
|
|
94
|
+
const [anchor, op] = index <= 0 || children.length === 0
|
|
95
|
+
? [node, "prependChild"]
|
|
96
|
+
: index >= children.length
|
|
97
|
+
? [node, "appendChild"]
|
|
98
|
+
: [children[index], "insertBefore"];
|
|
99
|
+
this.#splice(requireNid(anchor, "insertChildAt", this.#refs), childNode, op);
|
|
123
100
|
}
|
|
124
101
|
/** Remove the `index`-th child of `node`; a no-op when there is no such child. */
|
|
125
102
|
removeChildAt(node, index) {
|
|
@@ -135,26 +112,46 @@ export class MdastVisitorContext {
|
|
|
135
112
|
*/
|
|
136
113
|
replaceNode(node, newNode) {
|
|
137
114
|
const id = requireNid(node, "replaceNode", this.#refs);
|
|
115
|
+
const onlyReplacement = Array.isArray(newNode)
|
|
116
|
+
? newNode.length === 1
|
|
117
|
+
? newNode[0]
|
|
118
|
+
: undefined
|
|
119
|
+
: newNode;
|
|
120
|
+
if (id === ROOT_NODE_ID &&
|
|
121
|
+
onlyReplacement !== undefined &&
|
|
122
|
+
reusedId(onlyReplacement, this.#refs) === id) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
138
125
|
if (Array.isArray(newNode)) {
|
|
139
126
|
if (id === ROOT_NODE_ID && newNode.length > 1)
|
|
140
127
|
throw rootReplacementError(newNode);
|
|
141
|
-
//
|
|
128
|
+
// One command, so the node's replacement is its whole slot and a ref back
|
|
129
|
+
// to it resolves to all of it rather than to the last element.
|
|
130
|
+
if (id !== ROOT_NODE_ID && newNode.length > 1 && newNode.every(isPlainReplacement)) {
|
|
131
|
+
emitMdastMultiReplace(this.#commandBuffer, id, newNode, this.#refs);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
142
134
|
let previous;
|
|
143
135
|
for (const n of newNode) {
|
|
144
136
|
if (previous !== undefined) {
|
|
145
|
-
emitMdastTree(this.#commandBuffer, "insertBefore", id, previous, false, this.#refs);
|
|
137
|
+
emitMdastTree(this.#commandBuffer, "insertBefore", id, previous, false, this.#refs, true);
|
|
146
138
|
}
|
|
147
139
|
previous = n;
|
|
148
140
|
}
|
|
149
141
|
if (previous === undefined) {
|
|
150
|
-
// Replacing with nothing drops the node, like removeNode.
|
|
151
142
|
this.removeNode(node);
|
|
152
143
|
}
|
|
153
144
|
else if (id === ROOT_NODE_ID && !isRawMdastContent(previous)) {
|
|
154
145
|
emitMdastRootReplace(this.#commandBuffer, requireRootReplacement(previous), this.#refs);
|
|
155
146
|
}
|
|
156
147
|
else {
|
|
157
|
-
emitMdastTree(this.#commandBuffer, "replace", id, previous, true, this.#refs);
|
|
148
|
+
emitMdastTree(this.#commandBuffer, "replace", id, previous, true, this.#refs, true);
|
|
149
|
+
}
|
|
150
|
+
if (previous !== undefined && !isRawMdastContent(previous)) {
|
|
151
|
+
this.#pendingNodes.set(id, previous);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
this.#pendingNodes.delete(id);
|
|
158
155
|
}
|
|
159
156
|
return;
|
|
160
157
|
}
|
|
@@ -162,24 +159,84 @@ export class MdastVisitorContext {
|
|
|
162
159
|
emitMdastRootReplace(this.#commandBuffer, requireRootReplacement(newNode), this.#refs);
|
|
163
160
|
return;
|
|
164
161
|
}
|
|
165
|
-
|
|
162
|
+
if (isRawMdastContent(newNode))
|
|
163
|
+
this.#pendingNodes.delete(id);
|
|
164
|
+
else
|
|
165
|
+
this.#pendingNodes.set(id, newNode);
|
|
166
|
+
emitMdastTree(this.#commandBuffer, "replace", id, newNode, true, this.#refs, true);
|
|
167
|
+
}
|
|
168
|
+
setField(node, key, value) {
|
|
169
|
+
const id = requireNid(node, "setField", this.#refs);
|
|
170
|
+
if (this.#foldPendingField(node, id, key, value))
|
|
171
|
+
return;
|
|
172
|
+
if (key === "children") {
|
|
173
|
+
if (!emitMdastChildrenCommand(this.#commandBuffer, id, value, this.#refs)) {
|
|
174
|
+
throw unencodableContentError(value);
|
|
175
|
+
}
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if ((key === "attributes" && (isMdxJsxElement(node) || isDirective(node))) ||
|
|
179
|
+
(key === "align" && node.type === "table")) {
|
|
180
|
+
// These are structured node fields, so re-encode the node rather than a scalar command.
|
|
181
|
+
this.replaceNode(node, { ...node, [key]: value });
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (key === "data")
|
|
185
|
+
value = value != null ? JSON.stringify(value) : null;
|
|
186
|
+
this.#commandBuffer.setField(id, key, value);
|
|
187
|
+
}
|
|
188
|
+
setAttribute(node, name, value) {
|
|
189
|
+
const id = requireNid(node, "setAttribute", this.#refs);
|
|
190
|
+
const target = this.#pendingNodes.get(id) ?? node;
|
|
191
|
+
if (isDirective(target) && typeof value !== "string") {
|
|
192
|
+
throw new TypeError("setAttribute: directive attributes must be strings");
|
|
193
|
+
}
|
|
194
|
+
if (this.#foldPendingAttribute(node, id, name, value))
|
|
195
|
+
return;
|
|
196
|
+
this.#commandBuffer.setAttribute(id, name, value);
|
|
197
|
+
}
|
|
198
|
+
/** A queued replacement would discard the field, so fold it into a fresh replacement. */
|
|
199
|
+
#foldPendingField(node, id, key, value) {
|
|
200
|
+
const pending = this.#pendingNodes.get(id);
|
|
201
|
+
if (pending === undefined)
|
|
202
|
+
return false;
|
|
203
|
+
this.replaceNode(node, { ...pending, [key]: value });
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
/** A queued replacement would discard the attribute, so fold it in instead. */
|
|
207
|
+
#foldPendingAttribute(node, id, name, value) {
|
|
208
|
+
const pending = this.#pendingNodes.get(id);
|
|
209
|
+
if (pending === undefined)
|
|
210
|
+
return false;
|
|
211
|
+
if (isDirective(pending)) {
|
|
212
|
+
this.replaceNode(node, {
|
|
213
|
+
...pending,
|
|
214
|
+
attributes: { ...pending.attributes, [name]: value },
|
|
215
|
+
});
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
if (isMdxJsxElement(pending)) {
|
|
219
|
+
this.replaceNode(node, withMdxJsxAttribute(pending, name, value));
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
return false;
|
|
166
223
|
}
|
|
167
224
|
setProperty(node, key, value) {
|
|
225
|
+
const id = requireNid(node, "setProperty", this.#refs);
|
|
168
226
|
if (key === "children") {
|
|
169
|
-
// children is structural: set-children keeps the node and swaps only its
|
|
170
|
-
// child list (reused children keep their id).
|
|
171
|
-
const id = requireNid(node, "setProperty", this.#refs);
|
|
172
227
|
if (!emitMdastChildrenCommand(this.#commandBuffer, id, value, this.#refs)) {
|
|
173
228
|
throw unencodableContentError(value);
|
|
174
229
|
}
|
|
175
230
|
return;
|
|
176
231
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
this
|
|
232
|
+
const target = this.#pendingNodes.get(id) ?? node;
|
|
233
|
+
if (key === "attributes" && isMdxJsxElement(target)) {
|
|
234
|
+
this.replaceNode(node, { ...target, attributes: value });
|
|
180
235
|
return;
|
|
181
236
|
}
|
|
182
|
-
|
|
237
|
+
if (key === "data")
|
|
238
|
+
value = value != null ? JSON.stringify(value) : null;
|
|
239
|
+
this.#commandBuffer.setProperty(id, key, value);
|
|
183
240
|
}
|
|
184
241
|
/** Collect the concatenated text of all descendant text nodes (like mdast-util-to-string). */
|
|
185
242
|
textContent(node, options) {
|
|
@@ -189,13 +246,7 @@ export class MdastVisitorContext {
|
|
|
189
246
|
const parentId = this.#resolver.parentIdOf(requireNid(node, "parent", this.#refs));
|
|
190
247
|
if (parentId === undefined)
|
|
191
248
|
return undefined;
|
|
192
|
-
|
|
193
|
-
let parent = byId.get(parentId);
|
|
194
|
-
if (parent === undefined) {
|
|
195
|
-
parent = this.#resolver.materializeOne(parentId);
|
|
196
|
-
byId.set(parentId, parent);
|
|
197
|
-
}
|
|
198
|
-
return parent;
|
|
249
|
+
return this.#resolver.materializeOne(parentId);
|
|
199
250
|
}
|
|
200
251
|
/**
|
|
201
252
|
* Index of `node` within its parent's children, or `undefined` at the root.
|
|
@@ -205,7 +256,7 @@ export class MdastVisitorContext {
|
|
|
205
256
|
return this.#resolver.indexInParent(requireNid(node, "indexOf", this.#refs));
|
|
206
257
|
}
|
|
207
258
|
report({ message, node, severity = "error", }) {
|
|
208
|
-
const id = node ?
|
|
259
|
+
const id = node ? getNodeId(node, this.#refs) : undefined;
|
|
209
260
|
this.#diagnostics.push({
|
|
210
261
|
message,
|
|
211
262
|
nodeId: id === FOREIGN_REF ? undefined : id,
|
|
@@ -213,7 +264,6 @@ export class MdastVisitorContext {
|
|
|
213
264
|
severity,
|
|
214
265
|
});
|
|
215
266
|
}
|
|
216
|
-
/** Get the binary command buffer for all mutations recorded via context methods. */
|
|
217
267
|
getCommandBuffer() {
|
|
218
268
|
return this.#commandBuffer;
|
|
219
269
|
}
|
|
@@ -254,169 +304,14 @@ function buildMdastSubscriptions(plugin) {
|
|
|
254
304
|
const rustSubs = subs.map((s) => ({ nodeType: s.nodeType, tagFilter: [] }));
|
|
255
305
|
return { subs, rustSubs };
|
|
256
306
|
}
|
|
257
|
-
const MDAST_EPOCH_CACHE = registerEpochCacheSlot(new WeakMap());
|
|
258
|
-
class MdastLazyChildResolver extends LazyChildResolver {
|
|
259
|
-
cacheSlot() {
|
|
260
|
-
return MDAST_EPOCH_CACHE;
|
|
261
|
-
}
|
|
262
|
-
createReader(wire) {
|
|
263
|
-
return new MdastReader(wire);
|
|
264
|
-
}
|
|
265
|
-
materializeNode(reader, nodeId, refs) {
|
|
266
|
-
return materializeNode(reader, nodeId, true, refs);
|
|
267
|
-
}
|
|
268
|
-
readParentId(reader, nodeId) {
|
|
269
|
-
return reader.getParentId(nodeId);
|
|
270
|
-
}
|
|
271
|
-
readChildIds(reader, nodeId) {
|
|
272
|
-
return reader.getChildIds(nodeId);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
/** Build the child-stub list for a matched node from the wire's `[child_ids]
|
|
276
|
-
* [child_types]` blocks, no arena snapshot. Stale ids are caught at
|
|
277
|
-
* materialization: the resolver's epoch check refuses a snapshot once the
|
|
278
|
-
* arena has mutated or been dropped. */
|
|
279
|
-
function readMdastChildStubs(view, buf, idsPos, typesPos, count, resolver) {
|
|
280
|
-
// With a hot snapshot a stub's deferral buys nothing; real nodes skip its per-field getters.
|
|
281
|
-
if (resolver.hasHotSnapshot()) {
|
|
282
|
-
const nodes = new Array(count);
|
|
283
|
-
for (let i = 0; i < count; i++) {
|
|
284
|
-
nodes[i] = resolver.materializeOne(ru32(view, idsPos + i * 4));
|
|
285
|
-
}
|
|
286
|
-
return nodes;
|
|
287
|
-
}
|
|
288
|
-
const stubs = new Array(count);
|
|
289
|
-
for (let i = 0; i < count; i++) {
|
|
290
|
-
stubs[i] = new MdastChildStub(resolver, ru32(view, idsPos + i * 4), buf[typesPos + i]);
|
|
291
|
-
}
|
|
292
|
-
return stubs;
|
|
293
|
-
}
|
|
294
|
-
/** Install `children` as an own enumerable getter (spread must carry it),
|
|
295
|
-
* self-replacing with the one stable stub array on first read. One closure
|
|
296
|
-
* and one define per node — installing the wire locals as hidden slots
|
|
297
|
-
* instead measurably regressed every matching pipeline. */
|
|
298
|
-
function makeLazyChildren(node, view, buf, childIdsPos, childTypesPos, childCount, resolver) {
|
|
299
|
-
Object.defineProperty(node, "children", {
|
|
300
|
-
get() {
|
|
301
|
-
const val = readMdastChildStubs(view, buf, childIdsPos, childTypesPos, childCount, resolver);
|
|
302
|
-
Object.defineProperty(this, "children", {
|
|
303
|
-
value: val,
|
|
304
|
-
writable: true,
|
|
305
|
-
enumerable: true,
|
|
306
|
-
configurable: true,
|
|
307
|
-
});
|
|
308
|
-
return val;
|
|
309
|
-
},
|
|
310
|
-
enumerable: true,
|
|
311
|
-
configurable: true,
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
/**
|
|
315
|
-
* Read an MDAST node from the inline data in a match buffer entry.
|
|
316
|
-
*
|
|
317
|
-
* Inline format (from Rust serialize_mdast_node_inline):
|
|
318
|
-
* [node_data: u32+bytes][position: 6×u32 = 24B][child_count: u32][child_ids: N×u32]
|
|
319
|
-
* [child_types: N×u8][type-specific data]
|
|
320
|
-
*/
|
|
321
|
-
function readMdastMatchedNode(view, buf, dataOffset, nodeId, nodeType, resolver) {
|
|
322
|
-
let pos = dataOffset;
|
|
323
|
-
const dataJsonLen = ru32(view, pos);
|
|
324
|
-
pos += 4;
|
|
325
|
-
let initialData = null;
|
|
326
|
-
if (dataJsonLen > 0) {
|
|
327
|
-
const jsonStr = rstr(buf, pos, dataJsonLen);
|
|
328
|
-
try {
|
|
329
|
-
initialData = JSON.parse(jsonStr);
|
|
330
|
-
}
|
|
331
|
-
catch (err) {
|
|
332
|
-
if (process.env.NODE_ENV !== "production") {
|
|
333
|
-
console.warn(`readMdastMatchedNode: malformed node_data for nodeId=${nodeId}`, err);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
pos += dataJsonLen;
|
|
337
|
-
}
|
|
338
|
-
const position = readPosition(view, pos);
|
|
339
|
-
pos += 24;
|
|
340
|
-
const childCount = ru32(view, pos);
|
|
341
|
-
pos += 4;
|
|
342
|
-
// Ids/types decode lazily with `.children` — most matched nodes never read them.
|
|
343
|
-
const childIdsPos = pos;
|
|
344
|
-
pos += childCount * 4;
|
|
345
|
-
const childTypesPos = pos;
|
|
346
|
-
pos += childCount;
|
|
347
|
-
const typeName = TYPE_NAMES[nodeType] ?? `unknown(${nodeType})`;
|
|
348
|
-
const node = { type: typeName };
|
|
349
|
-
if (position !== undefined)
|
|
350
|
-
node.position = position;
|
|
351
|
-
if (childCount > 0) {
|
|
352
|
-
makeLazyChildren(node, view, buf, childIdsPos, childTypesPos, childCount, resolver);
|
|
353
|
-
}
|
|
354
|
-
// Fixed-field types decode from the generated layout table; the rest
|
|
355
|
-
// (variable-length / cross-field) stay in the hand-written switch.
|
|
356
|
-
if (!decodeMdastTypeData(view, buf, pos, nodeType, node)) {
|
|
357
|
-
switch (nodeType) {
|
|
358
|
-
case 5: {
|
|
359
|
-
// list
|
|
360
|
-
node.start = ru32(view, pos);
|
|
361
|
-
node.ordered = buf[pos + 4] !== 0;
|
|
362
|
-
node.spread = buf[pos + 5] !== 0;
|
|
363
|
-
if (!node.ordered)
|
|
364
|
-
node.start = null;
|
|
365
|
-
break;
|
|
366
|
-
}
|
|
367
|
-
case 6: {
|
|
368
|
-
// listItem
|
|
369
|
-
const checked = buf[pos];
|
|
370
|
-
node.checked = checked === 2 ? null : checked === 1;
|
|
371
|
-
node.spread = buf[pos + 1] !== 0;
|
|
372
|
-
break;
|
|
373
|
-
}
|
|
374
|
-
case 37: {
|
|
375
|
-
// descriptionDetails
|
|
376
|
-
node.spread = buf[pos] !== 0;
|
|
377
|
-
break;
|
|
378
|
-
}
|
|
379
|
-
// table (21), directives (30/31/32) and mdxJsx elements (100/101) are
|
|
380
|
-
// decoded by the generated `decodeMdastTypeData` from their tails.
|
|
381
|
-
// root(0), paragraph(1), thematicBreak(3), blockquote(4), emphasis(11),
|
|
382
|
-
// strong(12), break(14), tableRow(22), tableCell(23), delete(24): no extra data
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
// User-defined node: the stored `name` field holds the author's public type
|
|
386
|
-
// string. Surface it as `node.type` (open type string) instead of the
|
|
387
|
-
// internal `"custom"`, drop the redundant `name`, and drop an empty `value`
|
|
388
|
-
// so a parent node isn't given a spurious leaf field.
|
|
389
|
-
if (nodeType === MDAST_CUSTOM) {
|
|
390
|
-
node.type = node.name;
|
|
391
|
-
delete node.name;
|
|
392
|
-
if (node.value === "")
|
|
393
|
-
delete node.value;
|
|
394
|
-
}
|
|
395
|
-
const leafCustom = nodeType === MDAST_CUSTOM && isCustomLeaf({ value: node.value, data: initialData }, childCount);
|
|
396
|
-
if (childCount === 0 && !LEAF_TYPES.has(nodeType) && !leafCustom) {
|
|
397
|
-
node.children = [];
|
|
398
|
-
}
|
|
399
|
-
resolver.refs.set(node, nodeId);
|
|
400
|
-
if (initialData) {
|
|
401
|
-
node.data = initialData;
|
|
402
|
-
}
|
|
403
|
-
return node;
|
|
404
|
-
}
|
|
405
307
|
const MDAST_ROOT = NAME_TO_TYPE.root;
|
|
406
|
-
/** Internal tag for user-defined nodes. The stored `name` field holds the
|
|
407
|
-
* author's public `type` string; the read paths surface it as `node.type`
|
|
408
|
-
* and the emit path routes any unrecognized `type` here. */
|
|
409
308
|
const MDAST_CUSTOM = NAME_TO_TYPE.custom;
|
|
410
|
-
/** The arena id of a node if it is an existing (materialized) node, else
|
|
411
|
-
* undefined for a freshly-built one. */
|
|
412
309
|
function reusedId(node, refs) {
|
|
413
310
|
if (node === null || typeof node !== "object")
|
|
414
311
|
return undefined;
|
|
415
|
-
const id =
|
|
312
|
+
const id = getNodeId(node, refs);
|
|
416
313
|
return id !== undefined && id !== FOREIGN_REF ? id : undefined;
|
|
417
314
|
}
|
|
418
|
-
/** Emit a set-children command in place: a root-wrapped child list, the shape
|
|
419
|
-
* `Patch::SetChildren` splices in. Reused children become refs. */
|
|
420
315
|
function emitMdastChildrenCommand(buffer, id, children, refs) {
|
|
421
316
|
if (!Array.isArray(children))
|
|
422
317
|
return false;
|
|
@@ -430,7 +325,25 @@ function emitMdastChildrenCommand(buffer, id, children, refs) {
|
|
|
430
325
|
return true;
|
|
431
326
|
});
|
|
432
327
|
}
|
|
433
|
-
/**
|
|
328
|
+
/** True for content the op-stream can carry inside one root-wrapped payload. */
|
|
329
|
+
function isPlainReplacement(content) {
|
|
330
|
+
return (!isRawMdastContent(content) && content._keepChildren !== true);
|
|
331
|
+
}
|
|
332
|
+
/** Replace `id` with several nodes in one command, root-wrapped so the engine
|
|
333
|
+
* splices the children in place of the node. */
|
|
334
|
+
function emitMdastMultiReplace(buffer, id, nodes, refs) {
|
|
335
|
+
const ok = buffer.emitOpstreamCommand(STRUCTURAL_CMD.replace, id, () => {
|
|
336
|
+
buffer.open(MDAST_ROOT);
|
|
337
|
+
for (const n of nodes)
|
|
338
|
+
if (!emitMdastOp(buffer, n, false, true, refs))
|
|
339
|
+
return false;
|
|
340
|
+
buffer.close();
|
|
341
|
+
return true;
|
|
342
|
+
});
|
|
343
|
+
if (!ok)
|
|
344
|
+
throw unencodableContentError(nodes);
|
|
345
|
+
}
|
|
346
|
+
// Root replacement needs a separate encoder because per-node encoding rejects root payloads.
|
|
434
347
|
function emitMdastRootReplace(buffer, root, refs) {
|
|
435
348
|
const ok = buffer.emitOpstreamCommand(STRUCTURAL_CMD.replace, ROOT_NODE_ID, () => emitMdastRootOp(buffer, root, refs));
|
|
436
349
|
if (!ok)
|
|
@@ -454,39 +367,33 @@ function emitMdastRootOp(w, n, refs) {
|
|
|
454
367
|
w.close();
|
|
455
368
|
return true;
|
|
456
369
|
}
|
|
457
|
-
function emitMdastOp(w, node, isRoot, forReplace, refs) {
|
|
370
|
+
function emitMdastOp(w, node, isRoot, forReplace, refs, allowRootRef = false) {
|
|
458
371
|
if (node === null || typeof node !== "object")
|
|
459
372
|
return false;
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
373
|
+
const id = getNodeId(node, refs);
|
|
374
|
+
if (id === FOREIGN_REF)
|
|
375
|
+
throw foreignContentError();
|
|
376
|
+
if ((!isRoot || allowRootRef) && id !== undefined) {
|
|
377
|
+
w.ref(id);
|
|
378
|
+
return true;
|
|
466
379
|
}
|
|
467
380
|
const n = node;
|
|
468
|
-
// Any `type` outside the built-in set is a user-defined node: route it to the
|
|
469
|
-
// internal `custom` tag and carry the author's `type` string as the name.
|
|
470
381
|
let type = MDAST_OPSTREAM_TYPES[n.type];
|
|
471
382
|
let isCustom = false;
|
|
472
383
|
if (type === undefined) {
|
|
473
384
|
if (typeof n.type !== "string" || n.type.length === 0)
|
|
474
385
|
return false;
|
|
475
|
-
//
|
|
476
|
-
// real type used wrong, so fail loudly rather than reinterpreting it as a
|
|
477
|
-
// user-defined node. Only genuinely-unknown type strings become custom.
|
|
386
|
+
// Unsupported built-in types must fail rather than silently becoming custom nodes.
|
|
478
387
|
if (NAME_TO_TYPE[n.type] !== undefined)
|
|
479
388
|
return false;
|
|
480
389
|
type = MDAST_CUSTOM;
|
|
481
390
|
isCustom = true;
|
|
482
391
|
}
|
|
483
392
|
else if (type === MDAST_CUSTOM) {
|
|
484
|
-
// `"custom"` is the internal tag's own public name, so it resolves here
|
|
485
|
-
// instead of falling through as unknown. Still a user-defined node, so carry
|
|
486
|
-
// the `type` string as the name so it round-trips rather than vanishing.
|
|
487
393
|
isCustom = true;
|
|
488
394
|
}
|
|
489
395
|
w.open(type);
|
|
396
|
+
w.position(n.position);
|
|
490
397
|
if (isCustom)
|
|
491
398
|
w.str(OF_NAME, n.type);
|
|
492
399
|
if (typeof n.value === "string")
|
|
@@ -507,8 +414,7 @@ function emitMdastOp(w, node, isRoot, forReplace, refs) {
|
|
|
507
414
|
w.str(OF_LABEL, n.label);
|
|
508
415
|
if (typeof n.referenceType === "string")
|
|
509
416
|
w.str(OF_REFERENCE_TYPE, n.referenceType);
|
|
510
|
-
//
|
|
511
|
-
// error instead of silently masking the bits.
|
|
417
|
+
// Reject out-of-range values before integer writes can truncate them.
|
|
512
418
|
if (typeof n.depth === "number") {
|
|
513
419
|
if (!Number.isInteger(n.depth) || n.depth < 0 || n.depth > 255)
|
|
514
420
|
return false;
|
|
@@ -533,8 +439,6 @@ function emitMdastOp(w, node, isRoot, forReplace, refs) {
|
|
|
533
439
|
emitMdxAttr(w, a);
|
|
534
440
|
}
|
|
535
441
|
else if (attrs !== null && typeof attrs === "object") {
|
|
536
|
-
// Directive attributes: a string→string map; non-string values are
|
|
537
|
-
// dropped, since the stored form holds only strings.
|
|
538
442
|
for (const key in attrs) {
|
|
539
443
|
const v = attrs[key];
|
|
540
444
|
if (typeof v === "string")
|
|
@@ -549,13 +453,10 @@ function emitMdastOp(w, node, isRoot, forReplace, refs) {
|
|
|
549
453
|
if (n.data != null)
|
|
550
454
|
w.data(n.data);
|
|
551
455
|
if (isRoot && forReplace && n._keepChildren === true) {
|
|
552
|
-
// Replace splices the target's original children, discarding any the
|
|
553
|
-
// replacement declares.
|
|
554
456
|
w.keepChildren();
|
|
555
457
|
}
|
|
556
458
|
else {
|
|
557
|
-
//
|
|
558
|
-
// and emit the declared children.
|
|
459
|
+
// Only replacement can reuse the target’s original children.
|
|
559
460
|
const children = n.children;
|
|
560
461
|
if (Array.isArray(children)) {
|
|
561
462
|
for (const c of children)
|
|
@@ -566,24 +467,23 @@ function emitMdastOp(w, node, isRoot, forReplace, refs) {
|
|
|
566
467
|
w.close();
|
|
567
468
|
return true;
|
|
568
469
|
}
|
|
569
|
-
/** Map a table `align` entry to its arena code (none=0). */
|
|
570
470
|
function alignCode(a) {
|
|
571
471
|
return a === "left" ? 1 : a === "right" ? 2 : a === "center" ? 3 : 0;
|
|
572
472
|
}
|
|
573
|
-
/** True for the `{raw}` / `{rawHtml}` escape hatches — re-parsed by Rust rather
|
|
574
|
-
* than compiled to an op-stream, so they ride the RAW_MARKDOWN / RAW_HTML
|
|
575
|
-
* payloads instead of the declarative encoder. */
|
|
576
473
|
function isRawMdastContent(content) {
|
|
577
474
|
const c = content;
|
|
578
475
|
return typeof c.raw === "string" || typeof c.rawHtml === "string";
|
|
579
476
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
477
|
+
function isDirective(node) {
|
|
478
|
+
return (node.type === "containerDirective" ||
|
|
479
|
+
node.type === "leafDirective" ||
|
|
480
|
+
node.type === "textDirective");
|
|
481
|
+
}
|
|
482
|
+
function isMdxJsxElement(node) {
|
|
483
|
+
return ((node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement") && "attributes" in node);
|
|
484
|
+
}
|
|
485
|
+
// Keep direct method calls so structural command dispatch stays monomorphic.
|
|
486
|
+
function emitMdastTree(buffer, op, id, content, forReplace, refs, allowRootRef = false) {
|
|
587
487
|
if (isRawMdastContent(content)) {
|
|
588
488
|
switch (op) {
|
|
589
489
|
case "replace":
|
|
@@ -600,7 +500,7 @@ function emitMdastTree(buffer, op, id, content, forReplace, refs) {
|
|
|
600
500
|
return buffer.wrapNode(id, content);
|
|
601
501
|
}
|
|
602
502
|
}
|
|
603
|
-
const ok = buffer.emitOpstreamCommand(STRUCTURAL_CMD[op], id, () => emitMdastOp(buffer, content, true, forReplace, refs));
|
|
503
|
+
const ok = buffer.emitOpstreamCommand(STRUCTURAL_CMD[op], id, () => emitMdastOp(buffer, content, true, forReplace, refs, allowRootRef));
|
|
604
504
|
if (!ok)
|
|
605
505
|
throw unencodableContentError(content);
|
|
606
506
|
}
|
|
@@ -613,8 +513,6 @@ function assertMdastWrapParent(parentNode) {
|
|
|
613
513
|
const type = parentNode.type;
|
|
614
514
|
const tag = typeof type === "string" ? NAME_TO_TYPE[type] : undefined;
|
|
615
515
|
if (tag === undefined) {
|
|
616
|
-
// User-defined type: only the declared shape distinguishes a parent from
|
|
617
|
-
// a text leaf.
|
|
618
516
|
if (Array.isArray(parentNode.children))
|
|
619
517
|
return;
|
|
620
518
|
throw new Error(`wrapNode: a user-defined "${String(type)}" wrapper must declare a children array. ` +
|
|
@@ -626,9 +524,7 @@ function assertMdastWrapParent(parentNode) {
|
|
|
626
524
|
'Pass a parent node such as { type: "blockquote", children: [] }, a raw wrapper ' +
|
|
627
525
|
`such as { raw: "> " }, or surround the node with tags: ${sandwich}.`);
|
|
628
526
|
}
|
|
629
|
-
|
|
630
|
-
* visitor returning one of these as `{type, value}` with no other fields
|
|
631
|
-
* skips a full arena rebuild. */
|
|
527
|
+
// Updating only value avoids an arena rebuild for these node types.
|
|
632
528
|
const MDAST_VALUE_ONLY_TYPES = new Set([
|
|
633
529
|
"text",
|
|
634
530
|
"html",
|
|
@@ -637,9 +533,7 @@ const MDAST_VALUE_ONLY_TYPES = new Set([
|
|
|
637
533
|
"toml",
|
|
638
534
|
"inlineMath",
|
|
639
535
|
]);
|
|
640
|
-
|
|
641
|
-
* + `value`. Any other field present (children, position, data, lang, meta)
|
|
642
|
-
* falls back to the full replace path so nothing is silently dropped. */
|
|
536
|
+
// Extra fields require full replacement so the value-only fast path cannot discard them.
|
|
643
537
|
function isMdastTextValueSwap(result, original) {
|
|
644
538
|
if (original === undefined)
|
|
645
539
|
return false;
|
|
@@ -656,8 +550,7 @@ function isMdastTextValueSwap(result, original) {
|
|
|
656
550
|
r.lang === undefined &&
|
|
657
551
|
r.meta === undefined);
|
|
658
552
|
}
|
|
659
|
-
|
|
660
|
-
* mutations (e.g. setProperty) are not clobbered. */
|
|
553
|
+
// Returning the input node must not overwrite mutations queued through the context.
|
|
661
554
|
function applyMdastVisitResult(result, nodeId, returnBuffer, refs, originalNode) {
|
|
662
555
|
if (result === undefined || result === null)
|
|
663
556
|
return;
|
|
@@ -677,14 +570,14 @@ function applyMdastVisitResult(result, nodeId, returnBuffer, refs, originalNode)
|
|
|
677
570
|
returnBuffer.setProperty(nodeId, "value", node.value);
|
|
678
571
|
break;
|
|
679
572
|
}
|
|
680
|
-
emitMdastTree(returnBuffer, "replace", nodeId, node, true, refs);
|
|
573
|
+
emitMdastTree(returnBuffer, "replace", nodeId, node, true, refs, true);
|
|
681
574
|
break;
|
|
682
575
|
}
|
|
683
576
|
}
|
|
684
577
|
}
|
|
685
578
|
/**
|
|
686
579
|
* Walk an MDAST handle in Rust, dispatch matched nodes to JS visitor functions,
|
|
687
|
-
* and
|
|
580
|
+
* and collect mutations for the caller to apply. No arena buffers cross NAPI.
|
|
688
581
|
*
|
|
689
582
|
* Returns MdastVisitResult synchronously if all visitors are sync,
|
|
690
583
|
* or Promise<MdastVisitResult> if any visitor is async.
|
|
@@ -716,20 +609,21 @@ export function visitMdastHandle(handle, plugin, subs, source, fileURL, data = {
|
|
|
716
609
|
}
|
|
717
610
|
}
|
|
718
611
|
if (deferred) {
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
612
|
+
const visits = deferred;
|
|
613
|
+
return Promise.all(visits.map((visit) => visit.promise)).then((results) => {
|
|
614
|
+
for (let i = 0; i < visits.length; i++) {
|
|
615
|
+
const { nodeId, originalNode } = visits[i];
|
|
616
|
+
applyMdastVisitResult(results[i], nodeId, returnBuffer, resolver.refs, originalNode);
|
|
722
617
|
}
|
|
723
|
-
return finalizeMdastVisit(
|
|
618
|
+
return finalizeMdastVisit(context, returnBuffer);
|
|
724
619
|
});
|
|
725
620
|
}
|
|
726
|
-
return finalizeMdastVisit(
|
|
621
|
+
return finalizeMdastVisit(context, returnBuffer);
|
|
727
622
|
}
|
|
728
623
|
const MDAST_ROOT_SUBS = [
|
|
729
624
|
{ nodeType: MDAST_ROOT, tagFilter: [] },
|
|
730
625
|
];
|
|
731
|
-
/**
|
|
732
|
-
* visitors walk, and the visitors' mutations before `after` reads the tree. */
|
|
626
|
+
/** Hooks run in separate passes so each sees mutations from the preceding pass. */
|
|
733
627
|
export function visitMdastHook(handle, plugin, hook, source, fileURL, data = {}, sourceFormat = "markdown", diagnostics = []) {
|
|
734
628
|
const getSource = typeof source === "function" ? source : () => source;
|
|
735
629
|
const resolver = new MdastLazyChildResolver(handle);
|
|
@@ -738,18 +632,19 @@ export function visitMdastHook(handle, plugin, hook, source, fileURL, data = {},
|
|
|
738
632
|
const matchBuf = walkMdastHandle(handle, MDAST_ROOT_SUBS);
|
|
739
633
|
const matchView = new DataView(matchBuf.buffer, matchBuf.byteOffset, matchBuf.byteLength);
|
|
740
634
|
if (ru32(matchView, 0) === 0)
|
|
741
|
-
return finalizeMdastVisit(
|
|
635
|
+
return finalizeMdastVisit(context, returnBuffer);
|
|
742
636
|
const root = readMdastMatchedNode(matchView, matchBuf, ru32(matchView, 10), ru32(matchView, 4), MDAST_ROOT, resolver);
|
|
743
637
|
const result = hook.call(plugin, root, context);
|
|
744
638
|
if (result instanceof Promise) {
|
|
745
|
-
return result.then(() => finalizeMdastVisit(
|
|
639
|
+
return result.then(() => finalizeMdastVisit(context, returnBuffer));
|
|
746
640
|
}
|
|
747
|
-
return finalizeMdastVisit(
|
|
641
|
+
return finalizeMdastVisit(context, returnBuffer);
|
|
748
642
|
}
|
|
749
|
-
function finalizeMdastVisit(
|
|
750
|
-
const
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
643
|
+
function finalizeMdastVisit(context, returnBuffer) {
|
|
644
|
+
const commandBuffer = collectCommands(returnBuffer, context.getCommandBuffer());
|
|
645
|
+
return {
|
|
646
|
+
commandBuffer,
|
|
647
|
+
diagnostics: context.getDiagnostics(),
|
|
648
|
+
hasMutations: commandBuffer.length > 0,
|
|
649
|
+
};
|
|
755
650
|
}
|