satteri 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -0
- package/dist/byte-writer.d.ts +31 -0
- package/dist/byte-writer.js +106 -0
- package/dist/child-stub.d.ts +17 -0
- package/dist/child-stub.js +57 -0
- package/dist/command-buffer.d.ts +23 -24
- package/dist/command-buffer.js +65 -128
- package/dist/compile.d.ts +5 -1
- package/dist/compile.js +40 -26
- package/dist/directive-types.d.ts +4 -2
- package/dist/generated/arena-layout.d.ts +37 -0
- package/dist/generated/arena-layout.js +41 -0
- package/dist/generated/wire-constants.d.ts +51 -0
- package/dist/generated/wire-constants.js +63 -0
- package/dist/handles.d.ts +21 -0
- package/dist/handles.js +1 -0
- package/dist/hast/child-stub.d.ts +18 -0
- package/dist/hast/child-stub.js +43 -0
- package/dist/hast/element-props.d.ts +2 -0
- package/dist/hast/element-props.js +23 -0
- package/dist/hast/generated/node-types.d.ts +14 -0
- package/dist/hast/generated/node-types.js +64 -0
- package/dist/hast/hast-materializer.d.ts +2 -1
- package/dist/hast/hast-materializer.js +9 -45
- package/dist/hast/hast-reader.d.ts +18 -23
- package/dist/hast/hast-reader.js +48 -115
- package/dist/hast/hast-visitor.d.ts +38 -12
- package/dist/hast/hast-visitor.js +417 -281
- package/dist/index.d.ts +10 -4
- package/dist/index.js +23 -1
- package/dist/lazy-child-resolver.d.ts +36 -0
- package/dist/lazy-child-resolver.js +95 -0
- package/dist/lazy-props.d.ts +1 -1
- package/dist/mdast/child-stub.d.ts +18 -0
- package/dist/mdast/child-stub.js +46 -0
- package/dist/mdast/column-align.d.ts +1 -0
- package/dist/mdast/column-align.js +6 -0
- package/dist/mdast/generated/layout.d.ts +19 -0
- package/dist/mdast/generated/layout.js +272 -0
- package/dist/mdast/generated/node-types.d.ts +58 -0
- package/dist/mdast/generated/node-types.js +266 -0
- package/dist/mdast/mdast-materializer.d.ts +3 -2
- package/dist/mdast/mdast-materializer.js +9 -93
- package/dist/mdast/mdast-reader.d.ts +4 -120
- package/dist/mdast/mdast-reader.js +45 -272
- package/dist/mdast/mdast-visitor.d.ts +55 -13
- package/dist/mdast/mdast-visitor.js +375 -410
- package/dist/mdx-attr.d.ts +2 -0
- package/dist/mdx-attr.js +20 -0
- package/dist/op-stream.d.ts +38 -0
- package/dist/op-stream.js +125 -0
- package/dist/phantom.d.ts +2 -0
- package/dist/phantom.js +13 -0
- package/dist/types.d.ts +34 -4
- package/dist/visitor-shared.d.ts +28 -0
- package/dist/visitor-shared.js +52 -0
- package/dist/wire-read.d.ts +27 -0
- package/dist/wire-read.js +59 -0
- package/index.js +52 -52
- package/package.json +6 -6
package/dist/index.d.ts
CHANGED
|
@@ -2,13 +2,19 @@ export { markdownToHtml, mdxToJs, evaluate, markdownToMdast, mdxToMdast, markdow
|
|
|
2
2
|
export type { CompileOptions, MdxCompileOptions, MdxOnlyOptions, EvaluateOptions, OptimizeStaticConfig, Features, SmartPunctuationOptions, Frontmatter, MarkdownToHtmlResult, MdxToJsResult, } from "./compile.js";
|
|
3
3
|
export { defineMdastPlugin, defineHastPlugin } from "./plugin.js";
|
|
4
4
|
export type { MdastPluginDefinition, HastPluginDefinition, MdastPluginInput, HastPluginInput, } from "./plugin.js";
|
|
5
|
-
export type { HastVisitorInstance, HastVisitorContext, HastFilteredVisitor, EstreeProgram, } from "./hast/hast-visitor.js";
|
|
6
|
-
export type { MdastNode, HastNode, 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, HastContent, EstreeProgram, } from "./hast/hast-visitor.js";
|
|
6
|
+
export type { MdastNode, HastNode, DataMap, Data, Position, Point, MdxJsxAttributeNode, MdxJsxExpressionAttributeNode, MdxJsxAttributeValueExpressionNode, MdxJsxAttributeUnion, MdxJsxFlowElement, MdxJsxTextElement, MdxFlowExpression, MdxTextExpression, MdxjsEsm, MdxJsxFlowElementHast, MdxJsxTextElementHast, MdxFlowExpressionHast, MdxTextExpressionHast, MdxjsEsmHast, } from "./types.js";
|
|
7
7
|
export { visitMdastHandle, resolveMdastSubscriptions } from "./mdast/mdast-visitor.js";
|
|
8
|
-
export type { MdastPluginInstance } from "./mdast/mdast-visitor.js";
|
|
8
|
+
export type { MdastPluginInstance, MdastContent } from "./mdast/mdast-visitor.js";
|
|
9
9
|
export { visitHastHandle, resolveSubscriptions as resolveHastSubscriptions, } from "./hast/hast-visitor.js";
|
|
10
10
|
export { MdastReader } from "./mdast/mdast-reader.js";
|
|
11
11
|
export { materializeMdastTree } from "./mdast/mdast-materializer.js";
|
|
12
12
|
export { HastReader } from "./hast/hast-reader.js";
|
|
13
13
|
export { materializeHastTree } from "./hast/hast-materializer.js";
|
|
14
|
-
export { createMdastHandle, createMdxMdastHandle, createHastHandle, createMdxHastHandle,
|
|
14
|
+
export { createMdastHandle, createMdxMdastHandle, createHastHandle, createMdxHastHandle, serializeHandle, renderHandle, compileHandle, getHandleSource, } from "#binding";
|
|
15
|
+
import { applyCommandsAndConvertToHastHandle as napiApplyCommandsAndConvertToHastHandle, convertMdastToHastHandle as napiConvertMdastToHastHandle } from "#binding";
|
|
16
|
+
import type { AnyHandle } from "./handles.js";
|
|
17
|
+
export declare function applyCommandsToMdastHandle(handle: MdastHandle, commandBuf: Uint8Array): number;
|
|
18
|
+
export declare function convertMdastToHastHandle(handle: MdastHandle, convertOptions?: Parameters<typeof napiConvertMdastToHastHandle>[1]): HastHandle;
|
|
19
|
+
export declare function dropHandle(handle: AnyHandle): void;
|
|
20
|
+
export declare function applyCommandsAndConvertToHastHandle(handle: MdastHandle, commandBuf: Uint8Array, convertOptions?: Parameters<typeof napiApplyCommandsAndConvertToHastHandle>[2]): HastHandle;
|
package/dist/index.js
CHANGED
|
@@ -10,4 +10,26 @@ export { MdastReader } from "./mdast/mdast-reader.js";
|
|
|
10
10
|
export { materializeMdastTree } from "./mdast/mdast-materializer.js";
|
|
11
11
|
export { HastReader } from "./hast/hast-reader.js";
|
|
12
12
|
export { materializeHastTree } from "./hast/hast-materializer.js";
|
|
13
|
-
export { createMdastHandle, createMdxMdastHandle, createHastHandle, createMdxHastHandle,
|
|
13
|
+
export { createMdastHandle, createMdxMdastHandle, createHastHandle, createMdxHastHandle, serializeHandle, renderHandle, compileHandle, getHandleSource, } from "#binding";
|
|
14
|
+
import { applyCommandsToMdastHandle as napiApplyCommandsToMdastHandle, applyCommandsAndConvertToHastHandle as napiApplyCommandsAndConvertToHastHandle, convertMdastToHastHandle as napiConvertMdastToHastHandle, dropHandle as napiDropHandle, } from "#binding";
|
|
15
|
+
import { markHandleMutated } from "./lazy-child-resolver.js";
|
|
16
|
+
// The raw NAPI mutators renumber or empty the arena; without the epoch bump a
|
|
17
|
+
// child stub retained past a manual-pipeline pass would silently snapshot the
|
|
18
|
+
// changed arena (or die with an opaque RangeError) instead of hitting the
|
|
19
|
+
// retention error.
|
|
20
|
+
export function applyCommandsToMdastHandle(handle, commandBuf) {
|
|
21
|
+
markHandleMutated(handle);
|
|
22
|
+
return napiApplyCommandsToMdastHandle(handle, commandBuf);
|
|
23
|
+
}
|
|
24
|
+
export function convertMdastToHastHandle(handle, convertOptions) {
|
|
25
|
+
markHandleMutated(handle);
|
|
26
|
+
return napiConvertMdastToHastHandle(handle, convertOptions);
|
|
27
|
+
}
|
|
28
|
+
export function dropHandle(handle) {
|
|
29
|
+
markHandleMutated(handle);
|
|
30
|
+
napiDropHandle(handle);
|
|
31
|
+
}
|
|
32
|
+
export function applyCommandsAndConvertToHastHandle(handle, commandBuf, convertOptions) {
|
|
33
|
+
markHandleMutated(handle);
|
|
34
|
+
return napiApplyCommandsAndConvertToHastHandle(handle, commandBuf, convertOptions);
|
|
35
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { AnyHandle } from "./handles.js";
|
|
2
|
+
/** Record that `handle`'s arena was rebuilt. Resolvers created before the bump
|
|
3
|
+
* refuse to take a fresh snapshot afterwards (their ids are stale). */
|
|
4
|
+
export declare function markHandleMutated(handle: AnyHandle): void;
|
|
5
|
+
/**
|
|
6
|
+
* Lazy node materializer for the walk paths: serializes the handle's arena
|
|
7
|
+
* once, on the first stub materialization, then materializes nodes from that
|
|
8
|
+
* snapshot. Subclasses supply reader construction and per-node materialization
|
|
9
|
+
* so the hot path stays free of per-node closures.
|
|
10
|
+
*/
|
|
11
|
+
export declare abstract class LazyChildResolver<TReader, TNode> {
|
|
12
|
+
#private;
|
|
13
|
+
constructor(handle: AnyHandle);
|
|
14
|
+
protected abstract createReader(wire: Uint8Array): TReader;
|
|
15
|
+
protected abstract materializeNode(reader: TReader, nodeId: number): TNode;
|
|
16
|
+
protected abstract readParentId(reader: TReader, nodeId: number): number;
|
|
17
|
+
protected abstract readChildIds(reader: TReader, nodeId: number): number[];
|
|
18
|
+
/**
|
|
19
|
+
* Mark the visitor pass over. Child ids were captured at match time; once
|
|
20
|
+
* the pass's mutations land the arena is rebuilt and ids renumbered, so a
|
|
21
|
+
* later snapshot would map those stale ids onto the wrong nodes (or out of
|
|
22
|
+
* range). Failing loudly here beats silently wrong children.
|
|
23
|
+
*/
|
|
24
|
+
seal(): void;
|
|
25
|
+
/** Guards the `.children` getters: a first read after the pass is refused
|
|
26
|
+
* outright, even when no mutation landed — match-time ids cannot be trusted
|
|
27
|
+
* once the plugin no longer controls the tree. */
|
|
28
|
+
assertUnsealed(): void;
|
|
29
|
+
/** Materialize one node for a child stub's first real-field read. */
|
|
30
|
+
materializeOne(nodeId: number): TNode;
|
|
31
|
+
/** Arena id of `nodeId`'s parent in the pass snapshot, or undefined at the root. */
|
|
32
|
+
parentIdOf(nodeId: number): number | undefined;
|
|
33
|
+
/** Index of `nodeId` within its parent's children in the pass snapshot,
|
|
34
|
+
* or undefined at the root. */
|
|
35
|
+
indexInParent(nodeId: number): number | undefined;
|
|
36
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { serializeHandle } from "#binding";
|
|
2
|
+
/** Rebuild count per handle: bumped whenever a command buffer lands and
|
|
3
|
+
* renumbers the arena, invalidating ids captured before it. */
|
|
4
|
+
const HANDLE_EPOCHS = new WeakMap();
|
|
5
|
+
/** Record that `handle`'s arena was rebuilt. Resolvers created before the bump
|
|
6
|
+
* refuse to take a fresh snapshot afterwards (their ids are stale). */
|
|
7
|
+
export function markHandleMutated(handle) {
|
|
8
|
+
HANDLE_EPOCHS.set(handle, (HANDLE_EPOCHS.get(handle) ?? 0) + 1);
|
|
9
|
+
}
|
|
10
|
+
/** Arena sentinel in the node struct's parent field: the node has no parent. */
|
|
11
|
+
const NO_PARENT = 0xffffffff;
|
|
12
|
+
/**
|
|
13
|
+
* Lazy node materializer for the walk paths: serializes the handle's arena
|
|
14
|
+
* once, on the first stub materialization, then materializes nodes from that
|
|
15
|
+
* snapshot. Subclasses supply reader construction and per-node materialization
|
|
16
|
+
* so the hot path stays free of per-node closures.
|
|
17
|
+
*/
|
|
18
|
+
export class LazyChildResolver {
|
|
19
|
+
#handle;
|
|
20
|
+
#reader = null;
|
|
21
|
+
#sealed = false;
|
|
22
|
+
#epoch;
|
|
23
|
+
constructor(handle) {
|
|
24
|
+
this.#handle = handle;
|
|
25
|
+
this.#epoch = HANDLE_EPOCHS.get(handle) ?? 0;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Mark the visitor pass over. Child ids were captured at match time; once
|
|
29
|
+
* the pass's mutations land the arena is rebuilt and ids renumbered, so a
|
|
30
|
+
* later snapshot would map those stale ids onto the wrong nodes (or out of
|
|
31
|
+
* range). Failing loudly here beats silently wrong children.
|
|
32
|
+
*/
|
|
33
|
+
seal() {
|
|
34
|
+
this.#sealed = true;
|
|
35
|
+
}
|
|
36
|
+
/** Guards the `.children` getters: a first read after the pass is refused
|
|
37
|
+
* outright, even when no mutation landed — match-time ids cannot be trusted
|
|
38
|
+
* once the plugin no longer controls the tree. */
|
|
39
|
+
assertUnsealed() {
|
|
40
|
+
if (this.#sealed) {
|
|
41
|
+
throw new Error("Cannot read node content: this node was retained past its visitor pass " +
|
|
42
|
+
"and the tree may have changed since; read it inside the visitor.");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
#ensureReader() {
|
|
46
|
+
if (this.#reader === null) {
|
|
47
|
+
// A node id proves the tree was read in-pass, so a deferred snapshot is
|
|
48
|
+
// still faithful as long as no command buffer rebuilt the arena since
|
|
49
|
+
// match time (ids renumber on rebuild). An existing reader is always
|
|
50
|
+
// safe: the snapshot is an immutable pre-mutation copy.
|
|
51
|
+
if ((HANDLE_EPOCHS.get(this.#handle) ?? 0) !== this.#epoch) {
|
|
52
|
+
throw new Error("Cannot read node content: this node was retained past its visitor pass " +
|
|
53
|
+
"and the tree has changed since; read it inside the visitor.");
|
|
54
|
+
}
|
|
55
|
+
// The serialized buffer already carries each node's `data` blob (read
|
|
56
|
+
// eagerly by the materializer), and the arena isn't mutated mid-visit —
|
|
57
|
+
// so no separate lazy NAPI fetch is needed. This also keeps walk-path
|
|
58
|
+
// children consistent with the fully materialized tree (no `data` key
|
|
59
|
+
// when a node has none).
|
|
60
|
+
this.#reader = this.createReader(serializeHandle(this.#handle));
|
|
61
|
+
}
|
|
62
|
+
return this.#reader;
|
|
63
|
+
}
|
|
64
|
+
/** Materialize one node for a child stub's first real-field read. */
|
|
65
|
+
materializeOne(nodeId) {
|
|
66
|
+
return this.materializeNode(this.#ensureReader(), nodeId);
|
|
67
|
+
}
|
|
68
|
+
/** Arena id of `nodeId`'s parent in the pass snapshot, or undefined at the root. */
|
|
69
|
+
parentIdOf(nodeId) {
|
|
70
|
+
this.assertUnsealed();
|
|
71
|
+
const parentId = this.readParentId(this.#ensureReader(), nodeId);
|
|
72
|
+
return parentId === NO_PARENT ? undefined : parentId;
|
|
73
|
+
}
|
|
74
|
+
/** Per-parent child-id→index maps, built lazily: null until a plugin calls
|
|
75
|
+
* `indexInParent` (most never do). Cache-safe because the snapshot is immutable. */
|
|
76
|
+
#childIndexByParent = null;
|
|
77
|
+
/** Index of `nodeId` within its parent's children in the pass snapshot,
|
|
78
|
+
* or undefined at the root. */
|
|
79
|
+
indexInParent(nodeId) {
|
|
80
|
+
this.assertUnsealed();
|
|
81
|
+
const reader = this.#ensureReader();
|
|
82
|
+
const parentId = this.readParentId(reader, nodeId);
|
|
83
|
+
if (parentId === NO_PARENT)
|
|
84
|
+
return undefined;
|
|
85
|
+
const byParent = (this.#childIndexByParent ??= new Map());
|
|
86
|
+
let indexById = byParent.get(parentId);
|
|
87
|
+
if (indexById === undefined) {
|
|
88
|
+
const map = new Map();
|
|
89
|
+
this.readChildIds(reader, parentId).forEach((id, i) => map.set(id, i));
|
|
90
|
+
byParent.set(parentId, map);
|
|
91
|
+
indexById = map;
|
|
92
|
+
}
|
|
93
|
+
return indexById.get(nodeId);
|
|
94
|
+
}
|
|
95
|
+
}
|
package/dist/lazy-props.d.ts
CHANGED
|
@@ -7,4 +7,4 @@ export declare function lazyProp<T>(key: string, get: () => T): PropertyDescript
|
|
|
7
7
|
* Uses a shared resolve-once pattern: the first getter to fire reads all data,
|
|
8
8
|
* defines own properties for every key, then each per-key getter returns its value.
|
|
9
9
|
*/
|
|
10
|
-
export declare function lazyGroup(node: object, keys: string[], resolve: () => Record<string, unknown>): void;
|
|
10
|
+
export declare function lazyGroup(node: object, keys: readonly string[], resolve: () => Record<string, unknown>): void;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { LazyChildResolver } from "../lazy-child-resolver.js";
|
|
2
|
+
import type { MdastNode } from "../types.js";
|
|
3
|
+
import type { MdastReader } from "./mdast-reader.js";
|
|
4
|
+
type MdastResolver = LazyChildResolver<MdastReader, MdastNode>;
|
|
5
|
+
/**
|
|
6
|
+
* Walk-path child stub: arena id + `type` eagerly, every other field a lazy
|
|
7
|
+
* forward to the materialized node (first read snapshots the arena via
|
|
8
|
+
* `materializeOne`, which enforces the handle epoch — the pass seal is checked
|
|
9
|
+
* where the stubs are built). Spread/identity rules are enforced by `nid()`
|
|
10
|
+
* (authoritative doc in hast-visitor.ts).
|
|
11
|
+
*/
|
|
12
|
+
export declare class MdastChildStub {
|
|
13
|
+
_resolver: MdastResolver;
|
|
14
|
+
_id: number;
|
|
15
|
+
type: string;
|
|
16
|
+
constructor(resolver: MdastResolver, id: number, nodeType: number);
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { flatByTag, stubDescriptors } from "../child-stub.js";
|
|
2
|
+
import { MDAST_LAYOUT_KEYS } from "./generated/layout.js";
|
|
3
|
+
import { NAME_TO_TYPE, TYPE_NAMES } from "./generated/node-types.js";
|
|
4
|
+
import { LEAF_TYPES } from "./mdast-materializer.js";
|
|
5
|
+
const N = NAME_TO_TYPE;
|
|
6
|
+
/** Per-type stub fields for the types `addTypeProperties` hand-writes; the
|
|
7
|
+
* fixed-layout types come from the generated `MDAST_LAYOUT_KEYS`. */
|
|
8
|
+
const HAND_WRITTEN_FIELDS = {
|
|
9
|
+
[N.list]: ["ordered", "start", "spread"],
|
|
10
|
+
[N.listItem]: ["spread", "checked"],
|
|
11
|
+
[N.table]: ["align"],
|
|
12
|
+
[N.containerDirective]: ["name", "attributes"],
|
|
13
|
+
[N.leafDirective]: ["name", "attributes"],
|
|
14
|
+
[N.textDirective]: ["name", "attributes"],
|
|
15
|
+
[N.mdxJsxFlowElement]: ["name", "attributes"],
|
|
16
|
+
[N.mdxJsxTextElement]: ["name", "attributes"],
|
|
17
|
+
};
|
|
18
|
+
const TYPE_NAME_BY_TAG = flatByTag(TYPE_NAMES);
|
|
19
|
+
const MDAST_STUB_DESCRIPTORS = [];
|
|
20
|
+
for (const tag of Object.keys(TYPE_NAMES)) {
|
|
21
|
+
const nodeType = Number(tag);
|
|
22
|
+
const fields = [...(MDAST_LAYOUT_KEYS[nodeType] ?? HAND_WRITTEN_FIELDS[nodeType] ?? [])];
|
|
23
|
+
if (!LEAF_TYPES.has(nodeType))
|
|
24
|
+
fields.push("children");
|
|
25
|
+
MDAST_STUB_DESCRIPTORS[nodeType] = stubDescriptors(fields);
|
|
26
|
+
}
|
|
27
|
+
/** Unknown node types still expose the prelude-backed lazy fields. */
|
|
28
|
+
const FALLBACK_DESCRIPTORS = stubDescriptors([]);
|
|
29
|
+
/**
|
|
30
|
+
* Walk-path child stub: arena id + `type` eagerly, every other field a lazy
|
|
31
|
+
* forward to the materialized node (first read snapshots the arena via
|
|
32
|
+
* `materializeOne`, which enforces the handle epoch — the pass seal is checked
|
|
33
|
+
* where the stubs are built). Spread/identity rules are enforced by `nid()`
|
|
34
|
+
* (authoritative doc in hast-visitor.ts).
|
|
35
|
+
*/
|
|
36
|
+
export class MdastChildStub {
|
|
37
|
+
_resolver;
|
|
38
|
+
_id;
|
|
39
|
+
type;
|
|
40
|
+
constructor(resolver, id, nodeType) {
|
|
41
|
+
this._resolver = resolver;
|
|
42
|
+
this._id = id;
|
|
43
|
+
this.type = TYPE_NAME_BY_TAG[nodeType] ?? `unknown(${nodeType})`;
|
|
44
|
+
Object.defineProperties(this, MDAST_STUB_DESCRIPTORS[nodeType] ?? FALLBACK_DESCRIPTORS);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function decodeColumnAlign(byte: number): string | null;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Decode a table column-alignment byte — shared by the generated walk decoder
|
|
2
|
+
* and the snapshot reader so the mapping lives in one place. */
|
|
3
|
+
const ALIGN_NAMES = [null, "left", "right", "center"];
|
|
4
|
+
export function decodeColumnAlign(byte) {
|
|
5
|
+
return ALIGN_NAMES[byte] ?? null;
|
|
6
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { MdastReader } from "../mdast-reader.js";
|
|
2
|
+
/** Materialized property names per tag (the non-skip `js` names above),
|
|
3
|
+
* precomputed so `materializeMdastFields` doesn't rebuild them per node.
|
|
4
|
+
* Exported for the child-stub field tables. */
|
|
5
|
+
export declare const MDAST_LAYOUT_KEYS: Readonly<Record<number, readonly string[]>>;
|
|
6
|
+
/**
|
|
7
|
+
* Decode a node's type-specific `type_data` from the walk buffer onto `node`,
|
|
8
|
+
* driven by `MDAST_LAYOUTS` (fixed-field types) and `MDAST_TAILS` (counted
|
|
9
|
+
* attribute lists). Returns `false` for tags in neither, so the caller falls
|
|
10
|
+
* through to the remaining hand-written cases (list, listItem).
|
|
11
|
+
*/
|
|
12
|
+
export declare function decodeMdastTypeData(view: DataView, buf: Uint8Array, start: number, nodeType: number, node: Record<string, unknown>): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Attach a node's fixed `type_data` fields, materialized from the arena
|
|
15
|
+
* snapshot, driven by `MDAST_LAYOUTS`. Returns `false` for tags with no entry,
|
|
16
|
+
* so the materializer falls through to its hand-written cases (list, table,
|
|
17
|
+
* directives, MDX JSX). Fields resolve lazily on first access.
|
|
18
|
+
*/
|
|
19
|
+
export declare function materializeMdastFields(reader: MdastReader, node: object, nodeId: number, nodeType: number): boolean;
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
// @generated by `cargo run -p satteri-layout-codegen`. Do not edit by hand.
|
|
2
|
+
import { ru16, ru32, rstr } from "../../wire-read.js";
|
|
3
|
+
import { restorePhantomSpaces } from "../../phantom.js";
|
|
4
|
+
import { lazyGroup } from "../../lazy-props.js";
|
|
5
|
+
import { decodeMdxJsxAttr } from "../../mdx-attr.js";
|
|
6
|
+
import { decodeColumnAlign } from "../column-align.js";
|
|
7
|
+
/** Walk-wire fields per node-type tag (see Rust `write_mdast_type_data_inline`). */
|
|
8
|
+
const MDAST_LAYOUTS = {
|
|
9
|
+
2: [{ js: "depth", offset: 0, kind: "u8", default: 1 }],
|
|
10
|
+
7: [{ js: "value", offset: 0, kind: "str32" }],
|
|
11
|
+
10: [{ js: "value", offset: 0, kind: "str32" }],
|
|
12
|
+
13: [{ js: "value", offset: 0, kind: "str32" }],
|
|
13
|
+
25: [{ js: "value", offset: 0, kind: "str32" }],
|
|
14
|
+
26: [{ js: "value", offset: 0, kind: "str32" }],
|
|
15
|
+
8: [
|
|
16
|
+
{ js: "lang", offset: 0, kind: "str16", nullable: true },
|
|
17
|
+
{ js: "meta", offset: 8, kind: "str16", nullable: true },
|
|
18
|
+
{ js: "value", offset: 16, kind: "str32" },
|
|
19
|
+
],
|
|
20
|
+
9: [
|
|
21
|
+
{ js: "url", offset: 0, kind: "str16" },
|
|
22
|
+
{ js: "title", offset: 8, kind: "str16", nullable: true },
|
|
23
|
+
{ js: "identifier", offset: 16, kind: "str16" },
|
|
24
|
+
{ js: "label", offset: 24, kind: "str16" },
|
|
25
|
+
],
|
|
26
|
+
15: [
|
|
27
|
+
{ js: "url", offset: 0, kind: "str16" },
|
|
28
|
+
{ js: "title", offset: 8, kind: "str16", nullable: true },
|
|
29
|
+
],
|
|
30
|
+
16: [
|
|
31
|
+
{ js: "url", offset: 0, kind: "str16" },
|
|
32
|
+
{ js: "alt", offset: 8, kind: "str16" },
|
|
33
|
+
{ js: "title", offset: 16, kind: "str16", nullable: true },
|
|
34
|
+
],
|
|
35
|
+
17: [
|
|
36
|
+
{ js: "identifier", offset: 0, kind: "str16" },
|
|
37
|
+
{ js: "label", offset: 8, kind: "str16" },
|
|
38
|
+
{ js: "referenceType", offset: 16, kind: "u8", values: ["shortcut", "collapsed", "full"] },
|
|
39
|
+
],
|
|
40
|
+
18: [
|
|
41
|
+
{ js: "identifier", offset: 0, kind: "str16" },
|
|
42
|
+
{ js: "label", offset: 8, kind: "str16" },
|
|
43
|
+
{ js: "referenceType", offset: 16, kind: "u8", values: ["shortcut", "collapsed", "full"] },
|
|
44
|
+
{ js: "alt", offset: 20, kind: "str16" },
|
|
45
|
+
],
|
|
46
|
+
19: [
|
|
47
|
+
{ js: "identifier", offset: 0, kind: "str16" },
|
|
48
|
+
{ js: "label", offset: 8, kind: "str16" },
|
|
49
|
+
],
|
|
50
|
+
20: [
|
|
51
|
+
{ js: "identifier", offset: 0, kind: "str16" },
|
|
52
|
+
{ js: "label", offset: 8, kind: "str16" },
|
|
53
|
+
{ js: "", offset: 16, kind: "u8", skip: true },
|
|
54
|
+
],
|
|
55
|
+
27: [
|
|
56
|
+
{ js: "meta", offset: 0, kind: "str16", nullable: true },
|
|
57
|
+
{ js: "value", offset: 8, kind: "str32" },
|
|
58
|
+
],
|
|
59
|
+
28: [{ js: "value", offset: 8, kind: "str32" }],
|
|
60
|
+
102: [{ js: "value", offset: 0, kind: "str32", phantom: true }],
|
|
61
|
+
103: [{ js: "value", offset: 0, kind: "str32", phantom: true }],
|
|
62
|
+
104: [{ js: "value", offset: 0, kind: "str32", phantom: true }],
|
|
63
|
+
};
|
|
64
|
+
/** Materialized property names per tag (the non-skip `js` names above),
|
|
65
|
+
* precomputed so `materializeMdastFields` doesn't rebuild them per node.
|
|
66
|
+
* Exported for the child-stub field tables. */
|
|
67
|
+
export const MDAST_LAYOUT_KEYS = {
|
|
68
|
+
2: ["depth"],
|
|
69
|
+
7: ["value"],
|
|
70
|
+
10: ["value"],
|
|
71
|
+
13: ["value"],
|
|
72
|
+
25: ["value"],
|
|
73
|
+
26: ["value"],
|
|
74
|
+
8: ["lang", "meta", "value"],
|
|
75
|
+
9: ["url", "title", "identifier", "label"],
|
|
76
|
+
15: ["url", "title"],
|
|
77
|
+
16: ["url", "alt", "title"],
|
|
78
|
+
17: ["identifier", "label", "referenceType"],
|
|
79
|
+
18: ["identifier", "label", "referenceType", "alt"],
|
|
80
|
+
19: ["identifier", "label"],
|
|
81
|
+
20: ["identifier", "label"],
|
|
82
|
+
27: ["meta", "value"],
|
|
83
|
+
28: ["value"],
|
|
84
|
+
102: ["value"],
|
|
85
|
+
103: ["value"],
|
|
86
|
+
104: ["value"],
|
|
87
|
+
};
|
|
88
|
+
/** Walk-wire tail descriptors (a name head, then a counted item list) for
|
|
89
|
+
* the variable-length attribute types the generic decoder below handles
|
|
90
|
+
* without a hand-written case: `map` is a string key/value object
|
|
91
|
+
* (directives), `jsx` a typed MDX-JSX attribute array. */
|
|
92
|
+
const MDAST_TAILS = {
|
|
93
|
+
21: { head: [], item: [{ js: "align", kind: "u8" }], bytes: { attrsKey: "align" } },
|
|
94
|
+
30: {
|
|
95
|
+
head: [{ js: "name", kind: "str16" }],
|
|
96
|
+
item: [
|
|
97
|
+
{ js: "key", kind: "str16" },
|
|
98
|
+
{ js: "value", kind: "str16" },
|
|
99
|
+
],
|
|
100
|
+
map: { attrsKey: "attributes", key: "key", value: "value" },
|
|
101
|
+
},
|
|
102
|
+
31: {
|
|
103
|
+
head: [{ js: "name", kind: "str16" }],
|
|
104
|
+
item: [
|
|
105
|
+
{ js: "key", kind: "str16" },
|
|
106
|
+
{ js: "value", kind: "str16" },
|
|
107
|
+
],
|
|
108
|
+
map: { attrsKey: "attributes", key: "key", value: "value" },
|
|
109
|
+
},
|
|
110
|
+
32: {
|
|
111
|
+
head: [{ js: "name", kind: "str16" }],
|
|
112
|
+
item: [
|
|
113
|
+
{ js: "key", kind: "str16" },
|
|
114
|
+
{ js: "value", kind: "str16" },
|
|
115
|
+
],
|
|
116
|
+
map: { attrsKey: "attributes", key: "key", value: "value" },
|
|
117
|
+
},
|
|
118
|
+
100: {
|
|
119
|
+
head: [{ js: "name", kind: "str16" }],
|
|
120
|
+
item: [
|
|
121
|
+
{ js: "kind", kind: "u8" },
|
|
122
|
+
{ js: "name", kind: "str16" },
|
|
123
|
+
{ js: "value", kind: "str32" },
|
|
124
|
+
],
|
|
125
|
+
jsx: { attrsKey: "attributes" },
|
|
126
|
+
},
|
|
127
|
+
101: {
|
|
128
|
+
head: [{ js: "name", kind: "str16" }],
|
|
129
|
+
item: [
|
|
130
|
+
{ js: "kind", kind: "u8" },
|
|
131
|
+
{ js: "name", kind: "str16" },
|
|
132
|
+
{ js: "value", kind: "str32" },
|
|
133
|
+
],
|
|
134
|
+
jsx: { attrsKey: "attributes" },
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* Decode a node's type-specific `type_data` from the walk buffer onto `node`,
|
|
139
|
+
* driven by `MDAST_LAYOUTS` (fixed-field types) and `MDAST_TAILS` (counted
|
|
140
|
+
* attribute lists). Returns `false` for tags in neither, so the caller falls
|
|
141
|
+
* through to the remaining hand-written cases (list, listItem).
|
|
142
|
+
*/
|
|
143
|
+
export function decodeMdastTypeData(view, buf, start, nodeType, node) {
|
|
144
|
+
const fields = MDAST_LAYOUTS[nodeType];
|
|
145
|
+
if (fields !== undefined) {
|
|
146
|
+
let pos = start;
|
|
147
|
+
for (const f of fields) {
|
|
148
|
+
if (f.kind === "u8") {
|
|
149
|
+
const b = buf[pos];
|
|
150
|
+
pos += 1;
|
|
151
|
+
if (f.skip)
|
|
152
|
+
continue;
|
|
153
|
+
node[f.js] = f.values ? (f.values[b] ?? f.values[0]) : b;
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
const len = f.kind === "str32" ? ru32(view, pos) : ru16(view, pos);
|
|
157
|
+
pos += f.kind === "str32" ? 4 : 2;
|
|
158
|
+
const raw = rstr(buf, pos, len);
|
|
159
|
+
pos += len;
|
|
160
|
+
if (f.skip)
|
|
161
|
+
continue;
|
|
162
|
+
node[f.js] = f.nullable && len === 0 ? null : f.phantom ? restorePhantomSpaces(raw) : raw;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
const tail = MDAST_TAILS[nodeType];
|
|
168
|
+
if (tail !== undefined) {
|
|
169
|
+
let pos = start;
|
|
170
|
+
// Reads are inlined and advance `pos` in place (no per-field tuple): this
|
|
171
|
+
// runs per attribute item in the matched-node decode path, and the
|
|
172
|
+
// unoptimized tiers CodSpeed measures won't elide the allocation.
|
|
173
|
+
for (const f of tail.head) {
|
|
174
|
+
let value;
|
|
175
|
+
if (f.kind === "u8") {
|
|
176
|
+
value = buf[pos];
|
|
177
|
+
pos += 1;
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
const len = f.kind === "str32" ? ru32(view, pos) : ru16(view, pos);
|
|
181
|
+
pos += f.kind === "str32" ? 4 : 2;
|
|
182
|
+
const raw = rstr(buf, pos, len);
|
|
183
|
+
pos += len;
|
|
184
|
+
value = f.phantom ? restorePhantomSpaces(raw) : raw;
|
|
185
|
+
}
|
|
186
|
+
// MDX JSX elements carry a nullable name (empty → null); map heads keep it.
|
|
187
|
+
node[f.js] = tail.jsx !== undefined && value === "" ? null : value;
|
|
188
|
+
}
|
|
189
|
+
const count = ru16(view, pos);
|
|
190
|
+
pos += 2;
|
|
191
|
+
const readItem = () => {
|
|
192
|
+
const item = {};
|
|
193
|
+
for (const f of tail.item) {
|
|
194
|
+
if (f.kind === "u8") {
|
|
195
|
+
item[f.js] = buf[pos];
|
|
196
|
+
pos += 1;
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
const len = f.kind === "str32" ? ru32(view, pos) : ru16(view, pos);
|
|
200
|
+
pos += f.kind === "str32" ? 4 : 2;
|
|
201
|
+
const raw = rstr(buf, pos, len);
|
|
202
|
+
pos += len;
|
|
203
|
+
item[f.js] = f.phantom ? restorePhantomSpaces(raw) : raw;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return item;
|
|
207
|
+
};
|
|
208
|
+
if (tail.map !== undefined) {
|
|
209
|
+
const attrs = {};
|
|
210
|
+
for (let i = 0; i < count; i++) {
|
|
211
|
+
const item = readItem();
|
|
212
|
+
attrs[item[tail.map.key]] = item[tail.map.value];
|
|
213
|
+
}
|
|
214
|
+
node[tail.map.attrsKey] = attrs;
|
|
215
|
+
}
|
|
216
|
+
else if (tail.jsx !== undefined) {
|
|
217
|
+
const attrs = [];
|
|
218
|
+
for (let i = 0; i < count; i++) {
|
|
219
|
+
const item = readItem();
|
|
220
|
+
attrs.push(decodeMdxJsxAttr(item.kind, item.name, item.value));
|
|
221
|
+
}
|
|
222
|
+
node[tail.jsx.attrsKey] = attrs;
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
const align = [];
|
|
226
|
+
for (let i = 0; i < count; i++) {
|
|
227
|
+
const item = readItem();
|
|
228
|
+
align.push(decodeColumnAlign(item.align));
|
|
229
|
+
}
|
|
230
|
+
node[tail.bytes.attrsKey] = align;
|
|
231
|
+
}
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Attach a node's fixed `type_data` fields, materialized from the arena
|
|
238
|
+
* snapshot, driven by `MDAST_LAYOUTS`. Returns `false` for tags with no entry,
|
|
239
|
+
* so the materializer falls through to its hand-written cases (list, table,
|
|
240
|
+
* directives, MDX JSX). Fields resolve lazily on first access.
|
|
241
|
+
*/
|
|
242
|
+
export function materializeMdastFields(reader, node, nodeId, nodeType) {
|
|
243
|
+
const fields = MDAST_LAYOUTS[nodeType];
|
|
244
|
+
if (fields === undefined)
|
|
245
|
+
return false;
|
|
246
|
+
lazyGroup(node, MDAST_LAYOUT_KEYS[nodeType], () => {
|
|
247
|
+
const data = reader.getTypeData(nodeId);
|
|
248
|
+
const out = {};
|
|
249
|
+
for (const f of fields) {
|
|
250
|
+
if (f.kind === "u8") {
|
|
251
|
+
// Short type_data falls back to the layout's declared default,
|
|
252
|
+
// matching the Rust walk serializer.
|
|
253
|
+
const b = data[f.offset] ?? f.default ?? 0;
|
|
254
|
+
if (!f.skip)
|
|
255
|
+
out[f.js] = f.values ? (f.values[b] ?? f.values[0]) : b;
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
// Short type_data (e.g. a 20-byte ReferenceData-only imageReference)
|
|
259
|
+
// yields an empty ref, mirroring the Rust decoders' bounds checks.
|
|
260
|
+
const ref = f.offset + 8 <= data.length
|
|
261
|
+
? reader.readStringRef(data, f.offset)
|
|
262
|
+
: { offset: 0, len: 0 };
|
|
263
|
+
if (f.skip)
|
|
264
|
+
continue;
|
|
265
|
+
const s = f.nullable && ref.len === 0 ? null : reader.getString(ref.offset, ref.len);
|
|
266
|
+
out[f.js] = f.phantom && typeof s === "string" ? restorePhantomSpaces(s) : s;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return out;
|
|
270
|
+
});
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/** Node-type tag -> canonical AST name. */
|
|
2
|
+
export declare const TYPE_NAMES: Readonly<Record<number, string>>;
|
|
3
|
+
/** Canonical AST name -> node-type tag. */
|
|
4
|
+
export declare const NAME_TO_TYPE: Readonly<Record<string, number>>;
|
|
5
|
+
/** Node-type tag by Rust enum variant name. */
|
|
6
|
+
export declare const NodeType: Readonly<{
|
|
7
|
+
readonly Root: 0;
|
|
8
|
+
readonly Paragraph: 1;
|
|
9
|
+
readonly Heading: 2;
|
|
10
|
+
readonly ThematicBreak: 3;
|
|
11
|
+
readonly Blockquote: 4;
|
|
12
|
+
readonly List: 5;
|
|
13
|
+
readonly ListItem: 6;
|
|
14
|
+
readonly Html: 7;
|
|
15
|
+
readonly Code: 8;
|
|
16
|
+
readonly Definition: 9;
|
|
17
|
+
readonly Text: 10;
|
|
18
|
+
readonly Emphasis: 11;
|
|
19
|
+
readonly Strong: 12;
|
|
20
|
+
readonly InlineCode: 13;
|
|
21
|
+
readonly Break: 14;
|
|
22
|
+
readonly Link: 15;
|
|
23
|
+
readonly Image: 16;
|
|
24
|
+
readonly LinkReference: 17;
|
|
25
|
+
readonly ImageReference: 18;
|
|
26
|
+
readonly FootnoteDefinition: 19;
|
|
27
|
+
readonly FootnoteReference: 20;
|
|
28
|
+
readonly Table: 21;
|
|
29
|
+
readonly TableRow: 22;
|
|
30
|
+
readonly TableCell: 23;
|
|
31
|
+
readonly Delete: 24;
|
|
32
|
+
readonly Yaml: 25;
|
|
33
|
+
readonly Toml: 26;
|
|
34
|
+
readonly Math: 27;
|
|
35
|
+
readonly InlineMath: 28;
|
|
36
|
+
readonly ContainerDirective: 30;
|
|
37
|
+
readonly LeafDirective: 31;
|
|
38
|
+
readonly TextDirective: 32;
|
|
39
|
+
readonly Superscript: 33;
|
|
40
|
+
readonly Subscript: 34;
|
|
41
|
+
readonly MdxJsxFlowElement: 100;
|
|
42
|
+
readonly MdxJsxTextElement: 101;
|
|
43
|
+
readonly MdxFlowExpression: 102;
|
|
44
|
+
readonly MdxTextExpression: 103;
|
|
45
|
+
readonly MdxjsEsm: 104;
|
|
46
|
+
}>;
|
|
47
|
+
/** Node-type tag -> Rust variant name (`typeName` diagnostics). */
|
|
48
|
+
export declare const NodeTypeName: Readonly<Record<number, string>>;
|
|
49
|
+
/** Names a plugin can subscribe to (every node except `root`). */
|
|
50
|
+
export declare const VISITOR_KEYS: ReadonlySet<string>;
|
|
51
|
+
/** Name -> tag for the types the op-stream can encode; one lookup gates AND
|
|
52
|
+
* resolves the emit-path tag. Excluded names (see `*_OPSTREAM_EXCLUDED` in
|
|
53
|
+
* schema.rs) have no encoding — the visitor throws for them. */
|
|
54
|
+
export declare const MDAST_OPSTREAM_TYPES: Readonly<Record<string, number>>;
|
|
55
|
+
/** Names of the variable-length `custom` node types (hand-written or
|
|
56
|
+
* `Tail`-generated codec). The op-stream round-trip oracle asserts it covers
|
|
57
|
+
* every one, so a forgotten or drifted encode/decode arm fails loudly. */
|
|
58
|
+
export declare const MDAST_CUSTOM_TYPES: readonly string[];
|