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
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { LazyChildResolver, type EpochCache } from "../lazy-child-resolver.js";
|
|
2
|
+
import { type NodeRefs } from "../visitor-shared.js";
|
|
3
|
+
import { type HastNode } from "./hast-materializer.js";
|
|
4
|
+
import { HastReader } from "./hast-reader.js";
|
|
5
|
+
export declare function getNodeId(node: HastNode, refs: NodeRefs): number | undefined;
|
|
6
|
+
export interface WalkWire {
|
|
7
|
+
view: DataView;
|
|
8
|
+
buf: Uint8Array;
|
|
9
|
+
resolver: HastLazyChildResolver;
|
|
10
|
+
}
|
|
11
|
+
export declare function readMatchedNode(wire: WalkWire, offset: number, nodeId: number, nodeType: number): HastNode;
|
|
12
|
+
export declare class HastLazyChildResolver extends LazyChildResolver<HastReader, HastNode> {
|
|
13
|
+
protected cacheSlot(): WeakMap<globalThis.AnyHandle, EpochCache<HastReader>>;
|
|
14
|
+
protected createReader(wire: Uint8Array): HastReader;
|
|
15
|
+
protected materializeNode(reader: HastReader, nodeId: number, refs: NodeRefs): HastNode;
|
|
16
|
+
protected readParentId(reader: HastReader, nodeId: number): number;
|
|
17
|
+
protected readChildIds(reader: HastReader, nodeId: number): number[];
|
|
18
|
+
}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { parseEsm as napiParseEsm, parseExpression as napiParseExpression } from "#binding";
|
|
2
|
+
import { LazyChildResolver, registerEpochCacheSlot, } from "../lazy-child-resolver.js";
|
|
3
|
+
import { VisitorNode } from "../visitor-node.js";
|
|
4
|
+
import { crossPipelineForeign, FOREIGN_REF } from "../visitor-shared.js";
|
|
5
|
+
import { readPosition, rstr } from "../wire-read.js";
|
|
6
|
+
import { HastChildStub } from "./child-stub.js";
|
|
7
|
+
import { TYPE_NAMES } from "./generated/node-types.js";
|
|
8
|
+
import { decodeWalkElementProps, readWalkElementTag, readWalkHastValue, readWalkMdxJsx, walkElementPropCount, walkElementPropsAt, } from "./generated/walk-decode.js";
|
|
9
|
+
import { materializeHastNode } from "./hast-materializer.js";
|
|
10
|
+
import { HAST_COMMENT, HAST_ELEMENT, HAST_MDX_ESM, HAST_MDX_FLOW_EXPRESSION, HAST_MDX_JSX_ELEMENT, HAST_MDX_JSX_TEXT_ELEMENT, HAST_MDX_TEXT_EXPRESSION, HAST_RAW, HAST_ROOT, HAST_TEXT, HastReader, } from "./hast-reader.js";
|
|
11
|
+
function attachParseExpression(node, parseFn) {
|
|
12
|
+
Object.defineProperty(node, "parseExpression", {
|
|
13
|
+
value() {
|
|
14
|
+
const value = this.value;
|
|
15
|
+
if (typeof value !== "string")
|
|
16
|
+
return null;
|
|
17
|
+
const json = parseFn(value);
|
|
18
|
+
if (json == null)
|
|
19
|
+
return null;
|
|
20
|
+
return JSON.parse(json);
|
|
21
|
+
},
|
|
22
|
+
writable: false,
|
|
23
|
+
enumerable: false,
|
|
24
|
+
configurable: true,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
// Spread copies must be treated as new content, not as references to the original arena node.
|
|
28
|
+
export function getNodeId(node, refs) {
|
|
29
|
+
if (node instanceof WalkElement)
|
|
30
|
+
return node._refs === refs ? node._nid : FOREIGN_REF;
|
|
31
|
+
if (node instanceof HastChildStub)
|
|
32
|
+
return node._refs === refs ? node._id : FOREIGN_REF;
|
|
33
|
+
const id = VisitorNode.getNodeId(node, refs) ?? refs.get(node);
|
|
34
|
+
if (id !== undefined)
|
|
35
|
+
return id;
|
|
36
|
+
const d = Object.getOwnPropertyDescriptor(node, "_nodeId");
|
|
37
|
+
if (d !== undefined && !d.enumerable)
|
|
38
|
+
return FOREIGN_REF;
|
|
39
|
+
return crossPipelineForeign(node);
|
|
40
|
+
}
|
|
41
|
+
function readChildStubs(view, buf, idsPos, typesPos, count, resolver) {
|
|
42
|
+
// With a hot snapshot a stub's deferral buys nothing; real nodes skip its per-field getters.
|
|
43
|
+
if (resolver.hasHotSnapshot()) {
|
|
44
|
+
const nodes = new Array(count);
|
|
45
|
+
for (let i = 0; i < count; i++) {
|
|
46
|
+
nodes[i] = resolver.materializeOne(view.getUint32(idsPos + i * 4, true));
|
|
47
|
+
}
|
|
48
|
+
return nodes;
|
|
49
|
+
}
|
|
50
|
+
const stubs = new Array(count);
|
|
51
|
+
for (let i = 0; i < count; i++) {
|
|
52
|
+
stubs[i] = new HastChildStub(resolver, view.getUint32(idsPos + i * 4, true), buf[typesPos + i]);
|
|
53
|
+
}
|
|
54
|
+
return stubs;
|
|
55
|
+
}
|
|
56
|
+
// Shared getters need a static block to access private wire fields.
|
|
57
|
+
let WALK_PROPS_DESC;
|
|
58
|
+
let WALK_CHILDREN_DESC;
|
|
59
|
+
// Private wire fields preserve spread identity without the GC cost of a WeakMap entry per element.
|
|
60
|
+
class WalkElement {
|
|
61
|
+
type = "element";
|
|
62
|
+
tagName;
|
|
63
|
+
#nodeId;
|
|
64
|
+
#wire;
|
|
65
|
+
#propsPos;
|
|
66
|
+
#childIdsPos;
|
|
67
|
+
#childTypesPos;
|
|
68
|
+
#childCount;
|
|
69
|
+
constructor(tagName, nodeId, wire, propsPos, propCount, childIdsPos, childTypesPos, childCount) {
|
|
70
|
+
this.tagName = tagName;
|
|
71
|
+
this.#nodeId = nodeId;
|
|
72
|
+
this.#wire = wire;
|
|
73
|
+
this.#propsPos = propsPos;
|
|
74
|
+
this.#childIdsPos = childIdsPos;
|
|
75
|
+
this.#childTypesPos = childTypesPos;
|
|
76
|
+
this.#childCount = childCount;
|
|
77
|
+
if (propCount === 0) {
|
|
78
|
+
this.properties = {};
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
Object.defineProperty(this, "properties", WALK_PROPS_DESC);
|
|
82
|
+
}
|
|
83
|
+
if (childCount === 0) {
|
|
84
|
+
this.children = [];
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
Object.defineProperty(this, "children", WALK_CHILDREN_DESC);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** @internal */
|
|
91
|
+
get _nid() {
|
|
92
|
+
return this.#nodeId;
|
|
93
|
+
}
|
|
94
|
+
/** @internal */
|
|
95
|
+
get _refs() {
|
|
96
|
+
return this.#wire.resolver.refs;
|
|
97
|
+
}
|
|
98
|
+
static {
|
|
99
|
+
WALK_PROPS_DESC = {
|
|
100
|
+
enumerable: true,
|
|
101
|
+
configurable: true,
|
|
102
|
+
get() {
|
|
103
|
+
const w = this.#wire;
|
|
104
|
+
const val = decodeWalkElementProps(w.view, w.buf, this.#propsPos);
|
|
105
|
+
Object.defineProperty(this, "properties", {
|
|
106
|
+
value: val,
|
|
107
|
+
writable: true,
|
|
108
|
+
enumerable: true,
|
|
109
|
+
configurable: true,
|
|
110
|
+
});
|
|
111
|
+
return val;
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
WALK_CHILDREN_DESC = {
|
|
115
|
+
enumerable: true,
|
|
116
|
+
configurable: true,
|
|
117
|
+
get() {
|
|
118
|
+
const w = this.#wire;
|
|
119
|
+
const val = readChildStubs(w.view, w.buf, this.#childIdsPos, this.#childTypesPos, this.#childCount, w.resolver);
|
|
120
|
+
Object.defineProperty(this, "children", {
|
|
121
|
+
value: val,
|
|
122
|
+
writable: true,
|
|
123
|
+
enumerable: true,
|
|
124
|
+
configurable: true,
|
|
125
|
+
});
|
|
126
|
+
return val;
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function readElementFromBinary(wire, offset, nodeId, position, childIdsPos, childTypesPos, childCount, data) {
|
|
132
|
+
// Visitors almost always read tagName, so decode it eagerly.
|
|
133
|
+
const tagName = readWalkElementTag(wire.view, wire.buf, offset);
|
|
134
|
+
const pos = walkElementPropsAt(wire.view, offset);
|
|
135
|
+
const propCount = walkElementPropCount(wire.view, pos);
|
|
136
|
+
const node = new WalkElement(tagName, nodeId, wire, pos, propCount, childIdsPos, childTypesPos, childCount);
|
|
137
|
+
if (position !== undefined)
|
|
138
|
+
node.position = position;
|
|
139
|
+
if (data !== null)
|
|
140
|
+
node.data = data;
|
|
141
|
+
return node;
|
|
142
|
+
}
|
|
143
|
+
function readTextFromBinary(view, buf, offset, nodeId, nodeType, position, data, refs) {
|
|
144
|
+
const value = readWalkHastValue(view, buf, offset, nodeType);
|
|
145
|
+
const base = new VisitorNode(TYPE_NAMES[nodeType] ?? `unknown(${nodeType})`, refs, nodeId);
|
|
146
|
+
base.value = value;
|
|
147
|
+
if (position !== undefined)
|
|
148
|
+
base.position = position;
|
|
149
|
+
if (data !== null)
|
|
150
|
+
base.data = data;
|
|
151
|
+
const node = base;
|
|
152
|
+
if (nodeType === HAST_MDX_FLOW_EXPRESSION || nodeType === HAST_MDX_TEXT_EXPRESSION) {
|
|
153
|
+
attachParseExpression(node, napiParseExpression);
|
|
154
|
+
}
|
|
155
|
+
else if (nodeType === HAST_MDX_ESM) {
|
|
156
|
+
attachParseExpression(node, napiParseEsm);
|
|
157
|
+
}
|
|
158
|
+
return node;
|
|
159
|
+
}
|
|
160
|
+
function readMdxJsxFromBinary(view, buf, offset, nodeId, nodeType, resolver, position, childIdsPos, childTypesPos, childCount, data) {
|
|
161
|
+
const { name, attributes } = readWalkMdxJsx(view, buf, offset);
|
|
162
|
+
const typeName = nodeType === HAST_MDX_JSX_ELEMENT ? "mdxJsxFlowElement" : "mdxJsxTextElement";
|
|
163
|
+
const base = new VisitorNode(typeName, resolver.refs, nodeId);
|
|
164
|
+
base.name = name;
|
|
165
|
+
base.attributes = attributes;
|
|
166
|
+
if (position !== undefined)
|
|
167
|
+
base.position = position;
|
|
168
|
+
if (data !== null)
|
|
169
|
+
base.data = data;
|
|
170
|
+
makeLazyChildren(base, view, buf, childIdsPos, childTypesPos, childCount, resolver);
|
|
171
|
+
return base;
|
|
172
|
+
}
|
|
173
|
+
export function readMatchedNode(wire, offset, nodeId, nodeType) {
|
|
174
|
+
const { view, buf, resolver } = wire;
|
|
175
|
+
let pos = offset;
|
|
176
|
+
const dataLen = view.getUint32(pos, true);
|
|
177
|
+
pos += 4;
|
|
178
|
+
let data = null;
|
|
179
|
+
if (dataLen > 0) {
|
|
180
|
+
const jsonStr = rstr(buf, pos, dataLen);
|
|
181
|
+
try {
|
|
182
|
+
data = JSON.parse(jsonStr);
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
if (process.env.NODE_ENV !== "production") {
|
|
186
|
+
console.warn(`readMatchedNode: malformed node_data for nodeId=${nodeId}`, err);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
pos += dataLen;
|
|
190
|
+
}
|
|
191
|
+
const position = readPosition(view, pos);
|
|
192
|
+
pos += 24;
|
|
193
|
+
const childCount = view.getUint32(pos, true);
|
|
194
|
+
pos += 4;
|
|
195
|
+
// Ids/types decode lazily with `.children` — most matched nodes never read them.
|
|
196
|
+
const childIdsPos = pos;
|
|
197
|
+
pos += childCount * 4;
|
|
198
|
+
const childTypesPos = pos;
|
|
199
|
+
pos += childCount;
|
|
200
|
+
if (nodeType === HAST_ELEMENT) {
|
|
201
|
+
return readElementFromBinary(wire, pos, nodeId, position, childIdsPos, childTypesPos, childCount, data);
|
|
202
|
+
}
|
|
203
|
+
else if (nodeType === HAST_TEXT ||
|
|
204
|
+
nodeType === HAST_COMMENT ||
|
|
205
|
+
nodeType === HAST_RAW ||
|
|
206
|
+
nodeType === HAST_MDX_FLOW_EXPRESSION ||
|
|
207
|
+
nodeType === HAST_MDX_TEXT_EXPRESSION ||
|
|
208
|
+
nodeType === HAST_MDX_ESM) {
|
|
209
|
+
return readTextFromBinary(view, buf, pos, nodeId, nodeType, position, data, resolver.refs);
|
|
210
|
+
}
|
|
211
|
+
else if (nodeType === HAST_MDX_JSX_ELEMENT || nodeType === HAST_MDX_JSX_TEXT_ELEMENT) {
|
|
212
|
+
return readMdxJsxFromBinary(view, buf, pos, nodeId, nodeType, resolver, position, childIdsPos, childTypesPos, childCount, data);
|
|
213
|
+
}
|
|
214
|
+
const base = new VisitorNode(TYPE_NAMES[nodeType] ?? `unknown(${nodeType})`, resolver.refs, nodeId);
|
|
215
|
+
if (position !== undefined)
|
|
216
|
+
base.position = position;
|
|
217
|
+
if (data !== null)
|
|
218
|
+
base.data = data;
|
|
219
|
+
if (nodeType === HAST_ROOT) {
|
|
220
|
+
if (childCount > 0) {
|
|
221
|
+
makeLazyChildren(base, view, buf, childIdsPos, childTypesPos, childCount, resolver);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
base.children = [];
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const node = base;
|
|
228
|
+
return node;
|
|
229
|
+
}
|
|
230
|
+
const HAST_EPOCH_CACHE = registerEpochCacheSlot(new WeakMap());
|
|
231
|
+
export class HastLazyChildResolver extends LazyChildResolver {
|
|
232
|
+
cacheSlot() {
|
|
233
|
+
return HAST_EPOCH_CACHE;
|
|
234
|
+
}
|
|
235
|
+
createReader(wire) {
|
|
236
|
+
return new HastReader(wire);
|
|
237
|
+
}
|
|
238
|
+
materializeNode(reader, nodeId, refs) {
|
|
239
|
+
return materializeHastNode(reader, nodeId, true, refs);
|
|
240
|
+
}
|
|
241
|
+
readParentId(reader, nodeId) {
|
|
242
|
+
return reader.getParentId(nodeId);
|
|
243
|
+
}
|
|
244
|
+
readChildIds(reader, nodeId) {
|
|
245
|
+
return reader.getChildIds(nodeId);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// Own enumerable getters preserve children in spreads; closures avoid costly per-node wire slots.
|
|
249
|
+
function makeLazyChildren(node, view, buf, childIdsPos, childTypesPos, childCount, resolver) {
|
|
250
|
+
Object.defineProperty(node, "children", {
|
|
251
|
+
get() {
|
|
252
|
+
const val = readChildStubs(view, buf, childIdsPos, childTypesPos, childCount, resolver);
|
|
253
|
+
Object.defineProperty(this, "children", {
|
|
254
|
+
value: val,
|
|
255
|
+
writable: true,
|
|
256
|
+
enumerable: true,
|
|
257
|
+
configurable: true,
|
|
258
|
+
});
|
|
259
|
+
return val;
|
|
260
|
+
},
|
|
261
|
+
enumerable: true,
|
|
262
|
+
configurable: true,
|
|
263
|
+
});
|
|
264
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
export { markdownToHtml, markdownToJs, mdxToJs, evaluate, markdownToMdast, mdxToMdast, markdownToHast, mdxToHast, htmlToHast, } from "./compile.js";
|
|
2
|
-
export type { CompileOptions, MdxCompileOptions, MdxOnlyOptions, MarkdownToJsOptions, EvaluateOptions, OptimizeStaticConfig, Features, TreeOptions, HtmlToHastOptions, SmartPunctuationOptions, Frontmatter, MarkdownToHtmlResult, MarkdownToJsResult, MdxToJsResult, } from "./compile.js";
|
|
1
|
+
export { markdownToHtml, markdownToJs, mdxToJs, evaluate, markdownToMdast, mdxToMdast, markdownToHast, mdxToHast, htmlToHast, hastToHtml, } from "./compile.js";
|
|
2
|
+
export type { CompileOptions, MdxCompileOptions, MdxOnlyOptions, MarkdownToJsOptions, EvaluateOptions, OptimizeStaticConfig, Features, TreeOptions, MdastTreeOptions, HastTreeOptions, HtmlToHastOptions, SmartPunctuationOptions, Frontmatter, MarkdownToHtmlResult, MarkdownToJsResult, MdxToJsResult, } from "./compile.js";
|
|
3
3
|
export { defineMdastPlugin, defineHastPlugin } from "./plugin.js";
|
|
4
4
|
export type { MdastPluginDefinition, HastPluginDefinition, MdastPluginInput, HastPluginInput, MdastPluginEntry, HastPluginEntry, MdastPluginList, HastPluginList, PluginFactoryContext, } from "./plugin.js";
|
|
5
|
-
export type { HastVisitorInstance, HastVisitorContext, HastFilteredVisitor, HastContent, HastParentContent, RawHastContent, RawHtmlHastContent, EstreeProgram, } from "./hast/hast-visitor.js";
|
|
6
|
-
export type { MdastNode, HastNode, Custom, DataMap, Data, SourceFormat, Position, Point, MdxJsxAttributeNode, MdxJsxExpressionAttributeNode, MdxJsxAttributeValueExpressionNode, MdxJsxAttributeUnion, MdxJsxFlowElement, MdxJsxTextElement, MdxFlowExpression, MdxTextExpression, MdxjsEsm, MdxJsxFlowElementHast, MdxJsxTextElementHast, MdxFlowExpressionHast, MdxTextExpressionHast, MdxjsEsmHast, } from "./types.js";
|
|
5
|
+
export type { HastVisitorInstance, HastVisitorContext, HastFilteredVisitor, HastVisitorFn, HastContent, HastParentContent, RawHastContent, RawHtmlHastContent, EstreeProgram, } from "./hast/hast-visitor.js";
|
|
6
|
+
export type { MdastNode, HastNode, Custom, DataMap, Data, SourceFormat, Position, Point, MdxJsxAttributeNode, MdxJsxExpressionAttributeNode, MdxJsxAttributeValueExpressionNode, MdxJsxAttributeUnion, MdxJsxFlowElement, MdxJsxTextElement, MdxFlowExpression, MdxTextExpression, MdxjsEsm, MdxJsxFlowElementHast, MdxJsxTextElementHast, MdxFlowExpressionHast, MdxTextExpressionHast, MdxjsEsmHast, ContainerDirective, LeafDirective, TextDirective, DirectiveAttributes, Toml, MathNode, InlineMath, Superscript, Subscript, DescriptionList, DescriptionTerm, DescriptionDetails, HastRaw, } from "./types.js";
|
|
7
7
|
export { normalizePlugins } from "./plugin.js";
|
|
8
8
|
export { visitMdastHandle, visitMdastHook, resolveMdastSubscriptions, } from "./mdast/mdast-visitor.js";
|
|
9
|
-
export type { MdastPluginInstance, MdastVisitorContext, MdastContent, MdastTarget, MdastParentContent, MdastDiagnostic, MdastHookFn, RawMdastContent, RawHtmlMdastContent, } from "./mdast/mdast-visitor.js";
|
|
9
|
+
export type { MdastPluginInstance, MdastVisitorContext, MdastVisitorFn, MdastContent, MdastTarget, MdastParentContent, MdastDiagnostic, MdastHookFn, RawMdastContent, RawHtmlMdastContent, } from "./mdast/mdast-visitor.js";
|
|
10
10
|
export { visitHastHandle, visitHastHook, resolveSubscriptions as resolveHastSubscriptions, } from "./hast/hast-visitor.js";
|
|
11
11
|
export type { HastDiagnostic, HastHookFn } from "./hast/hast-visitor.js";
|
|
12
12
|
export { MdastReader } from "./mdast/mdast-reader.js";
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
export { markdownToHtml, markdownToJs, mdxToJs, evaluate, markdownToMdast, mdxToMdast, markdownToHast, mdxToHast, htmlToHast, } from "./compile.js";
|
|
3
|
-
// Plugin definitions
|
|
1
|
+
export { markdownToHtml, markdownToJs, mdxToJs, evaluate, markdownToMdast, mdxToMdast, markdownToHast, mdxToHast, htmlToHast, hastToHtml, } from "./compile.js";
|
|
4
2
|
export { defineMdastPlugin, defineHastPlugin } from "./plugin.js";
|
|
5
|
-
// Visitor pipeline (for manual plugin execution)
|
|
6
3
|
export { normalizePlugins } from "./plugin.js";
|
|
7
4
|
export { visitMdastHandle, visitMdastHook, resolveMdastSubscriptions, } from "./mdast/mdast-visitor.js";
|
|
8
5
|
export { visitHastHandle, visitHastHook, resolveSubscriptions as resolveHastSubscriptions, } from "./hast/hast-visitor.js";
|
|
9
|
-
// Step-by-step API: readers, materializers, and handle functions
|
|
10
6
|
export { MdastReader } from "./mdast/mdast-reader.js";
|
|
11
7
|
export { materializeMdastTree } from "./mdast/mdast-materializer.js";
|
|
12
8
|
export { HastReader } from "./hast/hast-reader.js";
|
|
@@ -14,8 +10,7 @@ export { materializeHastTree } from "./hast/hast-materializer.js";
|
|
|
14
10
|
export { serializeHandle, renderHandle, compileHandle, getHandleSource } from "#binding";
|
|
15
11
|
import { applyCommandsToMdastHandle as napiApplyCommandsToMdastHandle, applyCommandsAndConvertToHastHandle as napiApplyCommandsAndConvertToHastHandle, convertMdastToHastHandle as napiConvertMdastToHastHandle, createHastHandle as napiCreateHastHandle, createMdastHandle as napiCreateMdastHandle, createMdxHastHandle as napiCreateMdxHastHandle, createMdxMdastHandle as napiCreateMdxMdastHandle, dropHandle as napiDropHandle, } from "#binding";
|
|
16
12
|
import { featuresToNative } from "./compile.js";
|
|
17
|
-
import { markHandleMutated } from "./lazy-child-resolver.js";
|
|
18
|
-
// The napi creators take pre-packed parser bits; these keep `Features` the public shape.
|
|
13
|
+
import { markHandleMutated, releaseHandleReferences } from "./lazy-child-resolver.js";
|
|
19
14
|
export function createMdastHandle(source, features, trackPositions) {
|
|
20
15
|
return napiCreateMdastHandle(source, featuresToNative(features).parseOptions, trackPositions);
|
|
21
16
|
}
|
|
@@ -37,10 +32,7 @@ function mergeConvertOptions(fromFeatures, explicit) {
|
|
|
37
32
|
return fromFeatures;
|
|
38
33
|
return { ...fromFeatures, ...explicit };
|
|
39
34
|
}
|
|
40
|
-
//
|
|
41
|
-
// child stub retained past a manual-pipeline pass would silently snapshot the
|
|
42
|
-
// changed arena (or die with an opaque RangeError) instead of hitting the
|
|
43
|
-
// retention error.
|
|
35
|
+
// Arena mutations invalidate retained child stubs because node IDs can be reassigned or freed.
|
|
44
36
|
export function applyCommandsToMdastHandle(handle, commandBuf) {
|
|
45
37
|
markHandleMutated(handle);
|
|
46
38
|
return napiApplyCommandsToMdastHandle(handle, commandBuf);
|
|
@@ -50,7 +42,7 @@ export function convertMdastToHastHandle(handle, convertOptions) {
|
|
|
50
42
|
return napiConvertMdastToHastHandle(handle, convertOptions);
|
|
51
43
|
}
|
|
52
44
|
export function dropHandle(handle) {
|
|
53
|
-
|
|
45
|
+
releaseHandleReferences(handle);
|
|
54
46
|
napiDropHandle(handle);
|
|
55
47
|
}
|
|
56
48
|
export function applyCommandsAndConvertToHastHandle(handle, commandBuf, convertOptions) {
|
|
@@ -1,23 +1,12 @@
|
|
|
1
1
|
import type { NodeRefs } from "./visitor-shared.js";
|
|
2
2
|
import type { AnyHandle } from "./handles.js";
|
|
3
3
|
export declare function registerEpochCacheSlot<T extends object>(slot: WeakMap<AnyHandle, T>): WeakMap<AnyHandle, T>;
|
|
4
|
-
/** Record that `handle`'s arena was rebuilt. Resolvers created before the bump
|
|
5
|
-
* refuse to take a fresh snapshot afterwards (their ids are stale). */
|
|
6
4
|
export declare function markHandleMutated(handle: AnyHandle): void;
|
|
7
|
-
|
|
8
|
-
* by every resolver of the same pass chain: nested matched containers dedup
|
|
9
|
-
* by id, and later read-only passes reuse the whole cache instead of
|
|
10
|
-
* re-serializing and re-materializing. */
|
|
5
|
+
export declare function releaseHandleReferences(handle: AnyHandle): void;
|
|
11
6
|
export interface EpochCache<TReader> {
|
|
12
7
|
epoch: number;
|
|
13
8
|
reader: TReader;
|
|
14
9
|
}
|
|
15
|
-
/**
|
|
16
|
-
* Lazy node materializer for the walk paths: serializes the handle's arena
|
|
17
|
-
* once, on the first stub materialization, then materializes nodes from that
|
|
18
|
-
* snapshot. Subclasses supply reader construction and per-node materialization
|
|
19
|
-
* so the hot path stays free of per-node closures.
|
|
20
|
-
*/
|
|
21
10
|
export declare abstract class LazyChildResolver<TReader, TNode> {
|
|
22
11
|
#private;
|
|
23
12
|
readonly refs: NodeRefs;
|
|
@@ -26,18 +15,9 @@ export declare abstract class LazyChildResolver<TReader, TNode> {
|
|
|
26
15
|
protected abstract materializeNode(reader: TReader, nodeId: number, refs: NodeRefs): TNode;
|
|
27
16
|
protected abstract readParentId(reader: TReader, nodeId: number): number;
|
|
28
17
|
protected abstract readChildIds(reader: TReader, nodeId: number): number[];
|
|
29
|
-
/** Kind-specific `(handle → cache)` slot, supplied as a module-level WeakMap
|
|
30
|
-
* by each subclass so MDAST and HAST snapshots never collide. */
|
|
31
18
|
protected abstract cacheSlot(): WeakMap<AnyHandle, EpochCache<TReader>>;
|
|
32
|
-
/** Whether this pass's snapshot already exists; never takes one itself. */
|
|
33
19
|
hasHotSnapshot(): boolean;
|
|
34
|
-
/** Materialize one node for a child stub's first real-field read. The
|
|
35
|
-
* materializers memoize per `(reader, id)`, so overlapping subtrees and
|
|
36
|
-
* later passes share the same materialized objects. */
|
|
37
20
|
materializeOne(nodeId: number): TNode;
|
|
38
|
-
/** Arena id of `nodeId`'s parent in the pass snapshot, or undefined at the root. */
|
|
39
21
|
parentIdOf(nodeId: number): number | undefined;
|
|
40
|
-
/** Index of `nodeId` within its parent's children in the pass snapshot,
|
|
41
|
-
* or undefined at the root. */
|
|
42
22
|
indexInParent(nodeId: number): number | undefined;
|
|
43
23
|
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { serializeHandle } from "#binding";
|
|
2
|
-
|
|
3
|
-
* renumbers the arena, invalidating ids captured before it. */
|
|
2
|
+
// Arena rebuilds can renumber nodes, invalidating IDs captured by earlier passes.
|
|
4
3
|
const HANDLE_EPOCHS = new WeakMap();
|
|
5
|
-
|
|
4
|
+
// Registered cache slots let mutations evict snapshots that handles would otherwise retain.
|
|
6
5
|
const EPOCH_CACHE_SLOTS = [];
|
|
7
6
|
export function registerEpochCacheSlot(slot) {
|
|
8
7
|
EPOCH_CACHE_SLOTS.push(slot);
|
|
9
8
|
return slot;
|
|
10
9
|
}
|
|
11
|
-
|
|
10
|
+
// Ownership is per handle; epoch checks separately reject stale IDs within the same handle.
|
|
12
11
|
const HANDLE_NODE_REFS = new WeakMap();
|
|
13
12
|
function nodeRefsOfHandle(handle) {
|
|
14
13
|
let refs = HANDLE_NODE_REFS.get(handle);
|
|
@@ -18,23 +17,19 @@ function nodeRefsOfHandle(handle) {
|
|
|
18
17
|
}
|
|
19
18
|
return refs;
|
|
20
19
|
}
|
|
21
|
-
/** Record that `handle`'s arena was rebuilt. Resolvers created before the bump
|
|
22
|
-
* refuse to take a fresh snapshot afterwards (their ids are stale). */
|
|
23
20
|
export function markHandleMutated(handle) {
|
|
24
21
|
HANDLE_EPOCHS.set(handle, (HANDLE_EPOCHS.get(handle) ?? 0) + 1);
|
|
25
|
-
// Safe to evict: a stale-epoch entry can never be adopted, only pinned by existing resolvers
|
|
26
22
|
for (const slot of EPOCH_CACHE_SLOTS) {
|
|
27
23
|
slot.delete(handle);
|
|
28
24
|
}
|
|
29
25
|
}
|
|
30
|
-
|
|
26
|
+
export function releaseHandleReferences(handle) {
|
|
27
|
+
markHandleMutated(handle);
|
|
28
|
+
// Retained contexts keep their own refs; do not keep the table alive until native handle GC.
|
|
29
|
+
HANDLE_NODE_REFS.delete(handle);
|
|
30
|
+
}
|
|
31
31
|
const NO_PARENT = 0xffffffff;
|
|
32
|
-
|
|
33
|
-
* Lazy node materializer for the walk paths: serializes the handle's arena
|
|
34
|
-
* once, on the first stub materialization, then materializes nodes from that
|
|
35
|
-
* snapshot. Subclasses supply reader construction and per-node materialization
|
|
36
|
-
* so the hot path stays free of per-node closures.
|
|
37
|
-
*/
|
|
32
|
+
// Subclasses keep the per-node materialization path free of closures.
|
|
38
33
|
export class LazyChildResolver {
|
|
39
34
|
#handle;
|
|
40
35
|
#epoch;
|
|
@@ -56,21 +51,13 @@ export class LazyChildResolver {
|
|
|
56
51
|
this.#cache = cache;
|
|
57
52
|
return cache;
|
|
58
53
|
}
|
|
59
|
-
//
|
|
60
|
-
// still faithful as long as no command buffer mutated the arena since
|
|
61
|
-
// match time. An existing same-epoch cache is always safe: the snapshot
|
|
62
|
-
// is an immutable copy.
|
|
54
|
+
// Snapshot only within the captured epoch; later arenas may assign these IDs to different nodes.
|
|
63
55
|
if ((HANDLE_EPOCHS.get(this.#handle) ?? 0) !== this.#epoch) {
|
|
64
56
|
throw new Error("Cannot read node content: this node was retained past its visitor pass " +
|
|
65
57
|
"and the tree has changed since. Reading a child node's field (or calling " +
|
|
66
58
|
"ctx.parent()) during the pass pins the pass snapshot; eager fields like " +
|
|
67
59
|
"tagName or properties do not. Or copy the data you need before the pass ends.");
|
|
68
60
|
}
|
|
69
|
-
// The serialized buffer already carries each node's `data` blob (read
|
|
70
|
-
// eagerly by the materializer), and the arena isn't mutated mid-visit,
|
|
71
|
-
// so no separate lazy NAPI fetch is needed. This also keeps walk-path
|
|
72
|
-
// children consistent with the fully materialized tree (no `data` key
|
|
73
|
-
// when a node has none).
|
|
74
61
|
cache = {
|
|
75
62
|
epoch: this.#epoch,
|
|
76
63
|
reader: this.createReader(serializeHandle(this.#handle)),
|
|
@@ -82,29 +69,21 @@ export class LazyChildResolver {
|
|
|
82
69
|
#ensureReader() {
|
|
83
70
|
return this.#ensureCache().reader;
|
|
84
71
|
}
|
|
85
|
-
/** Whether this pass's snapshot already exists; never takes one itself. */
|
|
86
72
|
hasHotSnapshot() {
|
|
87
73
|
if (this.#cache !== undefined)
|
|
88
74
|
return true;
|
|
89
75
|
const cache = this.cacheSlot().get(this.#handle);
|
|
90
76
|
return cache !== undefined && cache.epoch === this.#epoch;
|
|
91
77
|
}
|
|
92
|
-
/** Materialize one node for a child stub's first real-field read. The
|
|
93
|
-
* materializers memoize per `(reader, id)`, so overlapping subtrees and
|
|
94
|
-
* later passes share the same materialized objects. */
|
|
95
78
|
materializeOne(nodeId) {
|
|
96
79
|
return this.materializeNode(this.#ensureCache().reader, nodeId, this.refs);
|
|
97
80
|
}
|
|
98
|
-
/** Arena id of `nodeId`'s parent in the pass snapshot, or undefined at the root. */
|
|
99
81
|
parentIdOf(nodeId) {
|
|
100
82
|
const parentId = this.readParentId(this.#ensureReader(), nodeId);
|
|
101
83
|
return parentId === NO_PARENT ? undefined : parentId;
|
|
102
84
|
}
|
|
103
|
-
|
|
104
|
-
* `indexInParent` (most never do). Cache-safe because the snapshot is immutable. */
|
|
85
|
+
// Immutable snapshots make cached child indices safe across repeated parent lookups.
|
|
105
86
|
#childIndexByParent = null;
|
|
106
|
-
/** Index of `nodeId` within its parent's children in the pass snapshot,
|
|
107
|
-
* or undefined at the root. */
|
|
108
87
|
indexInParent(nodeId) {
|
|
109
88
|
const reader = this.#ensureReader();
|
|
110
89
|
const parentId = this.readParentId(reader, nodeId);
|
|
@@ -1,42 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
* Shared materializer machinery for the HAST and MDAST flavors: per-reader
|
|
3
|
-
* node memo, lazy `children` descriptors, and the frozen-mode (plugin walk
|
|
4
|
-
* path) freeze rules.
|
|
5
|
-
*/
|
|
6
|
-
import type { Position } from "unist";
|
|
1
|
+
import type { Node } from "unist";
|
|
7
2
|
import type { NodeRefs } from "./visitor-shared.js";
|
|
8
|
-
/** The reader surface the shared machinery needs; both `HastReader` and `MdastReader` satisfy it. */
|
|
9
3
|
export interface MaterializerReader {
|
|
10
4
|
getNodeType(nodeId: number): number;
|
|
11
5
|
getChildIds(nodeId: number): number[];
|
|
12
|
-
|
|
6
|
+
getChildrenCount(nodeId: number): number;
|
|
13
7
|
getNodeData(nodeId: number): string | null;
|
|
14
8
|
}
|
|
15
|
-
export interface MaterializerSpec<TReader extends MaterializerReader, TNode extends
|
|
16
|
-
/** Function name used in error/warning messages (e.g. "materializeHastNode"). */
|
|
9
|
+
export interface MaterializerSpec<TReader extends MaterializerReader, TNode extends Node> {
|
|
17
10
|
label: string;
|
|
18
|
-
/** Node-type tag -> canonical AST name (the generated `TYPE_NAMES`). */
|
|
19
11
|
typeNames: Readonly<Record<number, string>>;
|
|
20
|
-
/** Whether `node` carries `children`. Takes the built node because mdast
|
|
21
|
-
* `custom` decides leafness per node rather than per type. */
|
|
22
12
|
hasChildren(nodeType: number, node: TNode, reader: TReader, nodeId: number): boolean;
|
|
23
|
-
/**
|
|
24
|
-
* Install the type-specific eager fields on `node`. Must not install
|
|
25
|
-
* `children`, `position`, `data`, or `_nodeId`, and must not freeze:
|
|
26
|
-
* the shared machinery owns all of those.
|
|
27
|
-
*/
|
|
28
13
|
populate(node: TNode, reader: TReader, nodeId: number, nodeType: number): void;
|
|
29
14
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
*
|
|
33
|
-
* `node` materializes one node with lazy `children`; `frozen` (the plugin walk
|
|
34
|
-
* path) deep-freezes every node at construction so plugins cannot corrupt the
|
|
35
|
-
* shared cache. `tree` materializes a whole tree eagerly, which is what the
|
|
36
|
-
* step-by-step API wants: it asked for the tree, so laziness would only add
|
|
37
|
-
* per-node accessor overhead.
|
|
38
|
-
*/
|
|
39
|
-
export declare function createMaterializer<TReader extends MaterializerReader, TNode extends object>(spec: MaterializerSpec<TReader, TNode>): {
|
|
15
|
+
export declare function installNodeData(node: Node, rawData: string | null, label: string, nodeId: number): void;
|
|
16
|
+
export declare function createMaterializer<TReader extends MaterializerReader, TNode extends Node>(spec: MaterializerSpec<TReader, TNode>): {
|
|
40
17
|
node: (reader: TReader, nodeId: number, frozen?: boolean, refs?: NodeRefs) => TNode;
|
|
41
|
-
tree: (reader: TReader, rootId: number) => TNode;
|
|
42
18
|
};
|