satteri 0.10.5 → 0.11.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/dist/arena-reader.d.ts +13 -0
- package/dist/arena-reader.js +104 -0
- package/dist/binding.browser.d.ts +1 -1
- package/dist/binding.browser.js +1 -1
- package/dist/binding.d.ts +1 -1
- package/dist/binding.js +1 -1
- package/dist/byte-writer.d.ts +0 -18
- package/dist/byte-writer.js +6 -30
- package/dist/child-stub.d.ts +0 -17
- package/dist/child-stub.js +6 -22
- package/dist/command-buffer.d.ts +5 -26
- package/dist/command-buffer.js +28 -39
- package/dist/compile-options.d.ts +11 -0
- package/dist/compile-options.js +101 -0
- package/dist/compile-types.d.ts +342 -0
- package/dist/compile-types.js +1 -0
- package/dist/compile.d.ts +22 -328
- package/dist/compile.js +133 -383
- package/dist/directive-types.d.ts +1 -0
- package/dist/directive-types.js +1 -6
- package/dist/generated/arena-layout.d.ts +6 -4
- package/dist/generated/arena-layout.js +6 -7
- package/dist/generated/fused-wire.d.ts +3 -0
- package/dist/generated/fused-wire.js +229 -0
- package/dist/generated/parse-options.d.ts +1 -0
- package/dist/generated/parse-options.js +1 -5
- package/dist/generated/wire-constants.d.ts +4 -0
- package/dist/generated/wire-constants.js +34 -42
- package/dist/handles.d.ts +0 -7
- package/dist/hast/child-stub.d.ts +1 -8
- package/dist/hast/child-stub.js +14 -13
- package/dist/hast/element-props.d.ts +13 -0
- package/dist/hast/element-props.js +52 -6
- package/dist/hast/generated/node-types.d.ts +1 -9
- package/dist/hast/generated/node-types.js +1 -9
- package/dist/hast/generated/walk-decode.d.ts +10 -0
- package/dist/hast/generated/walk-decode.js +61 -0
- package/dist/hast/hast-materializer.d.ts +3 -10
- package/dist/hast/hast-materializer.js +73 -47
- package/dist/hast/hast-reader.d.ts +9 -38
- package/dist/hast/hast-reader.js +82 -271
- package/dist/hast/hast-visitor.d.ts +29 -16
- package/dist/hast/hast-visitor.js +200 -508
- package/dist/hast/hast-walk.d.ts +18 -0
- package/dist/hast/hast-walk.js +264 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.js +4 -12
- package/dist/lazy-child-resolver.d.ts +1 -21
- package/dist/lazy-child-resolver.js +11 -32
- package/dist/materializer-cache.d.ts +5 -29
- package/dist/materializer-cache.js +29 -101
- package/dist/mdast/child-stub.d.ts +1 -12
- package/dist/mdast/child-stub.js +18 -28
- package/dist/mdast/column-align.d.ts +1 -1
- package/dist/mdast/column-align.js +0 -2
- package/dist/mdast/generated/layout.d.ts +0 -16
- package/dist/mdast/generated/layout.js +6 -47
- package/dist/mdast/generated/node-types.d.ts +1 -9
- package/dist/mdast/generated/node-types.js +3 -9
- package/dist/mdast/mdast-materializer.d.ts +3 -11
- package/dist/mdast/mdast-materializer.js +91 -63
- package/dist/mdast/mdast-reader.d.ts +15 -63
- package/dist/mdast/mdast-reader.js +132 -269
- package/dist/mdast/mdast-visitor.d.ts +23 -15
- package/dist/mdast/mdast-visitor.js +189 -294
- package/dist/mdast/mdast-walk.d.ts +13 -0
- package/dist/mdast/mdast-walk.js +144 -0
- package/dist/mdx-attr.js +0 -3
- package/dist/mdx-types.js +1 -6
- package/dist/op-stream.d.ts +4 -15
- package/dist/op-stream.js +31 -20
- package/dist/phantom.d.ts +0 -1
- package/dist/phantom.js +1 -9
- package/dist/plugin-pipeline.d.ts +32 -0
- package/dist/plugin-pipeline.js +177 -0
- package/dist/plugin.d.ts +14 -8
- package/dist/plugin.js +5 -10
- package/dist/types.d.ts +0 -1
- package/dist/visitor-node.d.ts +12 -0
- package/dist/visitor-node.js +23 -0
- package/dist/visitor-shared.d.ts +26 -30
- package/dist/visitor-shared.js +34 -36
- package/dist/wire-read.d.ts +0 -22
- package/dist/wire-read.js +3 -24
- package/index.d.ts +19 -10
- package/index.js +56 -53
- package/package.json +6 -12
- package/dist/string-pool.d.ts +0 -13
- package/dist/string-pool.js +0 -91
package/dist/hast/hast-reader.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { decodeElementProp } from "./element-props.js";
|
|
5
|
-
import { PoolOffsets } from "../string-pool.js";
|
|
1
|
+
import { ArenaReader } from "../arena-reader.js";
|
|
2
|
+
import { FIELD, KIND_HAST, W_CHILDREN_COUNT, W_CHILDREN_START, W_DATA_LEN, W_DATA_OFFSET, W_PARENT, W_START_OFFSET, } from "../generated/arena-layout.js";
|
|
3
|
+
import { decodeMdxJsxAttr } from "../mdx-attr.js";
|
|
6
4
|
import { NAME_TO_TYPE } from "./generated/node-types.js";
|
|
7
|
-
import { ARENA_MAGIC, KIND_HAST, FIELD, HEADER } from "../generated/arena-layout.js";
|
|
8
5
|
export const HAST_ROOT = NAME_TO_TYPE.root;
|
|
9
6
|
export const HAST_ELEMENT = NAME_TO_TYPE.element;
|
|
10
7
|
export const HAST_TEXT = NAME_TO_TYPE.text;
|
|
@@ -16,315 +13,129 @@ export const HAST_MDX_FLOW_EXPRESSION = NAME_TO_TYPE.mdxFlowExpression;
|
|
|
16
13
|
export const HAST_MDX_ESM = NAME_TO_TYPE.mdxjsEsm;
|
|
17
14
|
export const HAST_MDX_TEXT_EXPRESSION = NAME_TO_TYPE.mdxTextExpression;
|
|
18
15
|
export class HastReader {
|
|
19
|
-
#
|
|
20
|
-
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
16
|
+
#arena;
|
|
17
|
+
// Typed arrays avoid DataView getter overhead in unoptimized V8 tiers.
|
|
18
|
+
#u8;
|
|
19
|
+
#u32;
|
|
20
|
+
#nodesB;
|
|
21
|
+
#nodesW;
|
|
22
|
+
#strideB;
|
|
23
|
+
#strideW;
|
|
24
|
+
#childrenW;
|
|
25
|
+
#typeDataB;
|
|
25
26
|
constructor(buffer) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
this.#
|
|
33
|
-
this.#
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
throw new Error(`HastReader was handed a buffer of kind ${kind} (expected ${KIND_HAST}). ` +
|
|
44
|
-
`MDAST and HAST node types overlap; reading the wrong kind decodes garbage.`);
|
|
45
|
-
}
|
|
46
|
-
return {
|
|
47
|
-
nodeStructSize: v.getUint32(HEADER.node_struct_size, true),
|
|
48
|
-
nodeCount: v.getUint32(HEADER.node_count, true),
|
|
49
|
-
nodesOffset: v.getUint32(HEADER.nodes_offset, true),
|
|
50
|
-
childrenCount: v.getUint32(HEADER.children_count, true),
|
|
51
|
-
childrenOffset: v.getUint32(HEADER.children_offset, true),
|
|
52
|
-
typeDataLen: v.getUint32(HEADER.type_data_len, true),
|
|
53
|
-
typeDataOffset: v.getUint32(HEADER.type_data_offset, true),
|
|
54
|
-
stringPoolLen: v.getUint32(HEADER.string_pool_len, true),
|
|
55
|
-
stringPoolOffset: v.getUint32(HEADER.string_pool_offset, true),
|
|
56
|
-
nodeDataCount: v.getUint32(HEADER.node_data_count, true),
|
|
57
|
-
nodeDataOffset: v.getUint32(HEADER.node_data_offset, true),
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
#nodeDataTable = null;
|
|
61
|
-
/**
|
|
62
|
-
* Per-node JSON `data` blob (set via `Arena::set_node_data` on the Rust
|
|
63
|
-
* side). Returns `null` when the node has no entry. Lazy-builds a
|
|
64
|
-
* `Map<id, string>` on first call so a materialization pass on a
|
|
65
|
-
* data-heavy tree stays O(nodes) rather than O(nodes × entries).
|
|
66
|
-
*/
|
|
27
|
+
const arena = new ArenaReader(buffer, KIND_HAST);
|
|
28
|
+
this.#arena = arena;
|
|
29
|
+
this.#u8 = arena.u8;
|
|
30
|
+
this.#u32 = arena.u32;
|
|
31
|
+
const header = arena.header;
|
|
32
|
+
this.#nodesB = header.nodesOffset;
|
|
33
|
+
this.#nodesW = header.nodesOffset >> 2;
|
|
34
|
+
this.#strideB = header.nodeStructSize;
|
|
35
|
+
this.#strideW = header.nodeStructSize >> 2;
|
|
36
|
+
this.#childrenW = header.childrenOffset >> 2;
|
|
37
|
+
this.#typeDataB = header.typeDataOffset;
|
|
38
|
+
}
|
|
39
|
+
/** @internal Memoized wire view for the fused decoder. */
|
|
40
|
+
getWire() {
|
|
41
|
+
return this.#arena.getWire();
|
|
42
|
+
}
|
|
43
|
+
/** Per-node JSON data, or null when absent. */
|
|
67
44
|
getNodeData(nodeId) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
let pos = this.#header.nodeDataOffset;
|
|
74
|
-
for (let i = 0; i < this.#header.nodeDataCount; i++) {
|
|
75
|
-
const id = v.getUint32(pos, true);
|
|
76
|
-
pos += 4;
|
|
77
|
-
const len = v.getUint32(pos, true);
|
|
78
|
-
pos += 4;
|
|
79
|
-
const slice = new Uint8Array(this.#view.buffer, this.#view.byteOffset + pos, len);
|
|
80
|
-
this.#nodeDataTable.set(id, this.#textDecoder.decode(slice));
|
|
81
|
-
pos += len;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
return this.#nodeDataTable.get(nodeId) ?? null;
|
|
45
|
+
return this.#arena.getNodeData(nodeId);
|
|
46
|
+
}
|
|
47
|
+
/** @internal null when no node carries data. */
|
|
48
|
+
getNodeDataTable() {
|
|
49
|
+
return this.#arena.getNodeDataTable();
|
|
85
50
|
}
|
|
86
51
|
get nodeCount() {
|
|
87
|
-
return this.#header.nodeCount;
|
|
52
|
+
return this.#arena.header.nodeCount;
|
|
88
53
|
}
|
|
89
54
|
get header() {
|
|
90
|
-
return { ...this.#header };
|
|
55
|
+
return { ...this.#arena.header };
|
|
91
56
|
}
|
|
92
|
-
/**
|
|
93
|
-
* source as written; for that, read `ctx.source` from a plugin. */
|
|
57
|
+
/** Full string pool, including interned strings; use ctx.source for the original document. */
|
|
94
58
|
getStringPool() {
|
|
95
|
-
|
|
96
|
-
const { stringPoolOffset, stringPoolLen } = this.#header;
|
|
97
|
-
const bytes = new Uint8Array(this.#view.buffer, this.#view.byteOffset + stringPoolOffset, stringPoolLen);
|
|
98
|
-
this.#stringPoolCache = this.#textDecoder.decode(bytes);
|
|
99
|
-
}
|
|
100
|
-
return this.#stringPoolCache;
|
|
59
|
+
return this.#arena.getStringPool();
|
|
101
60
|
}
|
|
102
|
-
/**
|
|
61
|
+
/** Wire string refs use UTF-16 units, so substring preserves their offsets. */
|
|
103
62
|
getString(offset, len) {
|
|
104
|
-
|
|
105
|
-
return "";
|
|
106
|
-
const pool = this.getStringPool();
|
|
107
|
-
if (!this.#poolChecked) {
|
|
108
|
-
this.#poolChecked = true;
|
|
109
|
-
const { stringPoolOffset, stringPoolLen } = this.#header;
|
|
110
|
-
const extraBytes = stringPoolLen - pool.length;
|
|
111
|
-
if (extraBytes > 0) {
|
|
112
|
-
const bytes = new Uint8Array(this.#view.buffer, this.#view.byteOffset + stringPoolOffset, stringPoolLen);
|
|
113
|
-
this.#poolOffsets = new PoolOffsets(bytes, extraBytes);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
// An all-ASCII pool has byte offsets equal to its UTF-16 indices.
|
|
117
|
-
const offsets = this.#poolOffsets;
|
|
118
|
-
return offsets === null
|
|
119
|
-
? pool.substring(offset, offset + len)
|
|
120
|
-
: offsets.slice(pool, offset, len);
|
|
63
|
+
return this.#arena.getString(offset, len);
|
|
121
64
|
}
|
|
122
|
-
/**
|
|
65
|
+
/** A zero start line marks a synthesized node with no source range (unist
|
|
66
|
+
* lines are 1-based), surfaced as `undefined`. */
|
|
123
67
|
getPosition(nodeId) {
|
|
124
|
-
const
|
|
125
|
-
|
|
68
|
+
const u32 = this.#u32;
|
|
69
|
+
const b = this.#nodesW + nodeId * this.#strideW + W_START_OFFSET;
|
|
70
|
+
const startLine = u32[b + 2] ?? 0;
|
|
71
|
+
if (startLine === 0)
|
|
72
|
+
return undefined;
|
|
73
|
+
return {
|
|
74
|
+
start: { offset: u32[b] ?? 0, line: startLine, column: u32[b + 3] ?? 0 },
|
|
75
|
+
end: { offset: u32[b + 1] ?? 0, line: u32[b + 4] ?? 0, column: u32[b + 5] ?? 0 },
|
|
76
|
+
};
|
|
126
77
|
}
|
|
127
78
|
/** Get the node_type byte for a given node ID. */
|
|
128
79
|
getNodeType(nodeId) {
|
|
129
|
-
|
|
130
|
-
return this.#view.getUint8(nodesOffset + nodeId * nodeStructSize + FIELD.node_type);
|
|
80
|
+
return this.#u8[this.#nodesB + nodeId * this.#strideB + FIELD.node_type] ?? 0;
|
|
131
81
|
}
|
|
132
82
|
/** Get the parent id for a given node (0xffffffff at the root). */
|
|
133
83
|
getParentId(nodeId) {
|
|
134
|
-
|
|
135
|
-
|
|
84
|
+
return this.#u32[this.#nodesW + nodeId * this.#strideW + W_PARENT] ?? 0;
|
|
85
|
+
}
|
|
86
|
+
getChildrenCount(nodeId) {
|
|
87
|
+
return this.#u32[this.#nodesW + nodeId * this.#strideW + W_CHILDREN_COUNT] ?? 0;
|
|
136
88
|
}
|
|
137
|
-
/** Get child node IDs for a given node. */
|
|
138
89
|
getChildIds(nodeId) {
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
-
const childrenStart =
|
|
142
|
-
const childrenCount =
|
|
90
|
+
const u32 = this.#u32;
|
|
91
|
+
const w = this.#nodesW + nodeId * this.#strideW;
|
|
92
|
+
const childrenStart = u32[w + W_CHILDREN_START] ?? 0;
|
|
93
|
+
const childrenCount = u32[w + W_CHILDREN_COUNT] ?? 0;
|
|
143
94
|
if (childrenCount === 0)
|
|
144
95
|
return [];
|
|
145
|
-
const
|
|
96
|
+
const base = this.#childrenW + childrenStart;
|
|
146
97
|
const ids = [];
|
|
147
98
|
for (let i = 0; i < childrenCount; i++) {
|
|
148
|
-
ids.push(
|
|
99
|
+
ids.push(u32[base + i] ?? 0);
|
|
149
100
|
}
|
|
150
101
|
return ids;
|
|
151
102
|
}
|
|
152
|
-
/** Push child node IDs directly onto a stack array (reverse order for depth-first). */
|
|
153
|
-
pushChildIds(nodeId, stack) {
|
|
154
|
-
const base = this.#header.nodesOffset + nodeId * this.#header.nodeStructSize;
|
|
155
|
-
const v = this.#view;
|
|
156
|
-
const childrenStart = v.getUint32(base + FIELD.children_start, true);
|
|
157
|
-
const childrenCount = v.getUint32(base + FIELD.children_count, true);
|
|
158
|
-
if (childrenCount === 0)
|
|
159
|
-
return;
|
|
160
|
-
const { childrenOffset } = this.#header;
|
|
161
|
-
for (let i = childrenCount - 1; i >= 0; i--) {
|
|
162
|
-
stack.push(v.getUint32(childrenOffset + (childrenStart + i) * 4, true));
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
103
|
/** Get the raw type_data bytes for a node. */
|
|
166
104
|
getTypeData(nodeId) {
|
|
167
|
-
const
|
|
168
|
-
const
|
|
169
|
-
const dataOffset =
|
|
170
|
-
const dataLen =
|
|
105
|
+
const u32 = this.#u32;
|
|
106
|
+
const w = this.#nodesW + nodeId * this.#strideW;
|
|
107
|
+
const dataOffset = u32[w + W_DATA_OFFSET] ?? 0;
|
|
108
|
+
const dataLen = u32[w + W_DATA_LEN] ?? 0;
|
|
171
109
|
if (dataLen === 0)
|
|
172
110
|
return new Uint8Array(0);
|
|
173
|
-
|
|
111
|
+
const start = this.#typeDataB + dataOffset;
|
|
112
|
+
return this.#u8.subarray(start, start + dataLen);
|
|
174
113
|
}
|
|
175
|
-
/** Read a StringRef (offset: u32 LE, len: u32 LE) from a byte array at byteOffset. */
|
|
176
|
-
#readStringRef(data, byteOffset) {
|
|
177
|
-
const at = data.byteOffset - this.#view.byteOffset + byteOffset;
|
|
178
|
-
return {
|
|
179
|
-
offset: this.#view.getUint32(at, true),
|
|
180
|
-
len: this.#view.getUint32(at + 4, true),
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Get element data for a HAST_ELEMENT node.
|
|
185
|
-
*
|
|
186
|
-
* Element type_data layout:
|
|
187
|
-
* [tag_name: StringRef(8B)][prop_count: u32(4B)][_pad: u32(4B)] = 16-byte header
|
|
188
|
-
* then prop_count * 20 bytes:
|
|
189
|
-
* [name: StringRef(8B)][value_type: u8(1B)][_pad: [u8;3](3B)][value: StringRef(8B)]
|
|
190
|
-
*/
|
|
191
114
|
/** Byte position of a node's `type_data`, relative to this reader's view, or
|
|
192
115
|
* `-1` when the node stores fewer than `min` bytes. */
|
|
193
116
|
#typeDataAt(nodeId, min) {
|
|
194
|
-
const
|
|
195
|
-
const
|
|
196
|
-
if (
|
|
117
|
+
const u32 = this.#u32;
|
|
118
|
+
const w = this.#nodesW + nodeId * this.#strideW;
|
|
119
|
+
if ((u32[w + W_DATA_LEN] ?? 0) < min)
|
|
197
120
|
return -1;
|
|
198
|
-
return this.#
|
|
199
|
-
}
|
|
200
|
-
#stringAt(at) {
|
|
201
|
-
return this.getString(this.#view.getUint32(at, true), this.#view.getUint32(at + 4, true));
|
|
121
|
+
return this.#typeDataB + (u32[w + W_DATA_OFFSET] ?? 0);
|
|
202
122
|
}
|
|
203
|
-
/**
|
|
204
|
-
|
|
205
|
-
readElementInto(nodeId, node) {
|
|
123
|
+
/** A zero-length element name represents an MDX fragment. */
|
|
124
|
+
getMdxJsxElementData(nodeId) {
|
|
206
125
|
const at = this.#typeDataAt(nodeId, 16);
|
|
207
126
|
if (at === -1) {
|
|
208
|
-
node.tagName = "";
|
|
209
|
-
node.properties = {};
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
const v = this.#view;
|
|
213
|
-
node.tagName = this.#stringAt(at);
|
|
214
|
-
const count = v.getUint32(at + 8, true);
|
|
215
|
-
const properties = {};
|
|
216
|
-
for (let i = 0; i < count; i++) {
|
|
217
|
-
const base = at + 16 + i * 20;
|
|
218
|
-
properties[this.#stringAt(base)] = decodeElementProp(v.getUint8(base + 8), this.#stringAt(base + 12));
|
|
219
|
-
}
|
|
220
|
-
node.properties = properties;
|
|
221
|
-
}
|
|
222
|
-
/** Element `tagName`, `properties` count, and the i-th property, read without
|
|
223
|
-
* building the intermediate views and records `getElementData` allocates. */
|
|
224
|
-
getElementTagName(nodeId) {
|
|
225
|
-
const at = this.#typeDataAt(nodeId, 16);
|
|
226
|
-
return at === -1 ? "" : this.#stringAt(at);
|
|
227
|
-
}
|
|
228
|
-
getElementPropCount(nodeId) {
|
|
229
|
-
const at = this.#typeDataAt(nodeId, 16);
|
|
230
|
-
return at === -1 ? 0 : this.#view.getUint32(at + 8, true);
|
|
231
|
-
}
|
|
232
|
-
getElementPropName(nodeId, index) {
|
|
233
|
-
const at = this.#typeDataAt(nodeId, 16);
|
|
234
|
-
return at === -1 ? "" : this.#stringAt(at + 16 + index * 20);
|
|
235
|
-
}
|
|
236
|
-
getElementPropValue(nodeId, index) {
|
|
237
|
-
const at = this.#typeDataAt(nodeId, 16);
|
|
238
|
-
if (at === -1)
|
|
239
|
-
return "";
|
|
240
|
-
const base = at + 16 + index * 20;
|
|
241
|
-
return decodeElementProp(this.#view.getUint8(base + 8), this.#stringAt(base + 12));
|
|
242
|
-
}
|
|
243
|
-
getElementData(nodeId) {
|
|
244
|
-
const data = this.getTypeData(nodeId);
|
|
245
|
-
if (data.length < 16) {
|
|
246
|
-
return { tagName: "", properties: [] };
|
|
247
|
-
}
|
|
248
|
-
const tagRef = this.#readStringRef(data, 0);
|
|
249
|
-
const tagName = this.getString(tagRef.offset, tagRef.len);
|
|
250
|
-
const view = new DataView(data.buffer, data.byteOffset + 8);
|
|
251
|
-
const propCount = view.getUint32(0, true);
|
|
252
|
-
const properties = [];
|
|
253
|
-
for (let i = 0; i < propCount; i++) {
|
|
254
|
-
const base = 16 + i * 20;
|
|
255
|
-
const nameRef = this.#readStringRef(data, base);
|
|
256
|
-
const name = this.getString(nameRef.offset, nameRef.len);
|
|
257
|
-
const valueType = data[base + 8];
|
|
258
|
-
const valueRef = this.#readStringRef(data, base + 12);
|
|
259
|
-
const valueStr = this.getString(valueRef.offset, valueRef.len);
|
|
260
|
-
properties.push({ name, value: decodeElementProp(valueType, valueStr) });
|
|
261
|
-
}
|
|
262
|
-
return { tagName, properties };
|
|
263
|
-
}
|
|
264
|
-
/**
|
|
265
|
-
* Get MDX JSX element data: name and attributes.
|
|
266
|
-
*
|
|
267
|
-
* MDX JSX element type_data layout:
|
|
268
|
-
* [name: StringRef(8B)][attr_count: u32(4B)][_pad: u32(4B)] = 16-byte header
|
|
269
|
-
* then attr_count * 20 bytes:
|
|
270
|
-
* [kind: u8(1B)][_pad: [u8;3](3B)][name: StringRef(8B)][value: StringRef(8B)]
|
|
271
|
-
*/
|
|
272
|
-
getMdxJsxElementData(nodeId) {
|
|
273
|
-
const data = this.getTypeData(nodeId);
|
|
274
|
-
if (data.length < 16) {
|
|
275
127
|
return { name: null, attributes: [] };
|
|
276
128
|
}
|
|
277
|
-
const
|
|
278
|
-
const
|
|
279
|
-
const
|
|
280
|
-
const
|
|
129
|
+
const u32 = this.#u32;
|
|
130
|
+
const w = at >> 2;
|
|
131
|
+
const nameLen = u32[w + 1] ?? 0;
|
|
132
|
+
const name = nameLen > 0 ? this.getString(u32[w] ?? 0, nameLen) : null;
|
|
133
|
+
const attrCount = u32[w + 2] ?? 0;
|
|
281
134
|
const attributes = [];
|
|
282
135
|
for (let i = 0; i < attrCount; i++) {
|
|
283
|
-
const base =
|
|
284
|
-
|
|
285
|
-
const attrNameRef = this.#readStringRef(data, base + 4);
|
|
286
|
-
const attrValueRef = this.#readStringRef(data, base + 12);
|
|
287
|
-
switch (kind) {
|
|
288
|
-
case MDX_ATTR_BOOLEAN_PROP:
|
|
289
|
-
attributes.push({
|
|
290
|
-
type: "mdxJsxAttribute",
|
|
291
|
-
name: this.getString(attrNameRef.offset, attrNameRef.len),
|
|
292
|
-
value: null,
|
|
293
|
-
});
|
|
294
|
-
break;
|
|
295
|
-
case MDX_ATTR_LITERAL_PROP:
|
|
296
|
-
attributes.push({
|
|
297
|
-
type: "mdxJsxAttribute",
|
|
298
|
-
name: this.getString(attrNameRef.offset, attrNameRef.len),
|
|
299
|
-
value: this.getString(attrValueRef.offset, attrValueRef.len),
|
|
300
|
-
});
|
|
301
|
-
break;
|
|
302
|
-
case MDX_ATTR_EXPRESSION_PROP:
|
|
303
|
-
attributes.push({
|
|
304
|
-
type: "mdxJsxAttribute",
|
|
305
|
-
name: this.getString(attrNameRef.offset, attrNameRef.len),
|
|
306
|
-
value: {
|
|
307
|
-
type: "mdxJsxAttributeValueExpression",
|
|
308
|
-
value: restorePhantomSpaces(this.getString(attrValueRef.offset, attrValueRef.len)),
|
|
309
|
-
},
|
|
310
|
-
});
|
|
311
|
-
break;
|
|
312
|
-
case MDX_ATTR_SPREAD:
|
|
313
|
-
attributes.push({
|
|
314
|
-
type: "mdxJsxExpressionAttribute",
|
|
315
|
-
value: restorePhantomSpaces(this.getString(attrValueRef.offset, attrValueRef.len)),
|
|
316
|
-
});
|
|
317
|
-
break;
|
|
318
|
-
}
|
|
136
|
+
const base = w + 4 + i * 5;
|
|
137
|
+
attributes.push(decodeMdxJsxAttr(this.#u8[base << 2] ?? 0, this.getString(u32[base + 1] ?? 0, u32[base + 2] ?? 0), this.getString(u32[base + 3] ?? 0, u32[base + 4] ?? 0)));
|
|
319
138
|
}
|
|
320
139
|
return { name, attributes };
|
|
321
140
|
}
|
|
322
|
-
/**
|
|
323
|
-
* Get the string value for HAST_TEXT, HAST_COMMENT, or HAST_RAW nodes.
|
|
324
|
-
* These store a single StringRef (8 bytes) as their type_data.
|
|
325
|
-
*/
|
|
326
|
-
getTextValue(nodeId) {
|
|
327
|
-
const at = this.#typeDataAt(nodeId, 8);
|
|
328
|
-
return at === -1 ? "" : this.#stringAt(at);
|
|
329
|
-
}
|
|
330
141
|
}
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
import { type HastNode } from "./hast-materializer.js";
|
|
2
|
-
import type { HastRaw, Data, SourceFormat } from "../types.js";
|
|
3
|
-
import type { Element, Text, Comment, Doctype, Parents as HastParents, Root as HastRoot } from "hast";
|
|
4
1
|
import type { Program } from "estree-jsx";
|
|
5
|
-
import type {
|
|
6
|
-
import type { MdxFlowExpressionHast, MdxTextExpressionHast } from "../mdx-types.js";
|
|
7
|
-
import type {
|
|
8
|
-
import { type
|
|
2
|
+
import type { Comment, Doctype, Element, Parents as HastParents, Root as HastRoot, Text } from "hast";
|
|
3
|
+
import type { MdxFlowExpressionHast, MdxjsEsmHast, MdxJsxFlowElementHast, MdxJsxTextElementHast, MdxTextExpressionHast } from "../mdx-types.js";
|
|
4
|
+
import type { Data, HastRaw, SourceFormat } from "../types.js";
|
|
5
|
+
import { type HastNode } from "./hast-materializer.js";
|
|
9
6
|
import type { HastHandle } from "../handles.js";
|
|
7
|
+
import { type PluginOptions, type SettableFieldKey } from "../visitor-shared.js";
|
|
10
8
|
export type { HastHandle };
|
|
11
9
|
/** ESTree-compatible Program node returned by `parseExpression()`. */
|
|
12
10
|
export type EstreeProgram = Program;
|
|
13
11
|
export interface HastDiagnostic {
|
|
14
12
|
message: string;
|
|
15
13
|
nodeId?: number | undefined;
|
|
14
|
+
position?: HastNode["position"] | undefined;
|
|
16
15
|
severity: "error" | "warning" | "info";
|
|
17
16
|
}
|
|
18
17
|
export interface HastVisitorContext {
|
|
@@ -61,6 +60,20 @@ export interface HastVisitorContext {
|
|
|
61
60
|
insertChildAt(node: Readonly<HastNode>, index: number, childNode: HastContent | HastContent[]): void;
|
|
62
61
|
/** Remove the `index`-th child of `node`; a no-op when there is no such child. */
|
|
63
62
|
removeChildAt(node: Readonly<HastNode>, index: number): void;
|
|
63
|
+
/** Replace a supported field on the node, including scalars and structured fields. */
|
|
64
|
+
setField<N extends HastNode, K extends SettableFieldKey<N>>(node: Readonly<N>, key: K, value: Exclude<N[K], undefined>): void;
|
|
65
|
+
/** `children` is structural and every parent accepts it, including node-type unions. */
|
|
66
|
+
setField(node: Readonly<HastNode>, key: "children", value: readonly HastContent[]): void;
|
|
67
|
+
/** `data` is an open per-node bag serialized to JSON. `null` clears it. */
|
|
68
|
+
setField(node: Readonly<HastNode>, key: "data", value: Record<string, unknown> | null): void;
|
|
69
|
+
/** Set one entry in an MDX JSX element's `attributes`. */
|
|
70
|
+
setAttribute(node: Readonly<HastNode>, name: string, value: unknown): void;
|
|
71
|
+
/**
|
|
72
|
+
* Set one entry in a hast element's `properties`.
|
|
73
|
+
*
|
|
74
|
+
* Using this for node fields or MDX JSX attributes is deprecated; use
|
|
75
|
+
* `setField` or `setAttribute` respectively.
|
|
76
|
+
*/
|
|
64
77
|
setProperty(node: Readonly<HastNode>, key: string, value: unknown): void;
|
|
65
78
|
/** Collect the concatenated text of all descendant text nodes (like DOM textContent). */
|
|
66
79
|
textContent(node: Readonly<HastNode>): string;
|
|
@@ -102,12 +115,19 @@ export interface RawHtmlHastContent {
|
|
|
102
115
|
/** @deprecated Use the equivalent `{ raw }`. */
|
|
103
116
|
rawHtml: string;
|
|
104
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Encode a standalone HAST tree as an op-stream document: one `root` wrapping
|
|
120
|
+
* `nodes`, which Rust replays into an arena of its own and serializes. The
|
|
121
|
+
* bytes are handed to `use` because they are a view into a pooled buffer,
|
|
122
|
+
* valid only until it is released.
|
|
123
|
+
*/
|
|
124
|
+
export declare function encodeHastDocument<T>(nodes: readonly HastNode[], use: (ops: Uint8Array) => T): T;
|
|
105
125
|
/** A filtered visitor: Rust filters by tag/component name, only matched nodes cross the boundary. */
|
|
106
126
|
export interface HastFilteredVisitor<N extends HastNode = HastNode> {
|
|
107
127
|
filter: string[];
|
|
108
128
|
visit(node: Readonly<N>, ctx: HastVisitorContext): HastNode | void | Promise<HastNode | void>;
|
|
109
129
|
}
|
|
110
|
-
type HastVisitorFn<N extends HastNode = HastNode> = (node: Readonly<N>, ctx: HastVisitorContext) => HastNode | void | Promise<HastNode | void>;
|
|
130
|
+
export type HastVisitorFn<N extends HastNode = HastNode> = (node: Readonly<N>, ctx: HastVisitorContext) => HastNode | void | Promise<HastNode | void>;
|
|
111
131
|
export type HastHookFn = (root: Readonly<HastRoot>, ctx: HastVisitorContext) => void | Promise<void>;
|
|
112
132
|
export interface HastVisitorInstance {
|
|
113
133
|
/** Plugin-level configuration (e.g. `{ position: true }` to read positions). */
|
|
@@ -149,14 +169,7 @@ export declare function resolveSubscriptions(plugin: HastVisitorInstance): Resol
|
|
|
149
169
|
* Promise of that count if any visitor is async.
|
|
150
170
|
*/
|
|
151
171
|
export declare function visitHastHandle(handle: HastHandle, plugin: HastVisitorInstance, subs: ResolvedSubscription[], source: string | (() => string), fileURL: URL | undefined, data?: Data, sourceFormat?: SourceFormat, diagnostics?: HastDiagnostic[]): number | Promise<number>;
|
|
152
|
-
/** Run a HAST visitor, build the command buffer, but do NOT apply it. Returns
|
|
153
|
-
* the merged commands so the caller can choose how to dispatch: either via
|
|
154
|
-
* `applyCommandsToHandle` (intermediate plugins in a chain) or via a fused
|
|
155
|
-
* NAPI call like `applyCommandsAndRenderHandle` (final plugin, saves one
|
|
156
|
-
* apply + one render + one drop crossing). Empty result means no mutations.
|
|
157
|
-
*/
|
|
158
172
|
export declare function visitHastHandleCollect(handle: HastHandle, plugin: HastVisitorInstance, subs: ResolvedSubscription[], source: string | (() => string), fileURL: URL | undefined, data?: Data, sourceFormat?: SourceFormat, diagnostics?: HastDiagnostic[]): Uint8Array | Promise<Uint8Array>;
|
|
159
|
-
/**
|
|
160
|
-
* visitors walk, and the visitors' mutations before `after` reads the tree. */
|
|
173
|
+
/** Hooks run in separate passes so each sees mutations from the preceding pass. */
|
|
161
174
|
export declare function visitHastHookCollect(handle: HastHandle, plugin: HastVisitorInstance, hook: HastHookFn, source: string | (() => string), fileURL: URL | undefined, data?: Data, sourceFormat?: SourceFormat, diagnostics?: HastDiagnostic[]): Uint8Array | Promise<Uint8Array>;
|
|
162
175
|
export declare function visitHastHook(handle: HastHandle, plugin: HastVisitorInstance, hook: HastHookFn, source: string | (() => string), fileURL: URL | undefined, data?: Data, sourceFormat?: SourceFormat, diagnostics?: HastDiagnostic[]): number | Promise<number>;
|