satteri 0.9.2 → 0.9.4
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 +16 -328
- package/dist/binding.browser.d.ts +1 -1
- package/dist/binding.browser.js +1 -1
- package/dist/generated/arena-layout.d.ts +2 -2
- package/dist/generated/arena-layout.js +2 -2
- package/dist/hast/hast-reader.d.ts +3 -2
- package/dist/hast/hast-reader.js +13 -12
- package/dist/index.d.ts +1 -1
- package/dist/mdast/mdast-reader.d.ts +3 -1
- package/dist/mdast/mdast-reader.js +13 -11
- package/dist/types.d.ts +2 -2
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Native-enhanced Markdown parsing and processing for JavaScript. Parse and compile in Rust, create flexible plugins in JavaScript.
|
|
4
4
|
|
|
5
|
+
Check out the [documentation](https://satteri.bruits.org/docs/) for [installation instructions](https://satteri.bruits.org/docs/installation/), the [API reference](https://satteri.bruits.org/docs/entry-points/), and [usage examples](https://satteri.bruits.org/docs/quick-start/), try it online on the [playground](https://satteri.bruits.org/playground), or join us on [Discord](https://discord.com/invite/84pd4QtmzA)!
|
|
6
|
+
|
|
5
7
|
## Install
|
|
6
8
|
|
|
7
9
|
```sh
|
|
@@ -17,7 +19,7 @@ pnpm add satteri
|
|
|
17
19
|
```ts
|
|
18
20
|
import { markdownToHtml } from "satteri";
|
|
19
21
|
|
|
20
|
-
const html = markdownToHtml("# Hello\n\nWorld");
|
|
22
|
+
const { html } = markdownToHtml("# Hello\n\nWorld");
|
|
21
23
|
// <h1>Hello</h1>\n<p>World</p>
|
|
22
24
|
```
|
|
23
25
|
|
|
@@ -26,345 +28,31 @@ const html = markdownToHtml("# Hello\n\nWorld");
|
|
|
26
28
|
```ts
|
|
27
29
|
import { mdxToJs } from "satteri";
|
|
28
30
|
|
|
29
|
-
const
|
|
31
|
+
const { code } = mdxToJs("# Hello\n\n<MyComponent />");
|
|
30
32
|
```
|
|
31
33
|
|
|
32
34
|
### With plugins
|
|
33
35
|
|
|
34
|
-
Both functions accept `mdastPlugins` (operate on the Markdown AST
|
|
35
|
-
|
|
36
|
-
```ts
|
|
37
|
-
import { markdownToHtml } from "satteri";
|
|
38
|
-
import { removeHeadings } from "./my-mdast-plugins.js";
|
|
39
|
-
import { addLinkClasses } from "./my-hast-plugins.js";
|
|
40
|
-
|
|
41
|
-
const html = markdownToHtml("# Hello\n\n[link](https://example.com)", {
|
|
42
|
-
mdastPlugins: [removeHeadings],
|
|
43
|
-
hastPlugins: [addLinkClasses],
|
|
44
|
-
});
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
If you're familiar with the unified ecosystem, mdast and hast plugins would be similar to remark and rehype plugins, respectively, and re-uses the same AST shape for both. This project does not currently have an equivalent of micromark or recma plugins.
|
|
48
|
-
|
|
49
|
-
## Plugins
|
|
50
|
-
|
|
51
|
-
### MDAST plugins
|
|
52
|
-
|
|
53
|
-
MDAST plugins run on the Markdown syntax tree, allowing you to do things like replace emoji shortcodes, unwrap images from paragraphs, or collect headings for a table of contents before Markdown is transformed to HTML / JS. Define visitor methods named after node types (`heading`, `code`, `link`, `image`, etc.). Each visitor receives the node and a context object for mutations.
|
|
54
|
-
|
|
55
|
-
```ts
|
|
56
|
-
const emojis = defineMdastPlugin({
|
|
57
|
-
name: "emojis",
|
|
58
|
-
text(node, ctx) {
|
|
59
|
-
if (node.value.includes(":wave:")) {
|
|
60
|
-
ctx.setProperty(node, "value", node.value.replaceAll(":wave:", "\u{1F44B}"));
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
});
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
Visitors can alternatively return a replacement node, raw Markdown, or raw HTML. This is useful when the replacement can't be expressed as property changes on the original node:
|
|
67
|
-
|
|
68
|
-
```ts
|
|
69
|
-
const highlightCode = defineMdastPlugin({
|
|
70
|
-
name: "highlight-code",
|
|
71
|
-
code(node) {
|
|
72
|
-
return { rawHtml: `<pre class="highlighted">${escape(node.value)}</pre>` };
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
All standard mdast node types are supported, plus GFM extensions (`table`, `tableRow`, `tableCell`, `delete`, `footnoteDefinition`, `footnoteReference`) and MDX nodes (`mdxJsxFlowElement`, `mdxJsxTextElement`, `mdxFlowExpression`, `mdxTextExpression`, `mdxjsEsm`) if enabled. For more information on the AST shape and node types, see the [mdast spec](https://github.com/syntax-tree/mdast).
|
|
78
|
-
|
|
79
|
-
### HAST plugins
|
|
80
|
-
|
|
81
|
-
HAST plugins run on the HTML syntax tree after mdast-to-hast conversion, allowing you to do things like add classes to elements, set attributes on links, or wrap HTML elements with other elements, etc. Element visitors use a `filter` array to specify which tag names (or component names for MDX) to match.
|
|
82
|
-
|
|
83
|
-
```ts
|
|
84
|
-
const addLinkClasses = defineHastPlugin({
|
|
85
|
-
name: "add-link-classes",
|
|
86
|
-
element: {
|
|
87
|
-
filter: ["a"],
|
|
88
|
-
visit(node, ctx) {
|
|
89
|
-
ctx.setProperty(node, "class", "link");
|
|
90
|
-
ctx.setProperty(node, "target", "_blank");
|
|
91
|
-
},
|
|
92
|
-
},
|
|
93
|
-
});
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
Multiple filter groups on the same node type:
|
|
97
|
-
|
|
98
|
-
```ts
|
|
99
|
-
const multiFilter = defineHastPlugin({
|
|
100
|
-
name: "multi-filter",
|
|
101
|
-
element: [
|
|
102
|
-
{
|
|
103
|
-
filter: ["h1", "h2", "h3"],
|
|
104
|
-
visit(node, ctx) {
|
|
105
|
-
ctx.setProperty(node, "class", "heading");
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
filter: ["a"],
|
|
110
|
-
visit(node, ctx) {
|
|
111
|
-
ctx.setProperty(node, "target", "_blank");
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
],
|
|
115
|
-
});
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
An empty filter matches all elements, but can quickly become expensive when used on large documents, so use with caution:
|
|
119
|
-
|
|
120
|
-
```ts
|
|
121
|
-
const allElements = defineHastPlugin({
|
|
122
|
-
name: "all-elements",
|
|
123
|
-
element: {
|
|
124
|
-
filter: [],
|
|
125
|
-
visit(node, ctx) {
|
|
126
|
-
ctx.setProperty(node, "data-visited", "true");
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
});
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
Non-element visitors (`text`, `comment`, `raw`, `doctype`, MDX expression types) use bare functions instead of filter objects:
|
|
133
|
-
|
|
134
|
-
```ts
|
|
135
|
-
const uppercaseText = defineHastPlugin({
|
|
136
|
-
name: "uppercase-text",
|
|
137
|
-
text(node, ctx) {
|
|
138
|
-
ctx.setProperty(node, "value", node.value.toUpperCase());
|
|
139
|
-
},
|
|
140
|
-
});
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
For more information on the AST shape and node types, see the [hast spec](https://github.com/syntax-tree/hast).
|
|
144
|
-
|
|
145
|
-
### Mutating nodes
|
|
146
|
-
|
|
147
|
-
Unlike remark and rehype plugins, nodes in Sätteri inside plugins are read-only. The AST lives in Rust memory and JavaScript only has a "view" over the different nodes, so direct mutations like `node.value = "new text"` have no effect. Use the context methods (`ctx.setProperty`, `ctx.removeNode`, `ctx.replaceNode`, etc.) instead, which send changes back to Rust in an efficient way.
|
|
148
|
-
|
|
149
|
-
```ts
|
|
150
|
-
// Won't work
|
|
151
|
-
heading(node, ctx) {
|
|
152
|
-
node.depth = 2; // no effect, TypeScript will also complain that the node is readonly
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Do this instead
|
|
156
|
-
heading(node, ctx) {
|
|
157
|
-
ctx.setProperty(node, "depth", 2);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Or return a new node to replace it entirely, but this is less efficient and generally not recommended
|
|
161
|
-
heading(node) {
|
|
162
|
-
return { ..node, depth: 2 };
|
|
163
|
-
}
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
### Sharing data between plugins
|
|
167
|
-
|
|
168
|
-
Each context exposes a `data` object, a document-scoped grab bag shared across every visitor in the compile. Writes from one plugin are visible to later plugins, and the bag persists across the mdast→hast boundary, so hast plugins can read what mdast plugins wrote. After compilation the final state is returned on `result.data`, making it suitable for things like extracting a table of contents.
|
|
169
|
-
|
|
170
|
-
```ts
|
|
171
|
-
const collectHeadings = defineMdastPlugin({
|
|
172
|
-
name: "collect-headings",
|
|
173
|
-
heading(node, ctx) {
|
|
174
|
-
const list = (ctx.data.headings as string[]) ?? [];
|
|
175
|
-
const first = node.children[0];
|
|
176
|
-
if (first && "value" in first) list.push(first.value as string);
|
|
177
|
-
ctx.data.headings = list;
|
|
178
|
-
},
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
const { html, data } = markdownToHtml("# A\n\n# B", { mdastPlugins: [collectHeadings] });
|
|
182
|
-
console.log(data?.headings); // ["A", "B"]
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
The bag lives entirely on the JS side, so any value is allowed, including functions, class instances, and `Map`/`Set`. References are preserved across plugins and across the mdast→hast boundary. A fresh, empty bag is created for every compile unless you seed one with the `data` option.
|
|
186
|
-
|
|
187
|
-
To seed data into the bag before plugins run, pass an object to the `data` option. The same object is available in all plugins and returned as `result.data` after compilation, so you can pre-populate it with values or methods that plugins can read and update.
|
|
188
|
-
|
|
189
|
-
```ts
|
|
190
|
-
const data = { title: "Hello" };
|
|
191
|
-
const rewriteTitle = defineMdastPlugin({
|
|
192
|
-
name: "rewrite-title",
|
|
193
|
-
heading(_node, ctx) {
|
|
194
|
-
ctx.data.title = "Updated";
|
|
195
|
-
},
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
const result = markdownToHtml("# A", { mdastPlugins: [rewriteTitle], data });
|
|
199
|
-
console.log(result.data === data); // true, the seeded object is returned
|
|
200
|
-
console.log(data.title); // "Updated"
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
By default keys are typed as `unknown`. To give a key a type, augment the `DataMap` interface:
|
|
204
|
-
|
|
205
|
-
```ts
|
|
206
|
-
declare module "satteri" {
|
|
207
|
-
interface DataMap {
|
|
208
|
-
headings: string[];
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// now ctx.data.headings and result.data.headings are typed as string[] | undefined
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
Unregistered keys stay `unknown`, so the bag remains open-ended.
|
|
216
|
-
|
|
217
|
-
### How transforms compose
|
|
218
|
-
|
|
219
|
-
Unlike remark and rehype, which re-walk the tree until it stops changing, each Sätteri plugin walks the tree **once**. Within that single pass:
|
|
220
|
-
|
|
221
|
-
- **Passed-through children keep their identity.** When a visitor returns a replacement that reuses the original node's children (e.g. `{ ...node, children: [...node.children] }`), those children are spliced back unchanged — so a transform the same pass queues on a nested one still applies. This is what lets a `containerDirective` visitor turn both an outer `:::note` and a nested `:::tip` into asides in one go.
|
|
222
|
-
- **A plugin's own freshly-built nodes are not re-walked by that plugin.** If a visitor returns a brand-new node (one that didn't come from the tree), the same plugin won't visit it. Produce its final shape directly, or hand it off to a later plugin — every plugin runs over the fully materialized output of the ones before it.
|
|
223
|
-
- **Dropping a subtree drops transforms queued inside it.** If one visitor removes or replaces a node while another (in the same pass) had queued a transform on something inside that subtree, the orphaned transform is dropped and a warning is logged. This is usually intentional — you discarded that subtree on purpose — but the warning helps catch the cases where it isn't.
|
|
224
|
-
|
|
225
|
-
### Async plugins
|
|
226
|
-
|
|
227
|
-
Visitors can optionally be async. When any visitor is async, `markdownToHtml` and `mdxToJs` return a `Promise<string>` instead of `string`. For performance reasons, it is typically best to avoid async visitors, especially if your visitor matches a large number of nodes.
|
|
228
|
-
|
|
229
|
-
````ts
|
|
230
|
-
const highlighter = await createHighlighter({ themes: ["github-dark"], langs: ["js", "ts"] });
|
|
231
|
-
|
|
232
|
-
const asyncHighlight = defineMdastPlugin({
|
|
233
|
-
name: "async-highlight",
|
|
234
|
-
async code(node) {
|
|
235
|
-
const html = await highlighter.codeToHtml(node.value, {
|
|
236
|
-
lang: node.lang,
|
|
237
|
-
theme: "github-dark",
|
|
238
|
-
});
|
|
239
|
-
return { rawHtml: html };
|
|
240
|
-
},
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
// Returns Promise<string> when async plugins are used
|
|
244
|
-
const html = await markdownToHtml("```js\ncode\n```", {
|
|
245
|
-
mdastPlugins: [asyncHighlight],
|
|
246
|
-
});
|
|
247
|
-
````
|
|
248
|
-
|
|
249
|
-
## API
|
|
250
|
-
|
|
251
|
-
### `markdownToHtml(source: string, options?: CompileOptions)`
|
|
252
|
-
|
|
253
|
-
Parse Markdown and compile to HTML. Returns `string` if all plugins are sync, `Promise<string>` if any are async.
|
|
36
|
+
Both 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.
|
|
254
37
|
|
|
255
38
|
```ts
|
|
256
|
-
|
|
257
|
-
// <h1>Hello</h1>\n<p>World</p>
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
### `mdxToJs(source: string, options?: MdxCompileOptions)`
|
|
39
|
+
import { markdownToHtml, defineMdastPlugin } from "satteri";
|
|
261
40
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
#### Static optimization
|
|
269
|
-
|
|
270
|
-
The `optimizeStatic` option for MDX collapses static subtrees into pre-rendered HTML strings, reducing the number of JSX element calls in the output and increasing rendering performance. Dynamic content (JSX components, expressions) is preserved as normal JSX calls.
|
|
271
|
-
|
|
272
|
-
```ts
|
|
273
|
-
// Astro-style: wraps static HTML in <Fragment set:html="...">
|
|
274
|
-
const js = mdxToJs("# Hello\n\nWorld", {
|
|
275
|
-
optimizeStatic: {
|
|
276
|
-
component: "Fragment",
|
|
277
|
-
prop: "set:html",
|
|
41
|
+
const stripInlineCode = defineMdastPlugin({
|
|
42
|
+
name: "strip-inline-code",
|
|
43
|
+
inlineCode(node, ctx) {
|
|
44
|
+
ctx.replaceNode(node, { type: "text", value: node.value });
|
|
278
45
|
},
|
|
279
46
|
});
|
|
280
47
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
optimizeStatic: {
|
|
284
|
-
component: "div",
|
|
285
|
-
prop: "dangerouslySetInnerHTML",
|
|
286
|
-
wrapPropValue: true,
|
|
287
|
-
},
|
|
48
|
+
const { html } = markdownToHtml("Use `let` instead of `var`.", {
|
|
49
|
+
mdastPlugins: [stripInlineCode],
|
|
288
50
|
});
|
|
51
|
+
// <p>Use let instead of var.</p>
|
|
289
52
|
```
|
|
290
53
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
Shoutout to [Bjorn Lu](https://bjornlu.com) for originally developing this optimization for [Astro](https://astro.build/).
|
|
294
|
-
|
|
295
|
-
### `markdownToMdast(source: string)`
|
|
296
|
-
|
|
297
|
-
Parse Markdown and return a complete mdast tree. This can be useful if you wanted to benefit from the fast native parsing of Sätteri, but ultimately wanted another pipeline to handle transformations and compilation, e.g. using remark plugins and `remark-stringify` to convert back to Markdown after processing.
|
|
298
|
-
|
|
299
|
-
```ts
|
|
300
|
-
import { markdownToMdast } from "satteri";
|
|
301
|
-
|
|
302
|
-
const tree = markdownToMdast("# Hello\n\nWorld");
|
|
303
|
-
// tree.children[0].type === "heading"
|
|
304
|
-
// tree.children[0].depth === 1
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
### `mdxToMdast(source: string)`
|
|
308
|
-
|
|
309
|
-
Parse MDX and return a complete mdast tree.
|
|
310
|
-
|
|
311
|
-
```ts
|
|
312
|
-
const tree = mdxToMdast('<Component foo="bar" />');
|
|
313
|
-
// tree.children[0].type === "mdxJsxFlowElement"
|
|
314
|
-
// tree.children[0].name === "Component"
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
### `markdownToHast(source: string)`
|
|
318
|
-
|
|
319
|
-
Parse Markdown, convert to hast, and return a complete hast tree.
|
|
320
|
-
|
|
321
|
-
```ts
|
|
322
|
-
const tree = markdownToHast("# Hello\n\nWorld");
|
|
323
|
-
// tree.children[0].type === "element"
|
|
324
|
-
// tree.children[0].tagName === "h1"
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
### `mdxToHast(source: string)`
|
|
328
|
-
|
|
329
|
-
Parse MDX, convert to hast, and return a complete hast tree.
|
|
330
|
-
|
|
331
|
-
```ts
|
|
332
|
-
const tree = mdxToHast("<MyComponent />");
|
|
333
|
-
// tree.children[0].type === "mdxJsxFlowElement"
|
|
334
|
-
// tree.children[0].name === "MyComponent"
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
### `defineMdastPlugin(definition: MdastPluginDefinition)`
|
|
338
|
-
|
|
339
|
-
Type-safe wrapper for MDAST plugin definitions.
|
|
340
|
-
|
|
341
|
-
### `defineHastPlugin(definition: HastPluginDefinition)`
|
|
342
|
-
|
|
343
|
-
Type-safe wrapper for HAST plugin definitions.
|
|
344
|
-
|
|
345
|
-
### `CompileOptions`
|
|
346
|
-
|
|
347
|
-
```ts
|
|
348
|
-
interface CompileOptions {
|
|
349
|
-
mdastPlugins?: MdastPluginDefinition[];
|
|
350
|
-
hastPlugins?: HastPluginDefinition[];
|
|
351
|
-
features?: Features;
|
|
352
|
-
fileURL?: URL;
|
|
353
|
-
data?: Data;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// mdxToJs accepts MdxCompileOptions, which extends CompileOptions
|
|
357
|
-
interface MdxCompileOptions extends CompileOptions {
|
|
358
|
-
optimizeStatic?: OptimizeStaticConfig;
|
|
359
|
-
}
|
|
360
|
-
```
|
|
361
|
-
|
|
362
|
-
`fileURL` is the `URL` of the document being processed, surfaced to plugins as `ctx.fileURL` (a `URL`, or `undefined` when omitted). Pass a file URL such as Astro's `fileURL`, or convert a filesystem path with Node's `pathToFileURL`; read it back with `fileURLToPath(ctx.fileURL)`.
|
|
363
|
-
|
|
364
|
-
`data` seeds the document-level data bag before plugins run, surfaced as `ctx.data` and returned as `result.data`. See [Sharing data between plugins](#sharing-data-between-plugins).
|
|
365
|
-
|
|
366
|
-
`Features` controls the Markdown extensions Sätteri's parser recognizes: `gfm`, `frontmatter`, `math`, `directive`, `smartPunctuation`, etc. `gfm`, `math`, and `smartPunctuation` also accept granular options. For example, `math: { singleDollarTextMath: false }` keeps single `$` as literal text. See the [Features reference](https://satteri.dev/docs/features/) for the full list.
|
|
54
|
+
If you're familiar with the unified ecosystem, mdast and hast plugins are similar to remark and rehype plugins, respectively, reusing the same AST shapes.
|
|
367
55
|
|
|
368
|
-
##
|
|
56
|
+
## Development
|
|
369
57
|
|
|
370
|
-
|
|
58
|
+
Refer to [CONTRIBUTING.md](https://github.com/bruits/satteri/blob/main/CONTRIBUTING.md) for development setup and workflow details.
|
|
@@ -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 "../
|
|
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";
|
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 "../
|
|
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";
|
|
@@ -30,8 +30,8 @@ export declare const HEADER: {
|
|
|
30
30
|
readonly children_offset: 24;
|
|
31
31
|
readonly type_data_len: 28;
|
|
32
32
|
readonly type_data_offset: 32;
|
|
33
|
-
readonly
|
|
34
|
-
readonly
|
|
33
|
+
readonly string_pool_len: 36;
|
|
34
|
+
readonly string_pool_offset: 40;
|
|
35
35
|
readonly node_data_count: 44;
|
|
36
36
|
readonly node_data_offset: 48;
|
|
37
37
|
};
|
|
@@ -29,8 +29,9 @@ export declare class HastReader {
|
|
|
29
29
|
getNodeData(nodeId: number): string | null;
|
|
30
30
|
get nodeCount(): number;
|
|
31
31
|
get header(): BufferHeader;
|
|
32
|
-
/**
|
|
33
|
-
|
|
32
|
+
/** The full string pool (original input + interning heap). Not the document
|
|
33
|
+
* source as written; for that, read `ctx.source` from a plugin. */
|
|
34
|
+
getStringPool(): string;
|
|
34
35
|
/** Read a substring from the string pool by byte offset and length. */
|
|
35
36
|
getString(offset: number, len: number): string;
|
|
36
37
|
/** Get position data for a node. */
|
package/dist/hast/hast-reader.js
CHANGED
|
@@ -19,7 +19,7 @@ export class HastReader {
|
|
|
19
19
|
#view;
|
|
20
20
|
#header;
|
|
21
21
|
#textDecoder;
|
|
22
|
-
#
|
|
22
|
+
#stringPoolCache = null;
|
|
23
23
|
constructor(buffer) {
|
|
24
24
|
if (buffer instanceof Uint8Array) {
|
|
25
25
|
this.#view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
@@ -49,8 +49,8 @@ export class HastReader {
|
|
|
49
49
|
childrenOffset: v.getUint32(HEADER.children_offset, true),
|
|
50
50
|
typeDataLen: v.getUint32(HEADER.type_data_len, true),
|
|
51
51
|
typeDataOffset: v.getUint32(HEADER.type_data_offset, true),
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
stringPoolLen: v.getUint32(HEADER.string_pool_len, true),
|
|
53
|
+
stringPoolOffset: v.getUint32(HEADER.string_pool_offset, true),
|
|
54
54
|
nodeDataCount: v.getUint32(HEADER.node_data_count, true),
|
|
55
55
|
nodeDataOffset: v.getUint32(HEADER.node_data_offset, true),
|
|
56
56
|
};
|
|
@@ -87,21 +87,22 @@ export class HastReader {
|
|
|
87
87
|
get header() {
|
|
88
88
|
return { ...this.#header };
|
|
89
89
|
}
|
|
90
|
-
/**
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
90
|
+
/** The full string pool (original input + interning heap). Not the document
|
|
91
|
+
* source as written; for that, read `ctx.source` from a plugin. */
|
|
92
|
+
getStringPool() {
|
|
93
|
+
if (this.#stringPoolCache === null) {
|
|
94
|
+
const { stringPoolOffset, stringPoolLen } = this.#header;
|
|
95
|
+
const bytes = new Uint8Array(this.#view.buffer, this.#view.byteOffset + stringPoolOffset, stringPoolLen);
|
|
96
|
+
this.#stringPoolCache = this.#textDecoder.decode(bytes);
|
|
96
97
|
}
|
|
97
|
-
return this.#
|
|
98
|
+
return this.#stringPoolCache;
|
|
98
99
|
}
|
|
99
100
|
/** Read a substring from the string pool by byte offset and length. */
|
|
100
101
|
getString(offset, len) {
|
|
101
102
|
if (len === 0)
|
|
102
103
|
return "";
|
|
103
|
-
const {
|
|
104
|
-
const bytes = new Uint8Array(this.#view.buffer, this.#view.byteOffset +
|
|
104
|
+
const { stringPoolOffset } = this.#header;
|
|
105
|
+
const bytes = new Uint8Array(this.#view.buffer, this.#view.byteOffset + stringPoolOffset + offset, len);
|
|
105
106
|
return this.#textDecoder.decode(bytes);
|
|
106
107
|
}
|
|
107
108
|
/** Get position data for a node. */
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export type { MdastPluginDefinition, HastPluginDefinition, MdastPluginInput, Has
|
|
|
5
5
|
export type { HastVisitorInstance, HastVisitorContext, HastFilteredVisitor, HastContent, EstreeProgram, } from "./hast/hast-visitor.js";
|
|
6
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, MdastContent } from "./mdast/mdast-visitor.js";
|
|
8
|
+
export type { MdastPluginInstance, MdastVisitorContext, 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";
|
|
@@ -9,7 +9,9 @@ export declare class MdastReader {
|
|
|
9
9
|
getNodeData(nodeId: number): string | null;
|
|
10
10
|
get nodeCount(): number;
|
|
11
11
|
get header(): BufferHeader;
|
|
12
|
-
|
|
12
|
+
/** The full string pool (original input + interning heap). Not the document
|
|
13
|
+
* source as written; for that, read `ctx.source` from a plugin. */
|
|
14
|
+
getStringPool(): string;
|
|
13
15
|
getString(offset: number, len: number): string;
|
|
14
16
|
getNode(nodeId: number): MdastNodeRaw;
|
|
15
17
|
/** Fast path: read only the type byte for a node. */
|
|
@@ -8,7 +8,7 @@ export class MdastReader {
|
|
|
8
8
|
#view;
|
|
9
9
|
#header;
|
|
10
10
|
#textDecoder;
|
|
11
|
-
#
|
|
11
|
+
#stringPoolCache = null;
|
|
12
12
|
constructor(buffer) {
|
|
13
13
|
if (buffer instanceof Uint8Array) {
|
|
14
14
|
this.#view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
@@ -38,8 +38,8 @@ export class MdastReader {
|
|
|
38
38
|
childrenOffset: v.getUint32(HEADER.children_offset, true),
|
|
39
39
|
typeDataLen: v.getUint32(HEADER.type_data_len, true),
|
|
40
40
|
typeDataOffset: v.getUint32(HEADER.type_data_offset, true),
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
stringPoolLen: v.getUint32(HEADER.string_pool_len, true),
|
|
42
|
+
stringPoolOffset: v.getUint32(HEADER.string_pool_offset, true),
|
|
43
43
|
nodeDataCount: v.getUint32(HEADER.node_data_count, true),
|
|
44
44
|
nodeDataOffset: v.getUint32(HEADER.node_data_offset, true),
|
|
45
45
|
};
|
|
@@ -73,19 +73,21 @@ export class MdastReader {
|
|
|
73
73
|
get header() {
|
|
74
74
|
return { ...this.#header };
|
|
75
75
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
/** The full string pool (original input + interning heap). Not the document
|
|
77
|
+
* source as written; for that, read `ctx.source` from a plugin. */
|
|
78
|
+
getStringPool() {
|
|
79
|
+
if (this.#stringPoolCache === null) {
|
|
80
|
+
const { stringPoolOffset, stringPoolLen } = this.#header;
|
|
81
|
+
const bytes = new Uint8Array(this.#view.buffer, this.#view.byteOffset + stringPoolOffset, stringPoolLen);
|
|
82
|
+
this.#stringPoolCache = this.#textDecoder.decode(bytes);
|
|
81
83
|
}
|
|
82
|
-
return this.#
|
|
84
|
+
return this.#stringPoolCache;
|
|
83
85
|
}
|
|
84
86
|
getString(offset, len) {
|
|
85
87
|
if (len === 0)
|
|
86
88
|
return "";
|
|
87
|
-
const {
|
|
88
|
-
const bytes = new Uint8Array(this.#view.buffer, this.#view.byteOffset +
|
|
89
|
+
const { stringPoolOffset } = this.#header;
|
|
90
|
+
const bytes = new Uint8Array(this.#view.buffer, this.#view.byteOffset + stringPoolOffset + offset, len);
|
|
89
91
|
return this.#textDecoder.decode(bytes);
|
|
90
92
|
}
|
|
91
93
|
getNode(nodeId) {
|
package/dist/types.d.ts
CHANGED
|
@@ -109,8 +109,8 @@ export interface BufferHeader {
|
|
|
109
109
|
childrenOffset: number;
|
|
110
110
|
typeDataLen: number;
|
|
111
111
|
typeDataOffset: number;
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
stringPoolLen: number;
|
|
113
|
+
stringPoolOffset: number;
|
|
114
114
|
/** Number of nodes that carry an extra JSON `data` blob. */
|
|
115
115
|
nodeDataCount: number;
|
|
116
116
|
/** Offset of the node-data section: `[id u32][len u32][bytes...]` repeated. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "satteri",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"description": "High-performance Markdown and MDX processing",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -84,15 +84,15 @@
|
|
|
84
84
|
]
|
|
85
85
|
},
|
|
86
86
|
"optionalDependencies": {
|
|
87
|
-
"@bruits/satteri-linux-x64-gnu": "0.9.
|
|
88
|
-
"@bruits/satteri-linux-arm64-gnu": "0.9.
|
|
89
|
-
"@bruits/satteri-linux-x64-musl": "0.9.
|
|
90
|
-
"@bruits/satteri-linux-arm64-musl": "0.9.
|
|
91
|
-
"@bruits/satteri-darwin-x64": "0.9.
|
|
92
|
-
"@bruits/satteri-darwin-arm64": "0.9.
|
|
93
|
-
"@bruits/satteri-win32-x64-msvc": "0.9.
|
|
94
|
-
"@bruits/satteri-win32-arm64-msvc": "0.9.
|
|
95
|
-
"@bruits/satteri-wasm32-wasi": "0.9.
|
|
87
|
+
"@bruits/satteri-linux-x64-gnu": "0.9.4",
|
|
88
|
+
"@bruits/satteri-linux-arm64-gnu": "0.9.4",
|
|
89
|
+
"@bruits/satteri-linux-x64-musl": "0.9.4",
|
|
90
|
+
"@bruits/satteri-linux-arm64-musl": "0.9.4",
|
|
91
|
+
"@bruits/satteri-darwin-x64": "0.9.4",
|
|
92
|
+
"@bruits/satteri-darwin-arm64": "0.9.4",
|
|
93
|
+
"@bruits/satteri-win32-x64-msvc": "0.9.4",
|
|
94
|
+
"@bruits/satteri-win32-arm64-msvc": "0.9.4",
|
|
95
|
+
"@bruits/satteri-wasm32-wasi": "0.9.4"
|
|
96
96
|
},
|
|
97
97
|
"scripts": {
|
|
98
98
|
"build:binary:wasm": "napi build --manifest-path ../../crates/satteri-napi-binding/Cargo.toml --output-dir . --platform --release --esm --strip --target wasm32-wasip1-threads",
|