satteri 0.9.4 → 0.10.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 +11 -1
- 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 +4 -0
- package/dist/byte-writer.js +13 -4
- package/dist/child-stub.d.ts +7 -3
- package/dist/child-stub.js +18 -7
- package/dist/command-buffer.d.ts +36 -50
- package/dist/command-buffer.js +102 -49
- package/dist/compile.d.ts +80 -32
- package/dist/compile.js +295 -70
- package/dist/freeze.d.ts +2 -0
- package/dist/freeze.js +10 -0
- package/dist/generated/wire-constants.d.ts +3 -2
- package/dist/generated/wire-constants.js +4 -2
- package/dist/hast/child-stub.d.ts +2 -3
- package/dist/hast/child-stub.js +18 -11
- package/dist/hast/element-props.d.ts +1 -1
- package/dist/hast/element-props.js +14 -6
- package/dist/hast/hast-materializer.d.ts +6 -2
- package/dist/hast/hast-materializer.js +48 -134
- package/dist/hast/hast-reader.d.ts +7 -13
- package/dist/hast/hast-reader.js +76 -21
- package/dist/hast/hast-visitor.d.ts +55 -5
- package/dist/hast/hast-visitor.js +320 -154
- package/dist/index.d.ts +6 -6
- package/dist/index.js +1 -1
- package/dist/lazy-child-resolver.d.ts +20 -13
- package/dist/lazy-child-resolver.js +66 -38
- package/dist/materializer-cache.d.ts +42 -0
- package/dist/materializer-cache.js +192 -0
- package/dist/mdast/child-stub.d.ts +6 -3
- package/dist/mdast/child-stub.js +71 -8
- package/dist/mdast/generated/layout.d.ts +2 -3
- package/dist/mdast/generated/layout.js +24 -28
- package/dist/mdast/generated/node-types.d.ts +4 -0
- package/dist/mdast/generated/node-types.js +25 -0
- package/dist/mdast/mdast-materializer.d.ts +12 -3
- package/dist/mdast/mdast-materializer.js +83 -78
- package/dist/mdast/mdast-reader.d.ts +15 -0
- package/dist/mdast/mdast-reader.js +55 -10
- package/dist/mdast/mdast-visitor.d.ts +79 -32
- package/dist/mdast/mdast-visitor.js +305 -122
- package/dist/op-stream.d.ts +5 -12
- package/dist/op-stream.js +6 -21
- package/dist/plugin.d.ts +36 -10
- package/dist/plugin.js +50 -2
- package/dist/string-pool.d.ts +13 -0
- package/dist/string-pool.js +91 -0
- package/dist/types.d.ts +44 -1
- package/dist/visitor-shared.d.ts +27 -4
- package/dist/visitor-shared.js +36 -10
- package/dist/wire-read.js +3 -1
- package/index.d.ts +130 -7
- package/index.js +89 -68
- package/package.json +24 -19
- package/dist/lazy-props.d.ts +0 -10
- package/dist/lazy-props.js +0 -50
- package/satteri_napi.wasi-browser.js +0 -83
- package/satteri_napi.wasi.cjs +0 -134
- package/wasi-worker-browser.mjs +0 -36
- package/wasi-worker.mjs +0 -63
package/README.md
CHANGED
|
@@ -31,9 +31,19 @@ import { mdxToJs } from "satteri";
|
|
|
31
31
|
const { code } = mdxToJs("# Hello\n\n<MyComponent />");
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
### Markdown to JS
|
|
35
|
+
|
|
36
|
+
Like `mdxToJs`, but the source is plain Markdown: `{...}` expressions, JSX tags, and `import`/`export` lines are ordinary text instead of MDX syntax.
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { markdownToJs } from "satteri";
|
|
40
|
+
|
|
41
|
+
const { code } = markdownToJs("# Hello\n\n{not an expression}");
|
|
42
|
+
```
|
|
43
|
+
|
|
34
44
|
### With plugins
|
|
35
45
|
|
|
36
|
-
|
|
46
|
+
All three functions accept `mdastPlugins` (operate on the Markdown AST) and `hastPlugins` (operate on the HTML AST). A plugin is an object with a `name` and a visitor per node type; `defineMdastPlugin` / `defineHastPlugin` add type inference.
|
|
37
47
|
|
|
38
48
|
```ts
|
|
39
49
|
import { markdownToHtml, defineMdastPlugin } from "satteri";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { applyCommandsAndConvertToHastHandle, applyCommandsToHandle, applyCommandsToMdastHandle, compileHandle, compileMdx, convertMdastToHastHandle, createHastHandle, createMdastHandle, createMdxHastHandle, createMdxMdastHandle, dropHandle, getHandleSource, getMdastFrontmatter, getNodeData, mdastTextContentHandle, parseEsm, parseExpression, parseToHtml, renderHandle, serializeHandle, setNodeData, textContentHandle, walkHandle, walkMdastHandle, } from "../browser.js";
|
|
1
|
+
export { applyCommandsAndCompileHandle, applyCommandsAndConvertToHastHandle, applyCommandsAndRenderHandle, applyCommandsToHandle, applyCommandsToMdastHandle, applyMdastCommandsAndConvertAndCompile, applyMdastCommandsAndConvertAndRender, compileHandle, compileMdx, convertMdastToHastHandle, createHastHandle, createHastHandleFromHtml, createHastHandleWithFrontmatter, createMdastHandle, createMdxHastHandle, createMdxHastHandleWithFrontmatter, createMdxMdastHandle, dropHandle, getHandleSource, getMdastFrontmatter, getNodeData, markdownToHtmlFast, markdownToJsFast, mdastTextContentHandle, mdxToJsFast, parseEsm, parseExpression, parseToHtml, renderHandle, serializeHandle, setNodeData, textContentHandle, walkHandle, walkMdastHandle, } from "../browser.js";
|
package/dist/binding.browser.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @ts-nocheck — WASM browser binding has no type declarations
|
|
2
|
-
export { applyCommandsAndConvertToHastHandle, applyCommandsToHandle, applyCommandsToMdastHandle, compileHandle, compileMdx, convertMdastToHastHandle, createHastHandle, createMdastHandle, createMdxHastHandle, createMdxMdastHandle, dropHandle, getHandleSource, getMdastFrontmatter, getNodeData, mdastTextContentHandle, parseEsm, parseExpression, parseToHtml, renderHandle, serializeHandle, setNodeData, textContentHandle, walkHandle, walkMdastHandle, } from "../browser.js";
|
|
2
|
+
export { applyCommandsAndCompileHandle, applyCommandsAndConvertToHastHandle, applyCommandsAndRenderHandle, applyCommandsToHandle, applyCommandsToMdastHandle, applyMdastCommandsAndConvertAndCompile, applyMdastCommandsAndConvertAndRender, compileHandle, compileMdx, convertMdastToHastHandle, createHastHandle, createHastHandleFromHtml, createHastHandleWithFrontmatter, createMdastHandle, createMdxHastHandle, createMdxHastHandleWithFrontmatter, createMdxMdastHandle, dropHandle, getHandleSource, getMdastFrontmatter, getNodeData, markdownToHtmlFast, markdownToJsFast, mdastTextContentHandle, mdxToJsFast, parseEsm, parseExpression, parseToHtml, renderHandle, serializeHandle, setNodeData, textContentHandle, walkHandle, walkMdastHandle, } from "../browser.js";
|
package/dist/binding.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { applyCommandsAndConvertToHastHandle, applyCommandsToHandle, applyCommandsToMdastHandle, compileHandle, compileMdx, convertMdastToHastHandle, createHastHandle, createMdastHandle, createMdxHastHandle, createMdxMdastHandle, dropHandle, getHandleSource, getMdastFrontmatter, getNodeData, mdastTextContentHandle, parseEsm, parseExpression, parseToHtml, renderHandle, serializeHandle, setNodeData, textContentHandle, walkHandle, walkMdastHandle, } from "../index.js";
|
|
1
|
+
export { applyCommandsAndCompileHandle, applyCommandsAndConvertToHastHandle, applyCommandsAndRenderHandle, applyCommandsToHandle, applyCommandsToMdastHandle, applyMdastCommandsAndConvertAndCompile, applyMdastCommandsAndConvertAndRender, compileHandle, compileMdx, convertMdastToHastHandle, createHastHandle, createHastHandleFromHtml, createHastHandleWithFrontmatter, createMdastHandle, createMdxHastHandle, createMdxHastHandleWithFrontmatter, createMdxMdastHandle, dropHandle, getHandleSource, getMdastFrontmatter, getNodeData, markdownToHtmlFast, markdownToJsFast, mdastTextContentHandle, mdxToJsFast, parseEsm, parseExpression, parseToHtml, renderHandle, serializeHandle, setNodeData, textContentHandle, walkHandle, walkMdastHandle, } from "../index.js";
|
package/dist/binding.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { applyCommandsAndConvertToHastHandle, applyCommandsToHandle, applyCommandsToMdastHandle, compileHandle, compileMdx, convertMdastToHastHandle, createHastHandle, createMdastHandle, createMdxHastHandle, createMdxMdastHandle, dropHandle, getHandleSource, getMdastFrontmatter, getNodeData, mdastTextContentHandle, parseEsm, parseExpression, parseToHtml, renderHandle, serializeHandle, setNodeData, textContentHandle, walkHandle, walkMdastHandle, } from "../index.js";
|
|
1
|
+
export { applyCommandsAndCompileHandle, applyCommandsAndConvertToHastHandle, applyCommandsAndRenderHandle, applyCommandsToHandle, applyCommandsToMdastHandle, applyMdastCommandsAndConvertAndCompile, applyMdastCommandsAndConvertAndRender, compileHandle, compileMdx, convertMdastToHastHandle, createHastHandle, createHastHandleFromHtml, createHastHandleWithFrontmatter, createMdastHandle, createMdxHastHandle, createMdxHastHandleWithFrontmatter, createMdxMdastHandle, dropHandle, getHandleSource, getMdastFrontmatter, getNodeData, markdownToHtmlFast, markdownToJsFast, mdastTextContentHandle, mdxToJsFast, parseEsm, parseExpression, parseToHtml, renderHandle, serializeHandle, setNodeData, textContentHandle, walkHandle, walkMdastHandle, } from "../index.js";
|
package/dist/byte-writer.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export declare class ByteWriter {
|
|
|
16
16
|
constructor(initialSize: number);
|
|
17
17
|
/** Number of bytes written so far. */
|
|
18
18
|
get length(): number;
|
|
19
|
+
get capacity(): number;
|
|
19
20
|
/** Reset for reuse; the grown buffer is retained so steady state is alloc-free. */
|
|
20
21
|
reset(): void;
|
|
21
22
|
/** View of the bytes written so far (no copy; valid until the next write or reset). */
|
|
@@ -28,4 +29,7 @@ export declare class ByteWriter {
|
|
|
28
29
|
protected writeU32(v: number): void;
|
|
29
30
|
/** u32 byte length + UTF-8 bytes (self-ensuring). */
|
|
30
31
|
protected utf8WithU32Len(s: string): void;
|
|
32
|
+
/** Backpatch a u32 (LE) at `pos` in the already-written region; the cursor
|
|
33
|
+
* is left untouched. */
|
|
34
|
+
protected patchU32(pos: number, v: number): void;
|
|
31
35
|
}
|
package/dist/byte-writer.js
CHANGED
|
@@ -29,6 +29,9 @@ export class ByteWriter {
|
|
|
29
29
|
get length() {
|
|
30
30
|
return this.n;
|
|
31
31
|
}
|
|
32
|
+
get capacity() {
|
|
33
|
+
return this.buf.length;
|
|
34
|
+
}
|
|
32
35
|
/** Reset for reuse; the grown buffer is retained so steady state is alloc-free. */
|
|
33
36
|
reset() {
|
|
34
37
|
this.n = 0;
|
|
@@ -97,10 +100,16 @@ export class ByteWriter {
|
|
|
97
100
|
const lenPos = this.n;
|
|
98
101
|
this.n += 4;
|
|
99
102
|
const written = encoder.encodeInto(s, this.buf.subarray(this.n)).written;
|
|
100
|
-
this.
|
|
101
|
-
this.buf[lenPos + 1] = (written >> 8) & 255;
|
|
102
|
-
this.buf[lenPos + 2] = (written >> 16) & 255;
|
|
103
|
-
this.buf[lenPos + 3] = (written >>> 24) & 255;
|
|
103
|
+
this.patchU32(lenPos, written);
|
|
104
104
|
this.n += written;
|
|
105
105
|
}
|
|
106
|
+
/** Backpatch a u32 (LE) at `pos` in the already-written region; the cursor
|
|
107
|
+
* is left untouched. */
|
|
108
|
+
patchU32(pos, v) {
|
|
109
|
+
const buf = this.buf;
|
|
110
|
+
buf[pos] = v & 255;
|
|
111
|
+
buf[pos + 1] = (v >> 8) & 255;
|
|
112
|
+
buf[pos + 2] = (v >> 16) & 255;
|
|
113
|
+
buf[pos + 3] = (v >>> 24) & 255;
|
|
114
|
+
}
|
|
106
115
|
}
|
package/dist/child-stub.d.ts
CHANGED
|
@@ -5,13 +5,17 @@
|
|
|
5
5
|
* Passthrough children (the common replaceNode shape) thus compile to one-word
|
|
6
6
|
* refs without ever serializing the arena.
|
|
7
7
|
*/
|
|
8
|
+
export type StubDescriptorEntry = readonly [string, PropertyDescriptor];
|
|
8
9
|
/**
|
|
9
|
-
* Descriptor
|
|
10
|
+
* Descriptor entries for one node type's stub fields. Own enumerable getters
|
|
10
11
|
* so a spread copy carries correct values. `position`/`data` ride on every
|
|
11
12
|
* stub; they may yield `undefined` where a materialized node omits the key —
|
|
12
|
-
* accepted drift, invisible to `toEqual`.
|
|
13
|
+
* accepted drift, invisible to `toEqual`. Installed one `defineProperty` at a
|
|
14
|
+
* time: `Object.defineProperties` re-validates the whole map per call and
|
|
15
|
+
* costs nearly double.
|
|
13
16
|
*/
|
|
14
|
-
export declare function stubDescriptors(fields: readonly string[]):
|
|
17
|
+
export declare function stubDescriptors(fields: readonly string[]): readonly StubDescriptorEntry[];
|
|
18
|
+
export declare function installStubDescriptors(host: object, entries: readonly StubDescriptorEntry[]): void;
|
|
15
19
|
/** Node tags are dense small ints, so the per-child stub loop indexes flat
|
|
16
20
|
* arrays instead of paying Map/dictionary lookups. */
|
|
17
21
|
export declare function flatByTag<T>(table: Readonly<Record<number, T>>): readonly (T | undefined)[];
|
package/dist/child-stub.js
CHANGED
|
@@ -20,11 +20,12 @@ function fieldGetter(key) {
|
|
|
20
20
|
REAL_NODES.set(this, real);
|
|
21
21
|
}
|
|
22
22
|
const value = real[key];
|
|
23
|
+
// Mirrors the deep-frozen node it forwards to; nothing redefines a stub field after this.
|
|
23
24
|
Object.defineProperty(this, key, {
|
|
24
25
|
value,
|
|
25
|
-
writable:
|
|
26
|
+
writable: false,
|
|
26
27
|
enumerable: true,
|
|
27
|
-
configurable:
|
|
28
|
+
configurable: false,
|
|
28
29
|
});
|
|
29
30
|
return value;
|
|
30
31
|
};
|
|
@@ -33,17 +34,27 @@ function fieldGetter(key) {
|
|
|
33
34
|
return getter;
|
|
34
35
|
}
|
|
35
36
|
/**
|
|
36
|
-
* Descriptor
|
|
37
|
+
* Descriptor entries for one node type's stub fields. Own enumerable getters
|
|
37
38
|
* so a spread copy carries correct values. `position`/`data` ride on every
|
|
38
39
|
* stub; they may yield `undefined` where a materialized node omits the key —
|
|
39
|
-
* accepted drift, invisible to `toEqual`.
|
|
40
|
+
* accepted drift, invisible to `toEqual`. Installed one `defineProperty` at a
|
|
41
|
+
* time: `Object.defineProperties` re-validates the whole map per call and
|
|
42
|
+
* costs nearly double.
|
|
40
43
|
*/
|
|
41
44
|
export function stubDescriptors(fields) {
|
|
42
|
-
const
|
|
45
|
+
const entries = [];
|
|
43
46
|
for (const key of [...fields, "position", "data"]) {
|
|
44
|
-
|
|
47
|
+
entries.push([key, { get: fieldGetter(key), enumerable: true, configurable: true }]);
|
|
48
|
+
}
|
|
49
|
+
return entries;
|
|
50
|
+
}
|
|
51
|
+
export function installStubDescriptors(host, entries) {
|
|
52
|
+
for (let i = 0; i < entries.length; i++) {
|
|
53
|
+
const e = entries[i];
|
|
54
|
+
if (e === undefined)
|
|
55
|
+
continue;
|
|
56
|
+
Object.defineProperty(host, e[0], e[1]);
|
|
45
57
|
}
|
|
46
|
-
return map;
|
|
47
58
|
}
|
|
48
59
|
/** Node tags are dense small ints, so the per-child stub loop indexes flat
|
|
49
60
|
* arrays instead of paying Map/dictionary lookups. */
|
package/dist/command-buffer.d.ts
CHANGED
|
@@ -10,65 +10,51 @@
|
|
|
10
10
|
* All multi-byte integers are little-endian to match native x86/ARM layout and
|
|
11
11
|
* avoid byte-swapping on the Rust side.
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
13
|
+
import { OpWriter } from "./op-stream.js";
|
|
14
14
|
import type { MdastNode } from "./types.js";
|
|
15
15
|
type ReturnClass = "no_change" | "raw_markdown" | "raw_html" | "structured_node";
|
|
16
|
+
/** Input to the structural mutators (`replace`, `insertBefore`, …). */
|
|
17
|
+
type StructuralContent = MdastNode | {
|
|
18
|
+
raw: string;
|
|
19
|
+
mdxExpressions?: boolean;
|
|
20
|
+
} | {
|
|
21
|
+
rawHtml: string;
|
|
22
|
+
};
|
|
16
23
|
export declare function classifyReturn(value: unknown): ReturnClass;
|
|
17
|
-
/**
|
|
18
|
-
|
|
24
|
+
/** Backings above this are dropped on release so one huge compile does not retain megabytes forever. */
|
|
25
|
+
export declare const COMMAND_BUFFER_RETAIN_MAX: number;
|
|
26
|
+
export declare function acquireCommandBuffer(): CommandBuffer;
|
|
27
|
+
export declare function releaseCommandBuffer(buf: CommandBuffer): void;
|
|
28
|
+
/** Structural commands that carry a subtree payload emitted in place via `emitOpstreamCommand`. */
|
|
19
29
|
export type StructuralOp = "replace" | "insertBefore" | "insertAfter" | "prependChild" | "appendChild" | "wrapNode";
|
|
20
|
-
export declare
|
|
30
|
+
export declare const STRUCTURAL_CMD: Record<StructuralOp, number>;
|
|
31
|
+
export declare class CommandBuffer extends OpWriter {
|
|
32
|
+
#private;
|
|
21
33
|
constructor();
|
|
34
|
+
reset(): void;
|
|
35
|
+
/** Emit a structural command whose opstream payload is written by `emit`
|
|
36
|
+
* via the inherited op methods. If `emit` returns false or throws, the
|
|
37
|
+
* buffer is rolled back to the command start so the Rust decoder never
|
|
38
|
+
* sees a half-written command. Returns `emit`'s verdict. */
|
|
39
|
+
emitOpstreamCommand(cmd: number, nodeId: number, emit: () => boolean): boolean;
|
|
22
40
|
removeNode(nodeId: number): void;
|
|
23
|
-
/** Unified set-property for both MDAST and HAST nodes.
|
|
41
|
+
/** Unified set-property for both MDAST and HAST nodes.
|
|
42
|
+
*
|
|
43
|
+
* Hot path: uses `encodeInto` to write UTF-8 straight into the buffer (no
|
|
44
|
+
* per-call `Uint8Array`), reserving the worst-case length up front and
|
|
45
|
+
* backfilling the length prefix once the byte count is known. */
|
|
24
46
|
setProperty(nodeId: number, key: string, value: unknown): void;
|
|
25
|
-
insertBefore(nodeId: number, newNode:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
rawHtml: string;
|
|
34
|
-
}): void;
|
|
35
|
-
prependChild(nodeId: number, newNode: MdastNode | {
|
|
36
|
-
raw: string;
|
|
37
|
-
} | {
|
|
38
|
-
rawHtml: string;
|
|
39
|
-
}): void;
|
|
40
|
-
appendChild(nodeId: number, newNode: MdastNode | {
|
|
41
|
-
raw: string;
|
|
42
|
-
} | {
|
|
43
|
-
rawHtml: string;
|
|
44
|
-
}): void;
|
|
45
|
-
wrapNode(nodeId: number, parentNode: MdastNode | {
|
|
46
|
-
raw: string;
|
|
47
|
-
} | {
|
|
48
|
-
rawHtml: string;
|
|
49
|
-
}): void;
|
|
50
|
-
replace(nodeId: number, newNode: MdastNode | {
|
|
51
|
-
raw: string;
|
|
52
|
-
} | {
|
|
53
|
-
rawHtml: string;
|
|
54
|
-
}): void;
|
|
55
|
-
/** Header (cmd + nodeId + payloadType) followed by a length-prefixed string payload. */
|
|
56
|
-
private writePayloadCommand;
|
|
57
|
-
replaceOpstream(nodeId: number, ops: Uint8Array): void;
|
|
58
|
-
/** Replace a node's child list (root-wrapped `ops`) while keeping the node. */
|
|
59
|
-
setChildrenOpstream(nodeId: number, ops: Uint8Array): void;
|
|
60
|
-
insertBeforeOpstream(nodeId: number, ops: Uint8Array): void;
|
|
61
|
-
insertAfterOpstream(nodeId: number, ops: Uint8Array): void;
|
|
62
|
-
prependChildOpstream(nodeId: number, ops: Uint8Array): void;
|
|
63
|
-
appendChildOpstream(nodeId: number, ops: Uint8Array): void;
|
|
64
|
-
wrapNodeOpstream(nodeId: number, ops: Uint8Array): void;
|
|
65
|
-
private writeOpstreamCommand;
|
|
47
|
+
insertBefore(nodeId: number, newNode: StructuralContent): void;
|
|
48
|
+
insertAfter(nodeId: number, newNode: StructuralContent): void;
|
|
49
|
+
prependChild(nodeId: number, newNode: StructuralContent): void;
|
|
50
|
+
appendChild(nodeId: number, newNode: StructuralContent): void;
|
|
51
|
+
wrapNode(nodeId: number, parentNode: StructuralContent): void;
|
|
52
|
+
replace(nodeId: number, newNode: StructuralContent): void;
|
|
53
|
+
/** Header (cmd + nodeId + PAYLOAD_RAW + flags) followed by a length-prefixed string. */
|
|
54
|
+
private writeRawCommand;
|
|
66
55
|
/** Return a Uint8Array view of the written bytes (no copy). */
|
|
67
56
|
getBuffer(): Uint8Array;
|
|
68
|
-
/**
|
|
69
|
-
reset(): void;
|
|
70
|
-
/** Raw structural content (`{raw}`/`{rawHtml}` escape hatches). Declarative
|
|
71
|
-
* nodes go through the `*Opstream` methods, not here. */
|
|
57
|
+
/** Encode the raw-string escape hatch; declarative nodes use `*Opstream` instead. */
|
|
72
58
|
private writeStructuralCommand;
|
|
73
59
|
}
|
|
74
60
|
export {};
|
package/dist/command-buffer.js
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
* All multi-byte integers are little-endian to match native x86/ARM layout and
|
|
11
11
|
* avoid byte-swapping on the Rust side.
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
13
|
+
import { OpWriter } from "./op-stream.js";
|
|
14
14
|
import { PROP_STRING, PROP_BOOL_TRUE, PROP_BOOL_FALSE, PROP_SPACE_SEP, PROP_INT, PROP_NULL, } from "./op-stream.js";
|
|
15
|
-
import { CMD_REMOVE, CMD_INSERT_BEFORE, CMD_INSERT_AFTER, CMD_PREPEND_CHILD, CMD_APPEND_CHILD, CMD_WRAP, CMD_REPLACE, CMD_SET_PROPERTY, CMD_SET_CHILDREN,
|
|
15
|
+
import { CMD_REMOVE, CMD_INSERT_BEFORE, CMD_INSERT_AFTER, CMD_PREPEND_CHILD, CMD_APPEND_CHILD, CMD_WRAP, CMD_REPLACE, CMD_SET_PROPERTY, CMD_SET_CHILDREN, PAYLOAD_RAW, RAW_LITERAL_BRACES, PAYLOAD_OPSTREAM, } from "./generated/wire-constants.js";
|
|
16
16
|
export function classifyReturn(value) {
|
|
17
17
|
if (value === undefined || value === null)
|
|
18
18
|
return "no_change";
|
|
@@ -26,17 +26,105 @@ export function classifyReturn(value) {
|
|
|
26
26
|
throw new Error("Invalid return value from visitor: must have raw, rawHtml, or type");
|
|
27
27
|
}
|
|
28
28
|
const INITIAL_SIZE = 4096;
|
|
29
|
-
|
|
29
|
+
/** Free list recycling `CommandBuffer` instances (and their grown backings)
|
|
30
|
+
* across plugin passes. Safe to retain the backing because `mergeAndReset`
|
|
31
|
+
* copies the bytes out before a buffer is released, so no view outlives a
|
|
32
|
+
* pass. Cap bounds the pool for processes that briefly burst high. */
|
|
33
|
+
const COMMAND_BUFFER_POOL_MAX = 8;
|
|
34
|
+
/** Backings above this are dropped on release so one huge compile does not retain megabytes forever. */
|
|
35
|
+
export const COMMAND_BUFFER_RETAIN_MAX = 1 << 20;
|
|
36
|
+
const commandBufferPool = [];
|
|
37
|
+
export function acquireCommandBuffer() {
|
|
38
|
+
return commandBufferPool.pop() ?? new CommandBuffer();
|
|
39
|
+
}
|
|
40
|
+
export function releaseCommandBuffer(buf) {
|
|
41
|
+
if (commandBufferPool.length >= COMMAND_BUFFER_POOL_MAX)
|
|
42
|
+
return;
|
|
43
|
+
if (buf.capacity > COMMAND_BUFFER_RETAIN_MAX)
|
|
44
|
+
return;
|
|
45
|
+
buf.reset();
|
|
46
|
+
commandBufferPool.push(buf);
|
|
47
|
+
}
|
|
48
|
+
export const STRUCTURAL_CMD = {
|
|
49
|
+
replace: CMD_REPLACE,
|
|
50
|
+
insertBefore: CMD_INSERT_BEFORE,
|
|
51
|
+
insertAfter: CMD_INSERT_AFTER,
|
|
52
|
+
prependChild: CMD_PREPEND_CHILD,
|
|
53
|
+
appendChild: CMD_APPEND_CHILD,
|
|
54
|
+
wrapNode: CMD_WRAP,
|
|
55
|
+
};
|
|
56
|
+
export class CommandBuffer extends OpWriter {
|
|
57
|
+
/** Set while a structural payload is being emitted in place; command
|
|
58
|
+
* methods must not interleave bytes into it. */
|
|
59
|
+
#inOpstream = false;
|
|
30
60
|
constructor() {
|
|
31
61
|
super(INITIAL_SIZE);
|
|
32
62
|
}
|
|
63
|
+
reset() {
|
|
64
|
+
this.#inOpstream = false;
|
|
65
|
+
super.reset();
|
|
66
|
+
}
|
|
67
|
+
#assertNotEncoding() {
|
|
68
|
+
if (this.#inOpstream) {
|
|
69
|
+
throw new Error("reentrant command emission: a node getter or toJSON invoked a context mutation while a structural payload was being encoded");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Emit a structural command whose opstream payload is written by `emit`
|
|
73
|
+
* via the inherited op methods. If `emit` returns false or throws, the
|
|
74
|
+
* buffer is rolled back to the command start so the Rust decoder never
|
|
75
|
+
* sees a half-written command. Returns `emit`'s verdict. */
|
|
76
|
+
emitOpstreamCommand(cmd, nodeId, emit) {
|
|
77
|
+
const commandStart = this.n;
|
|
78
|
+
const lenPos = this.#beginOpstream(cmd, nodeId);
|
|
79
|
+
// ok starts false so a throwing emit still hits the abort in finally
|
|
80
|
+
let ok = false;
|
|
81
|
+
try {
|
|
82
|
+
ok = emit();
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
if (!ok)
|
|
86
|
+
this.#abortOpstream(commandStart);
|
|
87
|
+
}
|
|
88
|
+
if (ok)
|
|
89
|
+
this.#endOpstream(lenPos);
|
|
90
|
+
return ok;
|
|
91
|
+
}
|
|
92
|
+
/** Open a structural command whose opstream payload is emitted in place via
|
|
93
|
+
* the inherited op methods; returns the backpatch position for
|
|
94
|
+
* `#endOpstream`. Pair with `#abortOpstream` on failure. */
|
|
95
|
+
#beginOpstream(cmd, nodeId) {
|
|
96
|
+
this.#assertNotEncoding();
|
|
97
|
+
this.#inOpstream = true;
|
|
98
|
+
this.ensure(10);
|
|
99
|
+
this.buf[this.n++] = cmd;
|
|
100
|
+
this.writeU32(nodeId);
|
|
101
|
+
this.buf[this.n++] = PAYLOAD_OPSTREAM;
|
|
102
|
+
const lenPos = this.n;
|
|
103
|
+
this.n += 4;
|
|
104
|
+
return lenPos;
|
|
105
|
+
}
|
|
106
|
+
#endOpstream(lenPos) {
|
|
107
|
+
this.#inOpstream = false;
|
|
108
|
+
this.patchU32(lenPos, this.n - (lenPos + 4));
|
|
109
|
+
}
|
|
110
|
+
/** Roll back an in-progress opstream command (unencodable content). */
|
|
111
|
+
#abortOpstream(commandStart) {
|
|
112
|
+
this.#inOpstream = false;
|
|
113
|
+
this.n = commandStart;
|
|
114
|
+
}
|
|
33
115
|
removeNode(nodeId) {
|
|
116
|
+
this.#assertNotEncoding();
|
|
34
117
|
this.ensure(5);
|
|
35
118
|
this.buf[this.n++] = CMD_REMOVE;
|
|
36
119
|
this.writeU32(nodeId);
|
|
37
120
|
}
|
|
38
|
-
/** Unified set-property for both MDAST and HAST nodes.
|
|
121
|
+
/** Unified set-property for both MDAST and HAST nodes.
|
|
122
|
+
*
|
|
123
|
+
* Hot path: uses `encodeInto` to write UTF-8 straight into the buffer (no
|
|
124
|
+
* per-call `Uint8Array`), reserving the worst-case length up front and
|
|
125
|
+
* backfilling the length prefix once the byte count is known. */
|
|
39
126
|
setProperty(nodeId, key, value) {
|
|
127
|
+
this.#assertNotEncoding();
|
|
40
128
|
let valueType;
|
|
41
129
|
let str;
|
|
42
130
|
if (value === null || value === undefined) {
|
|
@@ -89,64 +177,29 @@ export class CommandBuffer extends ByteWriter {
|
|
|
89
177
|
replace(nodeId, newNode) {
|
|
90
178
|
this.writeStructuralCommand(CMD_REPLACE, nodeId, newNode);
|
|
91
179
|
}
|
|
92
|
-
/** Header (cmd + nodeId +
|
|
93
|
-
|
|
94
|
-
this
|
|
180
|
+
/** Header (cmd + nodeId + PAYLOAD_RAW + flags) followed by a length-prefixed string. */
|
|
181
|
+
writeRawCommand(cmd, nodeId, flags, s) {
|
|
182
|
+
this.#assertNotEncoding();
|
|
183
|
+
this.ensure(7);
|
|
95
184
|
this.buf[this.n++] = cmd;
|
|
96
185
|
this.writeU32(nodeId);
|
|
97
|
-
this.buf[this.n++] =
|
|
186
|
+
this.buf[this.n++] = PAYLOAD_RAW;
|
|
187
|
+
this.buf[this.n++] = flags;
|
|
98
188
|
this.utf8WithU32Len(s);
|
|
99
189
|
}
|
|
100
|
-
replaceOpstream(nodeId, ops) {
|
|
101
|
-
this.writeOpstreamCommand(CMD_REPLACE, nodeId, ops);
|
|
102
|
-
}
|
|
103
|
-
/** Replace a node's child list (root-wrapped `ops`) while keeping the node. */
|
|
104
|
-
setChildrenOpstream(nodeId, ops) {
|
|
105
|
-
this.writeOpstreamCommand(CMD_SET_CHILDREN, nodeId, ops);
|
|
106
|
-
}
|
|
107
|
-
insertBeforeOpstream(nodeId, ops) {
|
|
108
|
-
this.writeOpstreamCommand(CMD_INSERT_BEFORE, nodeId, ops);
|
|
109
|
-
}
|
|
110
|
-
insertAfterOpstream(nodeId, ops) {
|
|
111
|
-
this.writeOpstreamCommand(CMD_INSERT_AFTER, nodeId, ops);
|
|
112
|
-
}
|
|
113
|
-
prependChildOpstream(nodeId, ops) {
|
|
114
|
-
this.writeOpstreamCommand(CMD_PREPEND_CHILD, nodeId, ops);
|
|
115
|
-
}
|
|
116
|
-
appendChildOpstream(nodeId, ops) {
|
|
117
|
-
this.writeOpstreamCommand(CMD_APPEND_CHILD, nodeId, ops);
|
|
118
|
-
}
|
|
119
|
-
wrapNodeOpstream(nodeId, ops) {
|
|
120
|
-
this.writeOpstreamCommand(CMD_WRAP, nodeId, ops);
|
|
121
|
-
}
|
|
122
|
-
writeOpstreamCommand(cmd, nodeId, ops) {
|
|
123
|
-
this.ensure(10 + ops.length);
|
|
124
|
-
this.buf[this.n++] = cmd;
|
|
125
|
-
this.writeU32(nodeId);
|
|
126
|
-
this.buf[this.n++] = PAYLOAD_OPSTREAM;
|
|
127
|
-
this.writeU32(ops.length);
|
|
128
|
-
this.buf.set(ops, this.n);
|
|
129
|
-
this.n += ops.length;
|
|
130
|
-
}
|
|
131
190
|
/** Return a Uint8Array view of the written bytes (no copy). */
|
|
132
191
|
getBuffer() {
|
|
133
192
|
return this.take();
|
|
134
193
|
}
|
|
135
|
-
/**
|
|
136
|
-
reset() {
|
|
137
|
-
if (this.n === 0)
|
|
138
|
-
return;
|
|
139
|
-
this.release();
|
|
140
|
-
}
|
|
141
|
-
/** Raw structural content (`{raw}`/`{rawHtml}` escape hatches). Declarative
|
|
142
|
-
* nodes go through the `*Opstream` methods, not here. */
|
|
194
|
+
/** Encode the raw-string escape hatch; declarative nodes use `*Opstream` instead. */
|
|
143
195
|
writeStructuralCommand(cmd, nodeId, node) {
|
|
144
196
|
const v = node;
|
|
145
197
|
if (typeof v.raw === "string") {
|
|
146
|
-
|
|
198
|
+
const flags = v.mdxExpressions === false ? RAW_LITERAL_BRACES : 0;
|
|
199
|
+
this.writeRawCommand(cmd, nodeId, flags, v.raw);
|
|
147
200
|
}
|
|
148
201
|
else if (typeof v.rawHtml === "string") {
|
|
149
|
-
this.
|
|
202
|
+
this.writeRawCommand(cmd, nodeId, RAW_LITERAL_BRACES, v.rawHtml);
|
|
150
203
|
}
|
|
151
204
|
else {
|
|
152
205
|
throw new Error("CommandBuffer: structural content must be {raw} or {rawHtml}");
|
package/dist/compile.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { MdastPluginList, HastPluginList } from "./plugin.js";
|
|
2
2
|
import type { MdastNode, HastNode, Data } from "./types.js";
|
|
3
3
|
/** Configuration for static subtree collapsing during MDX compilation. */
|
|
4
4
|
export interface OptimizeStaticConfig {
|
|
@@ -110,6 +110,8 @@ export interface Features {
|
|
|
110
110
|
subscript?: boolean;
|
|
111
111
|
/** Obsidian-style wikilinks (`[[link]]`). Default: false. */
|
|
112
112
|
wikilinks?: boolean;
|
|
113
|
+
/** Definition lists (a term line then a `: definition`). Default: false. */
|
|
114
|
+
definitionList?: boolean;
|
|
113
115
|
/**
|
|
114
116
|
* Smart punctuation à la SmartyPants. Default: false.
|
|
115
117
|
*
|
|
@@ -119,10 +121,24 @@ export interface Features {
|
|
|
119
121
|
* ```
|
|
120
122
|
*/
|
|
121
123
|
smartPunctuation?: boolean | SmartPunctuationOptions;
|
|
124
|
+
/**
|
|
125
|
+
* Parse raw HTML embedded in Markdown into real HAST element nodes.
|
|
126
|
+
* Default: false.
|
|
127
|
+
*
|
|
128
|
+
* By default, inline and block HTML is kept as opaque `raw` nodes
|
|
129
|
+
* (re-emitted verbatim on stringify). With `rawHtml: true`, the tree is
|
|
130
|
+
* reparsed so raw HTML becomes structured `element`/`text`/`comment` nodes
|
|
131
|
+
* with normalized properties, including tags that open in one raw block
|
|
132
|
+
* and close in another. Positions are not preserved through the reparse.
|
|
133
|
+
*/
|
|
134
|
+
rawHtml?: boolean;
|
|
122
135
|
}
|
|
123
136
|
export interface CompileOptions {
|
|
124
|
-
|
|
125
|
-
|
|
137
|
+
/** MDAST plugins, in run order. A nested array runs its own plugins, in
|
|
138
|
+
* their own order, at the array's position. */
|
|
139
|
+
mdastPlugins?: MdastPluginList;
|
|
140
|
+
/** HAST plugins, in run order. Nests like `mdastPlugins`. */
|
|
141
|
+
hastPlugins?: HastPluginList;
|
|
126
142
|
features?: Features;
|
|
127
143
|
/**
|
|
128
144
|
* The document being processed, surfaced to plugins as `ctx.fileURL`. Must
|
|
@@ -140,12 +156,9 @@ export interface CompileOptions {
|
|
|
140
156
|
data?: Data;
|
|
141
157
|
}
|
|
142
158
|
/**
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
* pipeline options in {@link CompileOptions}. Useful for wrappers (Vite/Rollup
|
|
147
|
-
* plugins, framework integrations) that want to expose MDX-specific knobs
|
|
148
|
-
* without re-exposing the shared pipeline fields.
|
|
159
|
+
* JS/JSX output options, accepted by both {@link mdxToJs} and
|
|
160
|
+
* {@link markdownToJs}. Exported on its own so wrappers can expose the codegen
|
|
161
|
+
* knobs without the shared pipeline fields.
|
|
149
162
|
*/
|
|
150
163
|
export interface MdxOnlyOptions {
|
|
151
164
|
optimizeStatic?: OptimizeStaticConfig;
|
|
@@ -195,6 +208,8 @@ export interface MdxOnlyOptions {
|
|
|
195
208
|
}
|
|
196
209
|
export interface MdxCompileOptions extends CompileOptions, MdxOnlyOptions {
|
|
197
210
|
}
|
|
211
|
+
export interface MarkdownToJsOptions extends CompileOptions, MdxOnlyOptions {
|
|
212
|
+
}
|
|
198
213
|
/** Frontmatter block extracted from the parsed Markdown/MDX source. */
|
|
199
214
|
export interface Frontmatter {
|
|
200
215
|
/** Delimiter syntax used for the block. */
|
|
@@ -220,27 +235,50 @@ export interface MdxToJsResult {
|
|
|
220
235
|
/** Document-level data bag shared with plugins via `ctx.data`; the seeded `data` option if provided, else a fresh `{}`. */
|
|
221
236
|
data: Data;
|
|
222
237
|
}
|
|
238
|
+
/** Result of {@link markdownToJs}. */
|
|
239
|
+
export interface MarkdownToJsResult {
|
|
240
|
+
/** Compiled JavaScript module source. */
|
|
241
|
+
code: string;
|
|
242
|
+
/** Frontmatter block at the start of the document, or `null` if none. */
|
|
243
|
+
frontmatter: Frontmatter | null;
|
|
244
|
+
/** Document-level data bag shared with plugins via `ctx.data`; the seeded `data` option if provided, else a fresh `{}`. */
|
|
245
|
+
data: Data;
|
|
246
|
+
}
|
|
247
|
+
type CombineAsync<A> = "async" extends A ? "async" : "maybe" extends A ? "maybe" : "sync";
|
|
223
248
|
type AnyFn = (...args: any[]) => unknown;
|
|
224
|
-
type ReturnsPromise<F> = F extends AnyFn ? Extract<ReturnType<F>, Promise<unknown>> extends never ?
|
|
249
|
+
type ReturnsPromise<F> = F extends AnyFn ? [Extract<ReturnType<F>, Promise<unknown>>] extends [never] ? "sync" : [Exclude<ReturnType<F>, Promise<unknown>>] extends [never] ? "async" : "maybe" : "sync";
|
|
225
250
|
type FieldIsAsync<V> = V extends AnyFn ? ReturnsPromise<V> : V extends {
|
|
226
251
|
visit: infer F;
|
|
227
252
|
} ? ReturnsPromise<F> : V extends ReadonlyArray<infer Item> ? Item extends {
|
|
228
253
|
visit: infer F;
|
|
229
|
-
} ? ReturnsPromise<F> :
|
|
254
|
+
} ? ReturnsPromise<F> : "sync" : "sync";
|
|
230
255
|
type AnyVisitorAsync<P> = {
|
|
231
256
|
[K in keyof P]-?: FieldIsAsync<NonNullable<P[K]>>;
|
|
232
257
|
}[keyof P];
|
|
233
|
-
type IsPluginAsync<P> =
|
|
234
|
-
type ResolveInput<P
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
258
|
+
type IsPluginAsync<P> = P extends unknown ? CombineAsync<AnyVisitorAsync<P>> : never;
|
|
259
|
+
type ResolveInput<P, D extends readonly unknown[] = [0, 0, 0, 0, 0, 0, 0, 0]> = D extends readonly [
|
|
260
|
+
unknown,
|
|
261
|
+
...infer Rest
|
|
262
|
+
] ? P extends ReadonlyArray<infer Item> ? ResolveInput<Item, Rest> : P extends (...args: never[]) => infer Def ? ResolveInput<Def, Rest> : P : never;
|
|
263
|
+
type AnyInputAsync<Ps> = Ps extends ReadonlyArray<infer P> ? CombineAsync<IsPluginAsync<ResolveInput<P>>> : "sync";
|
|
264
|
+
type OptionsAsync<O extends CompileOptions> = CombineAsync<AnyInputAsync<NonNullable<O["mdastPlugins"]>> | AnyInputAsync<NonNullable<O["hastPlugins"]>>>;
|
|
265
|
+
type ResultFor<O extends CompileOptions, R> = {
|
|
266
|
+
sync: R;
|
|
267
|
+
async: Promise<R>;
|
|
268
|
+
maybe: R | Promise<R>;
|
|
269
|
+
}[OptionsAsync<O>];
|
|
270
|
+
export declare function markdownToHtml(source: string): MarkdownToHtmlResult;
|
|
242
271
|
export declare function markdownToHtml<O extends CompileOptions>(source: string, options?: O): ResultFor<O, MarkdownToHtmlResult>;
|
|
272
|
+
export declare function mdxToJs(source: string): MdxToJsResult;
|
|
243
273
|
export declare function mdxToJs<O extends MdxCompileOptions>(source: string, options?: O): ResultFor<O, MdxToJsResult>;
|
|
274
|
+
/**
|
|
275
|
+
* Compile plain Markdown to a JavaScript module: like {@link mdxToJs}, but
|
|
276
|
+
* without MDX syntax: `{...}` expressions, JSX tags, and `import`/`export`
|
|
277
|
+
* lines are ordinary Markdown. HTML has no JSX representation and is dropped;
|
|
278
|
+
* enable `features: { rawHtml: true }` to parse it into real elements instead.
|
|
279
|
+
*/
|
|
280
|
+
export declare function markdownToJs(source: string): MarkdownToJsResult;
|
|
281
|
+
export declare function markdownToJs<O extends MarkdownToJsOptions>(source: string, options?: O): ResultFor<O, MarkdownToJsResult>;
|
|
244
282
|
export interface EvaluateOptions extends Omit<MdxCompileOptions, "jsx" | "outputFormat"> {
|
|
245
283
|
Fragment: unknown;
|
|
246
284
|
jsx: (type: unknown, props: unknown, key?: unknown) => unknown;
|
|
@@ -260,20 +298,30 @@ export interface EvaluateOptions extends Omit<MdxCompileOptions, "jsx" | "output
|
|
|
260
298
|
* ```
|
|
261
299
|
*/
|
|
262
300
|
export declare function evaluate(source: string, options: EvaluateOptions): Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
263
|
-
/**
|
|
264
|
-
export
|
|
301
|
+
/** Options for the step-by-step tree functions. */
|
|
302
|
+
export interface TreeOptions {
|
|
265
303
|
features?: Features;
|
|
266
|
-
|
|
304
|
+
/**
|
|
305
|
+
* Record `position` on every node. Default: `true`.
|
|
306
|
+
*
|
|
307
|
+
* A unist position is three objects per node and roughly half a materialized
|
|
308
|
+
* tree's memory, on top of the parser's line-index build. Pass `false` when
|
|
309
|
+
* nothing downstream reads `node.position`.
|
|
310
|
+
*/
|
|
311
|
+
position?: boolean;
|
|
312
|
+
}
|
|
313
|
+
/** Parse Markdown source into a materialized mdast tree. */
|
|
314
|
+
export declare function markdownToMdast(source: string, options?: TreeOptions): MdastNode;
|
|
267
315
|
/** Parse MDX source into a materialized mdast tree. */
|
|
268
|
-
export declare function mdxToMdast(source: string, options?:
|
|
269
|
-
features?: Features;
|
|
270
|
-
}): MdastNode;
|
|
316
|
+
export declare function mdxToMdast(source: string, options?: TreeOptions): MdastNode;
|
|
271
317
|
/** Convert Markdown source to a materialized hast tree. */
|
|
272
|
-
export declare function markdownToHast(source: string, options?:
|
|
273
|
-
features?: Features;
|
|
274
|
-
}): HastNode;
|
|
318
|
+
export declare function markdownToHast(source: string, options?: TreeOptions): HastNode;
|
|
275
319
|
/** Convert MDX source to a materialized hast tree. */
|
|
276
|
-
export declare function mdxToHast(source: string, options?:
|
|
277
|
-
|
|
278
|
-
|
|
320
|
+
export declare function mdxToHast(source: string, options?: TreeOptions): HastNode;
|
|
321
|
+
/**
|
|
322
|
+
* Parse an HTML string into a materialized hast tree: a `root` whose children
|
|
323
|
+
* are the doctype (if any) and the implied `<html>` subtree. Only available
|
|
324
|
+
* in builds that include the `from-html` feature.
|
|
325
|
+
*/
|
|
326
|
+
export declare function htmlToHast(html: string): HastNode;
|
|
279
327
|
export {};
|