satteri 0.6.3 → 0.7.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 CHANGED
@@ -163,6 +163,14 @@ heading(node) {
163
163
  }
164
164
  ```
165
165
 
166
+ ### How transforms compose
167
+
168
+ 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:
169
+
170
+ - **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.
171
+ - **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.
172
+ - **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.
173
+
166
174
  ### Async plugins
167
175
 
168
176
  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.
@@ -289,7 +297,8 @@ Type-safe wrapper for HAST plugin definitions.
289
297
  interface CompileOptions {
290
298
  mdastPlugins?: MdastPluginDefinition[];
291
299
  hastPlugins?: HastPluginDefinition[];
292
- filename?: string;
300
+ features?: Features;
301
+ fileURL?: URL;
293
302
  }
294
303
 
295
304
  // mdxToJs accepts MdxCompileOptions, which extends CompileOptions
@@ -298,6 +307,10 @@ interface MdxCompileOptions extends CompileOptions {
298
307
  }
299
308
  ```
300
309
 
310
+ `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)`.
311
+
312
+ `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.
313
+
301
314
  ## License
302
315
 
303
316
  MIT
package/dist/compile.d.ts CHANGED
@@ -16,14 +16,90 @@ export interface SmartPunctuationOptions {
16
16
  /** Replace `...` with ellipsis (`…`). Default: true. */
17
17
  ellipses?: boolean;
18
18
  }
19
+ /**
20
+ * Per-backref callback. Invoked once per anchor in the footnotes section
21
+ * with 1-based `referenceNumber` and `rerunIndex` (1 on the first backref
22
+ * to that definition, 2 on the second, and so on). Must return the final
23
+ * string used as the backref content or `aria-label`.
24
+ */
25
+ export type FootnoteBackrefCallback = (referenceNumber: number, rerunIndex: number) => string;
26
+ /**
27
+ * i18n strings for the GFM footnotes section. Mirrors `footnoteLabel`,
28
+ * `footnoteBackLabel`, and `footnoteBackContent` from remark-rehype.
29
+ *
30
+ * `backContent` and `backLabel` each accept either a template string with
31
+ * the `{reference}` placeholder (substituted with `1` or `1-2` to match
32
+ * remark-rehype's default suffix), or a callback receiving the raw
33
+ * `(referenceNumber, rerunIndex)` pair.
34
+ *
35
+ * Passing this object enables footnotes. To turn them off, use
36
+ * `gfm: { footnotes: false }`.
37
+ */
38
+ export interface FootnoteOptions {
39
+ /** `<h2>` label opening the footnotes section. Default: `"Footnotes"`. */
40
+ label?: string;
41
+ /**
42
+ * Backref `<a>` content. Default: `"↩"`.
43
+ *
44
+ * Template form: the string is used as-is, and a `<sup>K</sup>` marker
45
+ * is auto-appended on reruns (k > 1). Callback form: returns the full
46
+ * content for each backref; no auto-sup is added.
47
+ */
48
+ backContent?: string | FootnoteBackrefCallback;
49
+ /**
50
+ * Backref `aria-label`. Default: `"Back to reference {reference}"`.
51
+ *
52
+ * Template form: `{reference}` becomes `n` for the first backref, `n-K`
53
+ * for subsequent ones. Callback form: returns the `aria-label` string
54
+ * for each backref.
55
+ */
56
+ backLabel?: string | FootnoteBackrefCallback;
57
+ }
58
+ /** Granular GFM toggles, nested under {@link Features.gfm}. */
59
+ export interface GfmOptions {
60
+ /**
61
+ * Enable GFM footnotes (`[^id]`). Default: true.
62
+ *
63
+ * Pass `false` to drop footnote parsing while keeping the rest of GFM;
64
+ * pass an object to enable footnotes with custom i18n strings (see
65
+ * {@link FootnoteOptions}).
66
+ */
67
+ footnotes?: boolean | FootnoteOptions;
68
+ }
69
+ /** Granular math toggles, nested under {@link Features.math}. */
70
+ export interface MathOptions {
71
+ /**
72
+ * Treat single-dollar runs (`$ ... $`) as inline math. Default: true.
73
+ *
74
+ * Set `false` to keep single `$` as literal text while still parsing
75
+ * `$$ ... $$` display math. Mirrors `singleDollarTextMath` from
76
+ * remark-math.
77
+ */
78
+ singleDollarTextMath?: boolean;
79
+ }
19
80
  /** Parser feature toggles. All default to their documented value when omitted. */
20
81
  export interface Features {
21
- /** GFM: tables, footnotes, strikethrough, task lists. Default: true. */
22
- gfm?: boolean;
82
+ /**
83
+ * GFM: tables, footnotes, strikethrough, task lists. Default: true.
84
+ *
85
+ * Pass an options object for granular control:
86
+ * ```ts
87
+ * gfm: { footnotes: false } // skip footnotes only
88
+ * gfm: { footnotes: { label: "Notes" } } // localize footnotes
89
+ * ```
90
+ */
91
+ gfm?: boolean | GfmOptions;
23
92
  /** Frontmatter: YAML (`--- ... ---`) and TOML (`+++ ... +++`). Default: true. */
24
93
  frontmatter?: boolean;
25
- /** Math blocks and inline math. Default: true. */
26
- math?: boolean;
94
+ /**
95
+ * Math blocks and inline math. Default: true.
96
+ *
97
+ * Pass an options object for granular control:
98
+ * ```ts
99
+ * math: { singleDollarTextMath: false } // $$..$$ only, $..$ literal
100
+ * ```
101
+ */
102
+ math?: boolean | MathOptions;
27
103
  /** Heading attributes (`# text { #id .class }`). Default: true. */
28
104
  headingAttributes?: boolean;
29
105
  /** Colon-delimited container directive blocks (`:::`). Default: false. */
@@ -48,7 +124,12 @@ export interface CompileOptions {
48
124
  mdastPlugins?: MdastPluginInput[];
49
125
  hastPlugins?: HastPluginInput[];
50
126
  features?: Features;
51
- filename?: string;
127
+ /**
128
+ * The document being processed, surfaced to plugins as `ctx.fileURL`. Must
129
+ * be a `URL` (e.g. Astro's `fileURL`); convert a filesystem path with Node's
130
+ * `pathToFileURL` before passing it.
131
+ */
132
+ fileURL?: URL;
52
133
  }
53
134
  /**
54
135
  * MDX-only compile options.
package/dist/compile.js CHANGED
@@ -5,16 +5,55 @@ import { MdastReader } from "./mdast/mdast-reader.js";
5
5
  import { materializeMdastTree } from "./mdast/mdast-materializer.js";
6
6
  import { HastReader } from "./hast/hast-reader.js";
7
7
  import { materializeHastTree } from "./hast/hast-materializer.js";
8
+ /**
9
+ * Split the user-facing `Features` (with nested unions) into the flat napi
10
+ * `JsFeatures` shape plus the conversion-side `JsConvertOptions` carrying
11
+ * the footnote i18n strings. The public API only exposes `features`; the
12
+ * footnote strings are routed to napi internally.
13
+ */
8
14
  function featuresToNative(features) {
9
15
  if (!features)
10
- return undefined;
16
+ return { features: undefined, convertOptions: undefined };
11
17
  const result = {};
12
- if (features.gfm !== undefined)
13
- result.gfm = features.gfm;
18
+ let convertOptions;
19
+ if (features.gfm !== undefined) {
20
+ if (typeof features.gfm === "object") {
21
+ const g = features.gfm;
22
+ const gfmOpts = {};
23
+ if (g.footnotes !== undefined) {
24
+ if (typeof g.footnotes === "object") {
25
+ gfmOpts.footnotes = true;
26
+ convertOptions = convertOptions ?? {};
27
+ if (g.footnotes.label !== undefined)
28
+ convertOptions.footnoteLabel = g.footnotes.label;
29
+ if (g.footnotes.backContent !== undefined)
30
+ convertOptions.footnoteBackContent = g.footnotes.backContent;
31
+ if (g.footnotes.backLabel !== undefined)
32
+ convertOptions.footnoteBackLabel = g.footnotes.backLabel;
33
+ }
34
+ else {
35
+ gfmOpts.footnotes = g.footnotes;
36
+ }
37
+ }
38
+ result.gfmOptions = gfmOpts;
39
+ }
40
+ else {
41
+ result.gfm = features.gfm;
42
+ }
43
+ }
14
44
  if (features.frontmatter !== undefined)
15
45
  result.frontmatter = features.frontmatter;
16
- if (features.math !== undefined)
17
- result.math = features.math;
46
+ if (features.math !== undefined) {
47
+ if (typeof features.math === "object") {
48
+ const mathOpts = {};
49
+ if (features.math.singleDollarTextMath !== undefined)
50
+ mathOpts.singleDollarTextMath = features.math.singleDollarTextMath;
51
+ result.mathOptions = mathOpts;
52
+ }
53
+ else {
54
+ result.math = features.math;
55
+ }
56
+ }
18
57
  if (features.headingAttributes !== undefined)
19
58
  result.headingAttributes = features.headingAttributes;
20
59
  if (features.directive !== undefined)
@@ -33,41 +72,46 @@ function featuresToNative(features) {
33
72
  result.smartPunctuation = features.smartPunctuation;
34
73
  }
35
74
  }
36
- return result;
75
+ return { features: result, convertOptions };
76
+ }
77
+ function warnDroppedTransforms(plugin, dropped) {
78
+ const name = plugin.name ?? "<anonymous>";
79
+ const noun = dropped === 1 ? "transform" : "transforms";
80
+ console.warn(`satteri: plugin "${name}" queued ${dropped} mdast ${noun} on node(s) that were removed or ` +
81
+ `replaced earlier in the same pass; ${dropped === 1 ? "it was" : "they were"} dropped.`);
37
82
  }
38
- function runMdastPluginsOnHandle(handle, plugins, filename) {
39
- let pendingCommands = null;
83
+ function runMdastPluginsOnHandle(handle, plugins, fileURL) {
84
+ // Each plugin runs once over the tree. A transform that passes a child
85
+ // through (returning it inside the replacement) keeps that child's identity,
86
+ // so a patch the same pass queued on it still applies — nesting composes in
87
+ // one pass. A plugin's own freshly-built nodes are not re-walked; transform
88
+ // them up front, or hand off to a later plugin that sees the materialized tree.
89
+ const runPlugin = (plugin) => {
90
+ const subs = resolveMdastSubscriptions(plugin);
91
+ const result = visitMdastHandle(handle, plugin, subs, () => getHandleSource(handle), fileURL);
92
+ const apply = (r) => {
93
+ if (!r.hasMutations)
94
+ return;
95
+ const dropped = applyCommandsToMdastHandle(handle, r.commandBuffer);
96
+ if (dropped)
97
+ warnDroppedTransforms(plugin, dropped);
98
+ };
99
+ return result instanceof Promise ? result.then(apply) : apply(result);
100
+ };
40
101
  let i = 0;
41
102
  const runNext = () => {
42
103
  while (i < plugins.length) {
43
- const idx = i++;
44
- const raw = plugins[idx];
104
+ const raw = plugins[i++];
45
105
  const plugin = typeof raw === "function" ? raw() : raw;
46
- const subs = resolveMdastSubscriptions(plugin);
47
- const result = visitMdastHandle(handle, plugin, subs, () => getHandleSource(handle), filename);
48
- if (result instanceof Promise) {
49
- return result.then((r) => {
50
- applyMdastResult(r, idx, plugins.length, handle);
51
- return runNext();
52
- });
53
- }
54
- applyMdastResult(result, idx, plugins.length, handle);
106
+ const r = runPlugin(plugin);
107
+ if (r instanceof Promise)
108
+ return r.then(runNext);
55
109
  }
56
- return { handle, pendingCommands };
110
+ return { handle };
57
111
  };
58
- function applyMdastResult(result, idx, total, h) {
59
- if (result.hasMutations) {
60
- if (idx === total - 1) {
61
- pendingCommands = result.commandBuffer;
62
- }
63
- else {
64
- applyCommandsToMdastHandle(h, result.commandBuffer);
65
- }
66
- }
67
- }
68
112
  return runNext();
69
113
  }
70
- function runHastPluginsOnHandle(handle, plugins, source, filename) {
114
+ function runHastPluginsOnHandle(handle, plugins, source, fileURL) {
71
115
  if (plugins.length === 0)
72
116
  return;
73
117
  let i = 0;
@@ -77,7 +121,7 @@ function runHastPluginsOnHandle(handle, plugins, source, filename) {
77
121
  i++;
78
122
  const plugin = typeof raw === "function" ? raw() : raw;
79
123
  const subs = resolveSubscriptions(plugin);
80
- const result = visitHastHandle(handle, plugin, subs, source, filename);
124
+ const result = visitHastHandle(handle, plugin, subs, source, fileURL);
81
125
  if (result instanceof Promise) {
82
126
  return result.then(runNext);
83
127
  }
@@ -129,9 +173,9 @@ function mdxOptionsToNative(opts) {
129
173
  return result;
130
174
  }
131
175
  export function markdownToHtml(source, options = {}) {
132
- const { mdastPlugins = [], hastPlugins = [], features, filename = "<unknown>" } = options;
133
- const nativeFeatures = featuresToNative(features);
134
- const result = createHastHandleFromMdast(source, mdastPlugins, false, filename, nativeFeatures);
176
+ const { mdastPlugins = [], hastPlugins = [], features, fileURL } = options;
177
+ const { features: nativeFeatures, convertOptions: nativeConvertOptions } = featuresToNative(features);
178
+ const result = createHastHandleFromMdast(source, mdastPlugins, false, fileURL, nativeFeatures, nativeConvertOptions);
135
179
  const renderAndDrop = (h, frontmatter) => {
136
180
  try {
137
181
  const html = renderHandle(h);
@@ -144,7 +188,7 @@ export function markdownToHtml(source, options = {}) {
144
188
  const runHastThenRender = (r) => {
145
189
  let hastResult;
146
190
  try {
147
- hastResult = runHastPluginsOnHandle(r.hastHandle, hastPlugins, source, filename);
191
+ hastResult = runHastPluginsOnHandle(r.hastHandle, hastPlugins, source, fileURL);
148
192
  }
149
193
  catch (err) {
150
194
  dropHandle(r.hastHandle);
@@ -163,10 +207,10 @@ export function markdownToHtml(source, options = {}) {
163
207
  return runHastThenRender(result);
164
208
  }
165
209
  export function mdxToJs(source, options = {}) {
166
- const { mdastPlugins = [], hastPlugins = [], features, filename = "<unknown>", ...mdxFields } = options;
210
+ const { mdastPlugins = [], hastPlugins = [], features, fileURL, ...mdxFields } = options;
167
211
  const mdxOptions = mdxOptionsToNative(mdxFields);
168
- const nativeFeatures = featuresToNative(features);
169
- const result = createHastHandleFromMdast(source, mdastPlugins, true, filename, nativeFeatures);
212
+ const { features: nativeFeatures, convertOptions: nativeConvertOptions } = featuresToNative(features);
213
+ const result = createHastHandleFromMdast(source, mdastPlugins, true, fileURL, nativeFeatures, nativeConvertOptions);
170
214
  const compileAndDrop = (h, frontmatter) => {
171
215
  try {
172
216
  const code = compileHandle(h, mdxOptions);
@@ -179,7 +223,7 @@ export function mdxToJs(source, options = {}) {
179
223
  const runHastThenCompile = (r) => {
180
224
  let hastResult;
181
225
  try {
182
- hastResult = runHastPluginsOnHandle(r.hastHandle, hastPlugins, source, filename);
226
+ hastResult = runHastPluginsOnHandle(r.hastHandle, hastPlugins, source, fileURL);
183
227
  }
184
228
  catch (err) {
185
229
  dropHandle(r.hastHandle);
@@ -224,9 +268,11 @@ function readFrontmatter(handle) {
224
268
  /** Parse, run mdast plugins, capture frontmatter, then convert to HAST.
225
269
  * Frontmatter is read from the post-plugin MDAST so visitor mutations to
226
270
  * the yaml/toml node are reflected in the returned value. */
227
- function createHastHandleFromMdast(source, mdastPlugins, mdx, filename,
271
+ function createHastHandleFromMdast(source, mdastPlugins, mdx, fileURL,
228
272
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
229
- nativeFeatures) {
273
+ nativeFeatures,
274
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
275
+ nativeConvertOptions) {
230
276
  const mdastHandle = mdx
231
277
  ? createMdxMdastHandle(source, nativeFeatures)
232
278
  : createMdastHandle(source, nativeFeatures);
@@ -234,11 +280,8 @@ nativeFeatures) {
234
280
  // on success, but if any step here throws the handle would otherwise leak.
235
281
  const finalize = (r) => {
236
282
  try {
237
- if (r.pendingCommands) {
238
- applyCommandsToMdastHandle(r.handle, r.pendingCommands);
239
- }
240
283
  const frontmatter = readFrontmatter(r.handle);
241
- const hastHandle = convertMdastToHastHandle(r.handle);
284
+ const hastHandle = convertMdastToHastHandle(r.handle, nativeConvertOptions);
242
285
  return { hastHandle, frontmatter };
243
286
  }
244
287
  finally {
@@ -247,9 +290,9 @@ nativeFeatures) {
247
290
  };
248
291
  try {
249
292
  if (mdastPlugins.length === 0) {
250
- return finalize({ handle: mdastHandle, pendingCommands: null });
293
+ return finalize({ handle: mdastHandle });
251
294
  }
252
- const mdastResult = runMdastPluginsOnHandle(mdastHandle, mdastPlugins, filename);
295
+ const mdastResult = runMdastPluginsOnHandle(mdastHandle, mdastPlugins, fileURL);
253
296
  if (mdastResult instanceof Promise) {
254
297
  return mdastResult.then(finalize, (err) => {
255
298
  dropHandle(mdastHandle);
@@ -266,7 +309,7 @@ nativeFeatures) {
266
309
  // Step-by-step API: individual pipeline stages with materialized trees
267
310
  /** Parse Markdown source into a materialized mdast tree. */
268
311
  export function markdownToMdast(source, options = {}) {
269
- const handle = createMdastHandle(source, featuresToNative(options.features));
312
+ const handle = createMdastHandle(source, featuresToNative(options.features).features);
270
313
  try {
271
314
  return materializeMdastTree(new MdastReader(serializeHandle(handle)));
272
315
  }
@@ -276,7 +319,7 @@ export function markdownToMdast(source, options = {}) {
276
319
  }
277
320
  /** Parse MDX source into a materialized mdast tree. */
278
321
  export function mdxToMdast(source, options = {}) {
279
- const handle = createMdxMdastHandle(source, featuresToNative(options.features));
322
+ const handle = createMdxMdastHandle(source, featuresToNative(options.features).features);
280
323
  try {
281
324
  return materializeMdastTree(new MdastReader(serializeHandle(handle)));
282
325
  }
@@ -286,7 +329,8 @@ export function mdxToMdast(source, options = {}) {
286
329
  }
287
330
  /** Convert Markdown source to a materialized hast tree. */
288
331
  export function markdownToHast(source, options = {}) {
289
- const handle = createHastHandle(source, featuresToNative(options.features));
332
+ const { features: nativeFeatures, convertOptions } = featuresToNative(options.features);
333
+ const handle = createHastHandle(source, nativeFeatures, convertOptions);
290
334
  try {
291
335
  return materializeHastTree(new HastReader(serializeHandle(handle)));
292
336
  }
@@ -296,7 +340,8 @@ export function markdownToHast(source, options = {}) {
296
340
  }
297
341
  /** Convert MDX source to a materialized hast tree. */
298
342
  export function mdxToHast(source, options = {}) {
299
- const handle = createMdxHastHandle(source, featuresToNative(options.features));
343
+ const { features: nativeFeatures, convertOptions } = featuresToNative(options.features);
344
+ const handle = createMdxHastHandle(source, nativeFeatures, convertOptions);
300
345
  try {
301
346
  return materializeHastTree(new HastReader(serializeHandle(handle)));
302
347
  }
@@ -15,7 +15,12 @@ export interface HastDiagnostic {
15
15
  }
16
16
  export interface HastVisitorContext {
17
17
  readonly source: string;
18
- readonly filename: string;
18
+ /**
19
+ * The URL of the document being processed (the compile `fileURL` option),
20
+ * or `undefined` when none was given. Use `fileURLToPath(ctx.fileURL)` for a
21
+ * decoded filesystem path.
22
+ */
23
+ readonly fileURL: URL | undefined;
19
24
  removeNode(node: Readonly<HastNode>): void;
20
25
  replaceNode(node: Readonly<HastNode>, newNode: HastNode): void;
21
26
  insertBefore(node: Readonly<HastNode>, newNode: HastNode): void;
@@ -70,5 +75,5 @@ export declare function resolveSubscriptions(plugin: HastVisitorInstance): Resol
70
75
  *
71
76
  * Returns void if all visitors are sync, or a Promise if any visitor is async.
72
77
  */
73
- export declare function visitHastHandle(handle: HastHandle, plugin: HastVisitorInstance, subs: ResolvedSubscription[], source: string | (() => string), filename: string): void | Promise<void>;
78
+ export declare function visitHastHandle(handle: HastHandle, plugin: HastVisitorInstance, subs: ResolvedSubscription[], source: string | (() => string), fileURL: URL | undefined): void | Promise<void>;
74
79
  export {};
@@ -21,24 +21,37 @@ function attachParseExpression(node, parseFn) {
21
21
  configurable: true,
22
22
  });
23
23
  }
24
- /** Inject `_hast: true` marker on a HastNode and all its children for JSON serialization. */
24
+ /**
25
+ * Serialize a HastNode for the command buffer. Marks each node `_hast: true`,
26
+ * and — except at the root, which is the new replacement content — emits a
27
+ * reused (materialized) node as a `{ _ref: id }` placeholder so the rebuild
28
+ * splices the original in place (preserving its id, applying any pending patch
29
+ * on it) instead of rebuilding it fresh.
30
+ */
25
31
  function markHast(node) {
26
- const n = node;
32
+ return markHastNode(node, true);
33
+ }
34
+ function markHastNode(node, isRoot) {
35
+ if (!isRoot) {
36
+ const id = nodeIdMap.get(node) ?? node._nodeId;
37
+ if (typeof id === "number")
38
+ return { _ref: id };
39
+ }
27
40
  const obj = { _hast: true, type: node.type };
28
41
  if ("tagName" in node)
29
- obj.tagName = n.tagName;
42
+ obj.tagName = node.tagName;
30
43
  if ("properties" in node)
31
- obj.properties = n.properties;
44
+ obj.properties = node.properties;
32
45
  if ("value" in node)
33
- obj.value = n.value;
46
+ obj.value = node.value;
34
47
  if ("name" in node)
35
- obj.name = n.name;
48
+ obj.name = node.name;
36
49
  if ("attributes" in node)
37
- obj.attributes = n.attributes;
38
- if ("data" in node && n.data != null)
39
- obj.data = n.data;
50
+ obj.attributes = node.attributes;
51
+ if ("data" in node && node.data != null)
52
+ obj.data = node.data;
40
53
  if ("children" in node) {
41
- obj.children = n.children.map(markHast);
54
+ obj.children = node.children.map((c) => markHastNode(c, false));
42
55
  }
43
56
  return obj;
44
57
  }
@@ -52,11 +65,11 @@ class HastVisitorContextImpl {
52
65
  #pendingNodes = new Map();
53
66
  #handle;
54
67
  #getSource;
55
- filename;
56
- constructor(handle, getSource, filename) {
68
+ fileURL;
69
+ constructor(handle, getSource, fileURL) {
57
70
  this.#handle = handle;
58
71
  this.#getSource = getSource;
59
- this.filename = filename;
72
+ this.fileURL = fileURL;
60
73
  }
61
74
  get source() {
62
75
  const value = this.#getSource();
@@ -99,7 +112,8 @@ class HastVisitorContextImpl {
99
112
  }
100
113
  if (node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement") {
101
114
  // MDX JSX nodes use `attributes`, not `properties`, keep replaceNode path
102
- const current = this.#pendingNodes.get(id) ?? node;
115
+ const pending = this.#pendingNodes.get(id);
116
+ const current = (pending ?? node);
103
117
  const updated = { ...current };
104
118
  const attrs = [...(updated.attributes ?? [])];
105
119
  const idx = attrs.findIndex((a) => a.type === "mdxJsxAttribute" && a.name === key);
@@ -109,7 +123,7 @@ class HastVisitorContextImpl {
109
123
  ? null
110
124
  : typeof value === "string"
111
125
  ? value
112
- : `${value}`;
126
+ : String(value);
113
127
  attrs.push({ type: "mdxJsxAttribute", name: key, value: attrValue });
114
128
  updated.attributes = attrs;
115
129
  this.replaceNode(node, updated);
@@ -213,6 +227,21 @@ function decodeProperties(view, buf, pos) {
213
227
  }
214
228
  return properties;
215
229
  }
230
+ /** Decode the 24-byte position block written by `serialize_hast_node_inline`. */
231
+ function readPositionPrefix(view, offset) {
232
+ const startOffset = view.getUint32(offset, true);
233
+ const endOffset = view.getUint32(offset + 4, true);
234
+ const startLine = view.getUint32(offset + 8, true);
235
+ const startColumn = view.getUint32(offset + 12, true);
236
+ const endLine = view.getUint32(offset + 16, true);
237
+ const endColumn = view.getUint32(offset + 20, true);
238
+ if (startLine === 0 && startOffset === 0)
239
+ return undefined;
240
+ return {
241
+ start: { offset: startOffset, line: startLine, column: startColumn },
242
+ end: { offset: endOffset, line: endLine, column: endColumn },
243
+ };
244
+ }
216
245
  /**
217
246
  * Walk-path element node: uses prototype getters instead of per-instance
218
247
  * Object.defineProperty. V8 optimises shared hidden classes far better,
@@ -228,8 +257,6 @@ class WalkElement {
228
257
  /** @internal */ _propsPos;
229
258
  /** @internal */ _childIds;
230
259
  /** @internal */ _resolver;
231
- /** @internal */ _dataPos;
232
- /** @internal */ _dataLen;
233
260
  get properties() {
234
261
  const val = decodeProperties(this._view, this._buf, this._propsPos);
235
262
  Object.defineProperty(this, "properties", {
@@ -250,59 +277,29 @@ class WalkElement {
250
277
  });
251
278
  return val;
252
279
  }
253
- get data() {
254
- if (this._dataPos < 0)
255
- return undefined;
256
- const val = JSON.parse(textDecoder.decode(this._buf.subarray(this._dataPos, this._dataPos + this._dataLen)));
257
- Object.defineProperty(this, "data", {
258
- value: val,
259
- writable: true,
260
- enumerable: true,
261
- configurable: true,
262
- });
263
- return val;
264
- }
265
280
  }
266
- /** Read a matched element node from the binary data section into a HastNode.
267
- * Only tagName is decoded eagerly; properties, children, and data are lazy. */
268
- function readElementFromBinary(view, buf, offset, nodeId, resolver) {
281
+ /** Read the tail of a matched element node (tag + properties).
282
+ * Common prelude (data/position/children) is already consumed by `readMatchedNode`. */
283
+ function readElementFromBinary(view, buf, offset, nodeId, resolver, position, childIds, data) {
269
284
  let pos = offset;
270
285
  // Eager: tagName (almost always accessed by visitors)
271
286
  const tagLen = view.getUint16(pos, true);
272
287
  pos += 2;
273
288
  const tagName = textDecoder.decode(buf.subarray(pos, pos + tagLen));
274
289
  pos += tagLen;
275
- // Pre-scan: find section byte offsets without decoding strings
276
290
  const propsPos = pos;
277
- const propCount = view.getUint16(pos, true);
278
- pos += 2;
279
- for (let i = 0; i < propCount; i++) {
280
- const nLen = view.getUint16(pos, true);
281
- pos += 2 + nLen + 1; // name + kind byte
282
- const vLen = view.getUint16(pos, true);
283
- pos += 2 + vLen; // value
284
- }
285
- const childCount = view.getUint16(pos, true);
286
- pos += 2;
287
- const childIdsPos = pos;
288
- pos += childCount * 4;
289
- const nodeDataLen = view.getUint32(pos, true);
290
- pos += 4;
291
- const nodeDataPos = nodeDataLen > 0 ? pos : -1;
292
- // Collect child IDs for lazy materialization
293
- const ids = [];
294
- for (let i = 0; i < childCount; i++)
295
- ids.push(view.getUint32(childIdsPos + i * 4, true));
296
291
  // Build node using class (prototype getters, no per-instance defineProperty)
297
292
  const node = new WalkElement();
298
293
  node.tagName = tagName;
294
+ if (position !== undefined)
295
+ node.position = position;
296
+ if (data !== null)
297
+ node.data = data;
299
298
  node._view = view;
300
299
  node._buf = buf;
301
300
  node._propsPos = propsPos;
302
- node._childIds = ids;
301
+ node._childIds = childIds;
303
302
  node._resolver = resolver;
304
- node._dataPos = nodeDataPos;
305
- node._dataLen = nodeDataLen;
306
303
  nodeIdMap.set(node, nodeId);
307
304
  return node;
308
305
  }
@@ -315,10 +312,15 @@ const TEXT_NODE_TYPES = {
315
312
  [HAST_MDX_TEXT_EXPRESSION]: "mdxTextExpression",
316
313
  [HAST_MDX_ESM]: "mdxjsEsm",
317
314
  };
318
- function readTextFromBinary(view, buf, offset, nodeId, nodeType) {
315
+ function readTextFromBinary(view, buf, offset, nodeId, nodeType, position, data) {
319
316
  const valLen = view.getUint32(offset, true);
320
317
  const value = textDecoder.decode(buf.subarray(offset + 4, offset + 4 + valLen));
321
- const node = { type: TEXT_NODE_TYPES[nodeType], value };
318
+ const base = { type: TEXT_NODE_TYPES[nodeType], value };
319
+ if (position !== undefined)
320
+ base.position = position;
321
+ if (data !== null)
322
+ base.data = data;
323
+ const node = base;
322
324
  nodeIdMap.set(node, nodeId);
323
325
  if (nodeType === HAST_MDX_FLOW_EXPRESSION || nodeType === HAST_MDX_TEXT_EXPRESSION) {
324
326
  attachParseExpression(node, napiParseExpression);
@@ -329,14 +331,14 @@ function readTextFromBinary(view, buf, offset, nodeId, nodeType) {
329
331
  return node;
330
332
  }
331
333
  /** Read an MDX JSX element from the binary data section. */
332
- function readMdxJsxFromBinary(view, buf, offset, nodeId, nodeType, resolver) {
334
+ function readMdxJsxFromBinary(view, buf, offset, nodeId, nodeType, resolver, position, childIds, data) {
333
335
  let pos = offset;
334
336
  // Name
335
337
  const nameLen = view.getUint16(pos, true);
336
338
  pos += 2;
337
339
  const name = nameLen > 0 ? textDecoder.decode(buf.subarray(pos, pos + nameLen)) : null;
338
340
  pos += nameLen;
339
- // Attributes: [kind: u8][nameLen: u16][name][valLen: u16][val]
341
+ // Attributes: [kind: u8][nameLen: u16][name][valLen: u32][val]
340
342
  const attrCount = view.getUint16(pos, true);
341
343
  pos += 2;
342
344
  const attributes = [];
@@ -370,7 +372,36 @@ function readMdxJsxFromBinary(view, buf, offset, nodeId, nodeType, resolver) {
370
372
  break;
371
373
  }
372
374
  }
373
- // Child IDs
375
+ const typeName = nodeType === HAST_MDX_JSX_ELEMENT ? "mdxJsxFlowElement" : "mdxJsxTextElement";
376
+ const base = { type: typeName, name, attributes };
377
+ if (position !== undefined)
378
+ base.position = position;
379
+ if (data !== null)
380
+ base.data = data;
381
+ nodeIdMap.set(base, nodeId);
382
+ makeLazyChildren(base, childIds, resolver);
383
+ return base;
384
+ }
385
+ function readMatchedNode(view, buf, offset, nodeId, nodeType, resolver) {
386
+ let pos = offset;
387
+ // Shared prelude (matches serialize_hast_node_inline / serialize_mdast_node_inline):
388
+ // [data_len: u32][data_bytes][position: 24B][child_count: u16][child_ids: N×u32]
389
+ // Data (JSON), eagerly parsed
390
+ const dataLen = view.getUint32(pos, true);
391
+ pos += 4;
392
+ let data = null;
393
+ if (dataLen > 0) {
394
+ const jsonStr = textDecoder.decode(buf.subarray(pos, pos + dataLen));
395
+ try {
396
+ data = JSON.parse(jsonStr);
397
+ }
398
+ catch {
399
+ /* ignore malformed JSON */
400
+ }
401
+ pos += dataLen;
402
+ }
403
+ const position = readPositionPrefix(view, pos);
404
+ pos += 24;
374
405
  const childCount = view.getUint16(pos, true);
375
406
  pos += 2;
376
407
  const childIds = [];
@@ -378,16 +409,9 @@ function readMdxJsxFromBinary(view, buf, offset, nodeId, nodeType, resolver) {
378
409
  childIds.push(view.getUint32(pos, true));
379
410
  pos += 4;
380
411
  }
381
- const typeName = nodeType === HAST_MDX_JSX_ELEMENT ? "mdxJsxFlowElement" : "mdxJsxTextElement";
382
- const node = { type: typeName, name, attributes };
383
- nodeIdMap.set(node, nodeId);
384
- makeLazyChildren(node, childIds, resolver);
385
- resolver.attachLazyData(node, nodeId);
386
- return node;
387
- }
388
- function readMatchedNode(view, buf, offset, nodeId, nodeType, resolver) {
412
+ // Dispatch to type-specific tail (pos now sits at the type-specific section)
389
413
  if (nodeType === HAST_ELEMENT) {
390
- return readElementFromBinary(view, buf, offset, nodeId, resolver);
414
+ return readElementFromBinary(view, buf, pos, nodeId, resolver, position, childIds, data);
391
415
  }
392
416
  else if (nodeType === HAST_TEXT ||
393
417
  nodeType === HAST_COMMENT ||
@@ -395,13 +419,18 @@ function readMatchedNode(view, buf, offset, nodeId, nodeType, resolver) {
395
419
  nodeType === HAST_MDX_FLOW_EXPRESSION ||
396
420
  nodeType === HAST_MDX_TEXT_EXPRESSION ||
397
421
  nodeType === HAST_MDX_ESM) {
398
- return readTextFromBinary(view, buf, offset, nodeId, nodeType);
422
+ return readTextFromBinary(view, buf, pos, nodeId, nodeType, position, data);
399
423
  }
400
424
  else if (nodeType === HAST_MDX_JSX_ELEMENT || nodeType === HAST_MDX_JSX_TEXT_ELEMENT) {
401
- return readMdxJsxFromBinary(view, buf, offset, nodeId, nodeType, resolver);
402
- }
403
- // Fallback: minimal node
404
- const node = { type: `unknown(${nodeType})` };
425
+ return readMdxJsxFromBinary(view, buf, pos, nodeId, nodeType, resolver, position, childIds, data);
426
+ }
427
+ // Fallback: minimal node carrying whatever prelude data we found
428
+ const base = { type: `unknown(${nodeType})` };
429
+ if (position !== undefined)
430
+ base.position = position;
431
+ if (data !== null)
432
+ base.data = data;
433
+ const node = base;
405
434
  nodeIdMap.set(node, nodeId);
406
435
  return node;
407
436
  }
@@ -529,9 +558,9 @@ function mergeAndReset(returnBuffer, ctx) {
529
558
  *
530
559
  * Returns void if all visitors are sync, or a Promise if any visitor is async.
531
560
  */
532
- export function visitHastHandle(handle, plugin, subs, source, filename) {
561
+ export function visitHastHandle(handle, plugin, subs, source, fileURL) {
533
562
  const getSource = typeof source === "function" ? source : () => source;
534
- const ctx = new HastVisitorContextImpl(handle, getSource, filename);
563
+ const ctx = new HastVisitorContextImpl(handle, getSource, fileURL);
535
564
  const returnBuffer = new CommandBuffer();
536
565
  const resolver = new LazyChildResolver(handle);
537
566
  const rustSubs = subs.map((s) => ({ nodeType: s.nodeType, tagFilter: s.tagFilter }));
@@ -13,8 +13,13 @@ export interface MdastDiagnostic {
13
13
  }
14
14
  export declare class MdastVisitorContext {
15
15
  #private;
16
- readonly filename: string;
17
- constructor(handle: MdastHandle, getSource: () => string, filename: string);
16
+ /**
17
+ * The URL of the document being processed (the compile `fileURL` option),
18
+ * or `undefined` when none was given. Use `fileURLToPath(ctx.fileURL)` for a
19
+ * decoded filesystem path.
20
+ */
21
+ readonly fileURL: URL | undefined;
22
+ constructor(handle: MdastHandle, getSource: () => string, fileURL: URL | undefined);
18
23
  get source(): string;
19
24
  removeNode(node: Readonly<MdastNode>): void;
20
25
  insertBefore(node: Readonly<MdastNode>, newNode: MdastNode): void;
@@ -102,5 +107,5 @@ export declare function resolveMdastSubscriptions(plugin: MdastPluginInstance):
102
107
  * Returns MdastVisitResult synchronously if all visitors are sync,
103
108
  * or Promise<MdastVisitResult> if any visitor is async.
104
109
  */
105
- export declare function visitMdastHandle(handle: MdastHandle, plugin: MdastPluginInstance, subs: MdastSubscription[], source: string | (() => string), filename: string): MdastVisitResult | Promise<MdastVisitResult>;
110
+ export declare function visitMdastHandle(handle: MdastHandle, plugin: MdastPluginInstance, subs: MdastSubscription[], source: string | (() => string), fileURL: URL | undefined): MdastVisitResult | Promise<MdastVisitResult>;
106
111
  export {};
@@ -60,11 +60,16 @@ export class MdastVisitorContext {
60
60
  #diagnostics = [];
61
61
  #handle;
62
62
  #getSource;
63
- filename;
64
- constructor(handle, getSource, filename) {
63
+ /**
64
+ * The URL of the document being processed (the compile `fileURL` option),
65
+ * or `undefined` when none was given. Use `fileURLToPath(ctx.fileURL)` for a
66
+ * decoded filesystem path.
67
+ */
68
+ fileURL;
69
+ constructor(handle, getSource, fileURL) {
65
70
  this.#handle = handle;
66
71
  this.#getSource = getSource;
67
- this.filename = filename;
72
+ this.fileURL = fileURL;
68
73
  }
69
74
  get source() {
70
75
  const value = this.#getSource();
@@ -501,6 +506,36 @@ function readMdastMatchedNode(view, buf, dataOffset, nodeId, nodeType, resolver)
501
506
  /** Apply a sync visitor result to the return buffer.
502
507
  * If the result is the same object as the input node, treat it as a no-op
503
508
  * so that context mutations (e.g. setProperty) are not clobbered. */
509
+ /** The arena id of a node if it is an existing (materialized) node, else
510
+ * undefined for a freshly-built one. */
511
+ function reusedId(node) {
512
+ if (node === null || typeof node !== "object")
513
+ return undefined;
514
+ const id = nid(node);
515
+ return typeof id === "number" ? id : undefined;
516
+ }
517
+ /**
518
+ * Rewrite a returned replacement tree so every *reused* node (one that came
519
+ * from the arena, at any depth) becomes a `{ _ref: id }` placeholder. The
520
+ * rebuild splices those originals back in place, preserving their ids — so a
521
+ * patch a nested visitor queued on a passed-through child still lands, in the
522
+ * same pass. Freshly-built nodes serialize as before. The root is never reffed:
523
+ * it is the new shape replacing the visited node.
524
+ */
525
+ function refifyReusedNodes(node, isRoot) {
526
+ if (node === null || typeof node !== "object")
527
+ return node;
528
+ if (!isRoot) {
529
+ const id = reusedId(node);
530
+ if (id !== undefined)
531
+ return { _ref: id };
532
+ }
533
+ const children = node.children;
534
+ if (Array.isArray(children)) {
535
+ return { ...node, children: children.map((c) => refifyReusedNodes(c, false)) };
536
+ }
537
+ return node;
538
+ }
504
539
  function applyMdastVisitResult(result, nodeId, returnBuffer, originalNode) {
505
540
  if (result === undefined || result === null)
506
541
  return;
@@ -515,7 +550,7 @@ function applyMdastVisitResult(result, nodeId, returnBuffer, originalNode) {
515
550
  returnBuffer.replace(nodeId, result);
516
551
  break;
517
552
  case "structured_node":
518
- returnBuffer.replace(nodeId, result);
553
+ returnBuffer.replace(nodeId, refifyReusedNodes(result, true));
519
554
  break;
520
555
  }
521
556
  }
@@ -526,9 +561,9 @@ function applyMdastVisitResult(result, nodeId, returnBuffer, originalNode) {
526
561
  * Returns MdastVisitResult synchronously if all visitors are sync,
527
562
  * or Promise<MdastVisitResult> if any visitor is async.
528
563
  */
529
- export function visitMdastHandle(handle, plugin, subs, source, filename) {
564
+ export function visitMdastHandle(handle, plugin, subs, source, fileURL) {
530
565
  const getSource = typeof source === "function" ? source : () => source;
531
- const context = new MdastVisitorContext(handle, getSource, filename);
566
+ const context = new MdastVisitorContext(handle, getSource, fileURL);
532
567
  const returnBuffer = new CommandBuffer();
533
568
  const resolver = new MdastLazyChildResolver(handle);
534
569
  const rustSubs = subs.map((s) => ({ nodeType: s.nodeType, tagFilter: [] }));
package/index.d.ts CHANGED
@@ -4,34 +4,38 @@
4
4
  * Apply MDAST commands and convert to HAST handle in one step.
5
5
  * The MDAST handle is consumed (emptied).
6
6
  */
7
- export declare function applyCommandsAndConvertToHastHandle(handle: MdastHandle, commandBuf: Uint8Array): HastHandle
7
+ export declare function applyCommandsAndConvertToHastHandle(handle: MdastHandle, commandBuf: Uint8Array, convertOptions?: JsConvertOptions | undefined | null): HastHandle
8
8
 
9
9
  /** Apply a command buffer to a HAST handle's arena in-place. */
10
10
  export declare function applyCommandsToHandle(handle: HastHandle, commandBuf: Uint8Array): void
11
11
 
12
- /** Apply a command buffer to an MDAST handle in-place. */
13
- export declare function applyCommandsToMdastHandle(handle: MdastHandle, commandBuf: Uint8Array): void
12
+ /**
13
+ * Apply a command buffer to an MDAST handle in-place. Returns how many patches
14
+ * were dropped because their target lived inside a subtree this pass removed or
15
+ * replaced (see the lenient note below); the JS pipeline warns when non-zero.
16
+ */
17
+ export declare function applyCommandsToMdastHandle(handle: MdastHandle, commandBuf: Uint8Array): number
14
18
 
15
19
  /** Compile a HAST handle's arena to MDX JavaScript. Does not consume the handle. */
16
20
  export declare function compileHandle(handle: HastHandle, options?: JsMdxOptions | undefined | null): string
17
21
 
18
22
  /** Compile MDX source directly to JavaScript. */
19
- export declare function compileMdx(source: string, options?: JsMdxOptions | undefined | null, features?: JsFeatures | undefined | null): string
23
+ export declare function compileMdx(source: string, options?: JsMdxOptions | undefined | null, features?: JsFeatures | undefined | null, convertOptions?: JsConvertOptions | undefined | null): string
20
24
 
21
25
  /** Convert an MDAST handle to a HAST handle. The MDAST handle is consumed (emptied). */
22
- export declare function convertMdastToHastHandle(handle: MdastHandle): HastHandle
26
+ export declare function convertMdastToHastHandle(handle: MdastHandle, convertOptions?: JsConvertOptions | undefined | null): HastHandle
23
27
 
24
28
  /**
25
29
  * Parse markdown source and convert to HAST. Returns an opaque handle.
26
30
  * The arena stays in Rust memory, no buffer is copied to JS.
27
31
  */
28
- export declare function createHastHandle(source: string, features?: JsFeatures | undefined | null): HastHandle
32
+ export declare function createHastHandle(source: string, features?: JsFeatures | undefined | null, convertOptions?: JsConvertOptions | undefined | null): HastHandle
29
33
 
30
34
  /** Parse markdown source into an MDAST arena handle. */
31
35
  export declare function createMdastHandle(source: string, features?: JsFeatures | undefined | null): MdastHandle
32
36
 
33
37
  /** Parse MDX source and convert to HAST. Returns an opaque handle. */
34
- export declare function createMdxHastHandle(source: string, features?: JsFeatures | undefined | null): HastHandle
38
+ export declare function createMdxHastHandle(source: string, features?: JsFeatures | undefined | null, convertOptions?: JsConvertOptions | undefined | null): HastHandle
35
39
 
36
40
  /** Parse MDX source into an MDAST arena handle. */
37
41
  export declare function createMdxMdastHandle(source: string, features?: JsFeatures | undefined | null): MdastHandle
@@ -60,14 +64,37 @@ export declare function getMdastFrontmatter(handle: MdastHandle): JsFrontmatter
60
64
  */
61
65
  export declare function getNodeData(handle: AnyHandle, nodeId: number): string | null
62
66
 
67
+ /**
68
+ * MDAST→HAST conversion options passed from JavaScript.
69
+ *
70
+ * Input-only: `object_to_js = false` because `FunctionRef` only crosses
71
+ * JS → Rust. A `JsConvertOptions` never gets serialized back to JS.
72
+ */
73
+ export interface JsConvertOptions {
74
+ /** `<h2>` label opening the footnotes section. Default: `"Footnotes"`. */
75
+ footnoteLabel?: string
76
+ /** Backref `<a>` content. Default: `"\u{21a9}"` (↩). */
77
+ footnoteBackContent?: string | ((arg0: number, arg1: number) => string)
78
+ /**
79
+ * Backref `aria-label`. The token `{reference}` is replaced with the
80
+ * footnote number (`1`) or `number-K` (`1-2`) for repeated references.
81
+ * Default: `"Back to reference {reference}"`.
82
+ */
83
+ footnoteBackLabel?: string | ((arg0: number, arg1: number) => string)
84
+ }
85
+
63
86
  /** Feature toggles for the Markdown/MDX parser, passed from JavaScript. */
64
87
  export interface JsFeatures {
65
88
  /** GFM: tables, footnotes, strikethrough, task lists. Default: true. */
66
89
  gfm?: boolean
90
+ /** Granular GFM control (overrides `gfm`). */
91
+ gfmOptions?: JsGfmOptions
67
92
  /** Frontmatter: YAML (`--- ... ---`) and TOML (`+++ ... +++`). Default: true. */
68
93
  frontmatter?: boolean
69
94
  /** Math blocks and inline math (`$$ ... $$`, `$ ... $`). Default: true. */
70
95
  math?: boolean
96
+ /** Granular math control (overrides `math`). */
97
+ mathOptions?: JsMathOptions
71
98
  /** Heading attributes (`# text { #id .class }`). Default: true. */
72
99
  headingAttributes?: boolean
73
100
  /** Colon-delimited container directive blocks (`:::`). Default: false. */
@@ -92,6 +119,30 @@ export interface JsFrontmatter {
92
119
  value: string
93
120
  }
94
121
 
122
+ /**
123
+ * Granular GFM toggles, nested under `features.gfm`. The footnote i18n
124
+ * strings (label, back-content, back-label) travel separately via the
125
+ * `JsConvertOptions` argument on conversion entry points; the JS package
126
+ * extracts them from `features.gfm.footnotes` before calling in.
127
+ */
128
+ export interface JsGfmOptions {
129
+ /**
130
+ * Enable GFM footnotes (`[^id]`). Default: true. Set `false` to drop
131
+ * footnote parsing while keeping the rest of the GFM bundle.
132
+ */
133
+ footnotes?: boolean
134
+ }
135
+
136
+ /** Granular math toggles, nested under `features.math`. */
137
+ export interface JsMathOptions {
138
+ /**
139
+ * Treat single-dollar runs (`$ ... $`) as inline math. Default: true.
140
+ * Set `false` to keep single `$` as literal text (prose with currency)
141
+ * while still parsing double-dollar (`$$ ... $$`) display math.
142
+ */
143
+ singleDollarTextMath?: boolean
144
+ }
145
+
95
146
  /** MDX compile options passed from JavaScript. */
96
147
  export interface JsMdxOptions {
97
148
  /**
@@ -189,7 +240,7 @@ export declare function parseEsm(source: string): string | null
189
240
  export declare function parseExpression(source: string): string | null
190
241
 
191
242
  /** Parse Markdown source and return HTML string directly. */
192
- export declare function parseToHtml(source: string, features?: JsFeatures | undefined | null): string
243
+ export declare function parseToHtml(source: string, features?: JsFeatures | undefined | null, convertOptions?: JsConvertOptions | undefined | null): string
193
244
 
194
245
  /** Render a HAST handle's arena to HTML. Does not consume the handle. */
195
246
  export declare function renderHandle(handle: HastHandle): string
package/index.js CHANGED
@@ -81,8 +81,8 @@ function requireNative() {
81
81
  try {
82
82
  const binding = require('@bruits/satteri-android-arm64')
83
83
  const bindingPackageVersion = require('@bruits/satteri-android-arm64/package.json').version
84
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
85
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
84
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
85
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
86
86
  }
87
87
  return binding
88
88
  } catch (e) {
@@ -97,8 +97,8 @@ function requireNative() {
97
97
  try {
98
98
  const binding = require('@bruits/satteri-android-arm-eabi')
99
99
  const bindingPackageVersion = require('@bruits/satteri-android-arm-eabi/package.json').version
100
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
101
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
100
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
101
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
102
102
  }
103
103
  return binding
104
104
  } catch (e) {
@@ -118,8 +118,8 @@ function requireNative() {
118
118
  try {
119
119
  const binding = require('@bruits/satteri-win32-x64-gnu')
120
120
  const bindingPackageVersion = require('@bruits/satteri-win32-x64-gnu/package.json').version
121
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
122
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
121
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
122
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
123
123
  }
124
124
  return binding
125
125
  } catch (e) {
@@ -134,8 +134,8 @@ function requireNative() {
134
134
  try {
135
135
  const binding = require('@bruits/satteri-win32-x64-msvc')
136
136
  const bindingPackageVersion = require('@bruits/satteri-win32-x64-msvc/package.json').version
137
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
138
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
137
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
138
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
139
139
  }
140
140
  return binding
141
141
  } catch (e) {
@@ -151,8 +151,8 @@ function requireNative() {
151
151
  try {
152
152
  const binding = require('@bruits/satteri-win32-ia32-msvc')
153
153
  const bindingPackageVersion = require('@bruits/satteri-win32-ia32-msvc/package.json').version
154
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
155
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
154
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
155
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
156
156
  }
157
157
  return binding
158
158
  } catch (e) {
@@ -167,8 +167,8 @@ function requireNative() {
167
167
  try {
168
168
  const binding = require('@bruits/satteri-win32-arm64-msvc')
169
169
  const bindingPackageVersion = require('@bruits/satteri-win32-arm64-msvc/package.json').version
170
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
171
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
170
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
171
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
172
172
  }
173
173
  return binding
174
174
  } catch (e) {
@@ -186,8 +186,8 @@ function requireNative() {
186
186
  try {
187
187
  const binding = require('@bruits/satteri-darwin-universal')
188
188
  const bindingPackageVersion = require('@bruits/satteri-darwin-universal/package.json').version
189
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
190
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
189
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
190
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
191
191
  }
192
192
  return binding
193
193
  } catch (e) {
@@ -202,8 +202,8 @@ function requireNative() {
202
202
  try {
203
203
  const binding = require('@bruits/satteri-darwin-x64')
204
204
  const bindingPackageVersion = require('@bruits/satteri-darwin-x64/package.json').version
205
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
206
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
205
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
206
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
207
207
  }
208
208
  return binding
209
209
  } catch (e) {
@@ -218,8 +218,8 @@ function requireNative() {
218
218
  try {
219
219
  const binding = require('@bruits/satteri-darwin-arm64')
220
220
  const bindingPackageVersion = require('@bruits/satteri-darwin-arm64/package.json').version
221
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
222
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
221
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
222
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
223
223
  }
224
224
  return binding
225
225
  } catch (e) {
@@ -238,8 +238,8 @@ function requireNative() {
238
238
  try {
239
239
  const binding = require('@bruits/satteri-freebsd-x64')
240
240
  const bindingPackageVersion = require('@bruits/satteri-freebsd-x64/package.json').version
241
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
242
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
241
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
242
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
243
243
  }
244
244
  return binding
245
245
  } catch (e) {
@@ -254,8 +254,8 @@ function requireNative() {
254
254
  try {
255
255
  const binding = require('@bruits/satteri-freebsd-arm64')
256
256
  const bindingPackageVersion = require('@bruits/satteri-freebsd-arm64/package.json').version
257
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
258
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
257
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
258
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
259
259
  }
260
260
  return binding
261
261
  } catch (e) {
@@ -275,8 +275,8 @@ function requireNative() {
275
275
  try {
276
276
  const binding = require('@bruits/satteri-linux-x64-musl')
277
277
  const bindingPackageVersion = require('@bruits/satteri-linux-x64-musl/package.json').version
278
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
279
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
278
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
279
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
280
280
  }
281
281
  return binding
282
282
  } catch (e) {
@@ -291,8 +291,8 @@ function requireNative() {
291
291
  try {
292
292
  const binding = require('@bruits/satteri-linux-x64-gnu')
293
293
  const bindingPackageVersion = require('@bruits/satteri-linux-x64-gnu/package.json').version
294
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
295
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
294
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
295
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
296
296
  }
297
297
  return binding
298
298
  } catch (e) {
@@ -309,8 +309,8 @@ function requireNative() {
309
309
  try {
310
310
  const binding = require('@bruits/satteri-linux-arm64-musl')
311
311
  const bindingPackageVersion = require('@bruits/satteri-linux-arm64-musl/package.json').version
312
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
313
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
312
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
313
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
314
314
  }
315
315
  return binding
316
316
  } catch (e) {
@@ -325,8 +325,8 @@ function requireNative() {
325
325
  try {
326
326
  const binding = require('@bruits/satteri-linux-arm64-gnu')
327
327
  const bindingPackageVersion = require('@bruits/satteri-linux-arm64-gnu/package.json').version
328
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
329
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
328
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
329
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
330
330
  }
331
331
  return binding
332
332
  } catch (e) {
@@ -343,8 +343,8 @@ function requireNative() {
343
343
  try {
344
344
  const binding = require('@bruits/satteri-linux-arm-musleabihf')
345
345
  const bindingPackageVersion = require('@bruits/satteri-linux-arm-musleabihf/package.json').version
346
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
347
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
346
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
347
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
348
348
  }
349
349
  return binding
350
350
  } catch (e) {
@@ -359,8 +359,8 @@ function requireNative() {
359
359
  try {
360
360
  const binding = require('@bruits/satteri-linux-arm-gnueabihf')
361
361
  const bindingPackageVersion = require('@bruits/satteri-linux-arm-gnueabihf/package.json').version
362
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
363
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
362
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
363
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
364
364
  }
365
365
  return binding
366
366
  } catch (e) {
@@ -377,8 +377,8 @@ function requireNative() {
377
377
  try {
378
378
  const binding = require('@bruits/satteri-linux-loong64-musl')
379
379
  const bindingPackageVersion = require('@bruits/satteri-linux-loong64-musl/package.json').version
380
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
381
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
380
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
381
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
382
382
  }
383
383
  return binding
384
384
  } catch (e) {
@@ -393,8 +393,8 @@ function requireNative() {
393
393
  try {
394
394
  const binding = require('@bruits/satteri-linux-loong64-gnu')
395
395
  const bindingPackageVersion = require('@bruits/satteri-linux-loong64-gnu/package.json').version
396
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
397
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
396
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
397
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
398
398
  }
399
399
  return binding
400
400
  } catch (e) {
@@ -411,8 +411,8 @@ function requireNative() {
411
411
  try {
412
412
  const binding = require('@bruits/satteri-linux-riscv64-musl')
413
413
  const bindingPackageVersion = require('@bruits/satteri-linux-riscv64-musl/package.json').version
414
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
415
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
414
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
415
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
416
416
  }
417
417
  return binding
418
418
  } catch (e) {
@@ -427,8 +427,8 @@ function requireNative() {
427
427
  try {
428
428
  const binding = require('@bruits/satteri-linux-riscv64-gnu')
429
429
  const bindingPackageVersion = require('@bruits/satteri-linux-riscv64-gnu/package.json').version
430
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
431
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
430
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
431
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
432
432
  }
433
433
  return binding
434
434
  } catch (e) {
@@ -444,8 +444,8 @@ function requireNative() {
444
444
  try {
445
445
  const binding = require('@bruits/satteri-linux-ppc64-gnu')
446
446
  const bindingPackageVersion = require('@bruits/satteri-linux-ppc64-gnu/package.json').version
447
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
448
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
447
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
448
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
449
449
  }
450
450
  return binding
451
451
  } catch (e) {
@@ -460,8 +460,8 @@ function requireNative() {
460
460
  try {
461
461
  const binding = require('@bruits/satteri-linux-s390x-gnu')
462
462
  const bindingPackageVersion = require('@bruits/satteri-linux-s390x-gnu/package.json').version
463
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
464
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
463
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
464
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
465
465
  }
466
466
  return binding
467
467
  } catch (e) {
@@ -480,8 +480,8 @@ function requireNative() {
480
480
  try {
481
481
  const binding = require('@bruits/satteri-openharmony-arm64')
482
482
  const bindingPackageVersion = require('@bruits/satteri-openharmony-arm64/package.json').version
483
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
484
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
483
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
484
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
485
485
  }
486
486
  return binding
487
487
  } catch (e) {
@@ -496,8 +496,8 @@ function requireNative() {
496
496
  try {
497
497
  const binding = require('@bruits/satteri-openharmony-x64')
498
498
  const bindingPackageVersion = require('@bruits/satteri-openharmony-x64/package.json').version
499
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
500
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
499
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
500
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
501
501
  }
502
502
  return binding
503
503
  } catch (e) {
@@ -512,8 +512,8 @@ function requireNative() {
512
512
  try {
513
513
  const binding = require('@bruits/satteri-openharmony-arm')
514
514
  const bindingPackageVersion = require('@bruits/satteri-openharmony-arm/package.json').version
515
- if (bindingPackageVersion !== '0.6.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
516
- throw new Error(`Native binding package version mismatch, expected 0.6.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
515
+ if (bindingPackageVersion !== '0.6.3' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
516
+ throw new Error(`Native binding package version mismatch, expected 0.6.3 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
517
517
  }
518
518
  return binding
519
519
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "satteri",
3
- "version": "0.6.3",
3
+ "version": "0.7.0",
4
4
  "description": "High-performance Markdown and MDX processing",
5
5
  "repository": {
6
6
  "type": "git",
@@ -80,17 +80,19 @@
80
80
  ]
81
81
  },
82
82
  "optionalDependencies": {
83
- "@bruits/satteri-linux-x64-gnu": "0.6.3",
84
- "@bruits/satteri-darwin-x64": "0.6.3",
85
- "@bruits/satteri-darwin-arm64": "0.6.3",
86
- "@bruits/satteri-win32-x64-msvc": "0.6.3",
87
- "@bruits/satteri-wasm32-wasi": "0.6.3"
83
+ "@bruits/satteri-linux-x64-gnu": "0.7.0",
84
+ "@bruits/satteri-darwin-x64": "0.7.0",
85
+ "@bruits/satteri-darwin-arm64": "0.7.0",
86
+ "@bruits/satteri-win32-x64-msvc": "0.7.0",
87
+ "@bruits/satteri-wasm32-wasi": "0.7.0"
88
88
  },
89
89
  "scripts": {
90
90
  "build:binary:wasm": "napi build --manifest-path ../../crates/satteri-napi-binding/Cargo.toml --output-dir . --platform --release --esm --strip --target wasm32-wasip1-threads",
91
91
  "postbuild:binary:wasm": "node patch-binding.js",
92
92
  "build:binary:native": "napi build --manifest-path ../../crates/satteri-napi-binding/Cargo.toml --output-dir . --platform --release --esm --strip",
93
93
  "postbuild:binary:native": "node patch-binding.js",
94
+ "build:binary:native:lite": "napi build --manifest-path ../../crates/satteri-napi-binding/Cargo.toml --output-dir . --platform --release --esm --strip -- --no-default-features",
95
+ "postbuild:binary:native:lite": "node patch-binding.js",
94
96
  "build:binary:native:cross": "napi build --manifest-path ../../crates/satteri-napi-binding/Cargo.toml --output-dir . --platform --release --esm --strip --cross-compile",
95
97
  "postbuild:binary:native:cross": "node patch-binding.js",
96
98
  "build:binary:native:debug": "napi build --manifest-path ../../crates/satteri-napi-binding/Cargo.toml --output-dir . --platform --esm",