+10
-2
@@ -133,2 +133,10 @@ import type { MdastPluginInput, HastPluginInput } from "./plugin.js"; | ||
| fileURL?: URL; | ||
| /** | ||
| * Initial document-level data bag, seeding `ctx.data` before any plugin runs. | ||
| * It is the same object plugins mutate and the caller reads back as | ||
| * `result.data`, so values flow both into and out of a compile. Defaults to a | ||
| * fresh empty object. Used by reference and mutated in place, so pass a | ||
| * throwaway object per compile rather than a shared one. | ||
| */ | ||
| data?: Data; | ||
| } | ||
@@ -204,3 +212,3 @@ /** | ||
| frontmatter: Frontmatter | null; | ||
| /** Document-level data bag populated by plugins via `ctx.data`. Empty `{}` if untouched. */ | ||
| /** Document-level data bag shared with plugins via `ctx.data`; the seeded `data` option if provided, else a fresh `{}`. */ | ||
| data: Data; | ||
@@ -214,3 +222,3 @@ } | ||
| frontmatter: Frontmatter | null; | ||
| /** Document-level data bag populated by plugins via `ctx.data`. Empty `{}` if untouched. */ | ||
| /** Document-level data bag shared with plugins via `ctx.data`; the seeded `data` option if provided, else a fresh `{}`. */ | ||
| data: Data; | ||
@@ -217,0 +225,0 @@ } |
+28
-26
@@ -78,7 +78,8 @@ import { visitHastHandle, resolveSubscriptions } from "./hast/hast-visitor.js"; | ||
| } | ||
| /** Drop a handle after bumping its epoch: the arena dies with the handle, so a | ||
| * child stub retained past the pipeline must hit the designed retention error, | ||
| * not a cryptic RangeError from snapshotting the freed arena. */ | ||
| function disposeHandle(handle) { | ||
| markHandleMutated(handle); | ||
| /** Free a handle's arena. A plugin's visitor can hand child stubs to user code; | ||
| * pass `invalidateStubs` so the epoch bump makes any stub retained past this | ||
| * point hit the designed retention error instead of snapshotting the freed arena. */ | ||
| function releaseHandle(handle, invalidateStubs) { | ||
| if (invalidateStubs) | ||
| markHandleMutated(handle); | ||
| dropHandle(handle); | ||
@@ -194,5 +195,5 @@ } | ||
| export function markdownToHtml(source, options = {}) { | ||
| const { mdastPlugins = [], hastPlugins = [], features, fileURL } = options; | ||
| const { mdastPlugins = [], hastPlugins = [], features, fileURL, data = {} } = options; | ||
| const hastMayHaveStubs = hastPlugins.length > 0; | ||
| const { features: nativeFeatures, convertOptions: nativeConvertOptions } = featuresToNative(features); | ||
| const data = {}; | ||
| const result = createHastHandleFromMdast(source, mdastPlugins, false, fileURL, nativeFeatures, nativeConvertOptions, data); | ||
@@ -205,3 +206,3 @@ const renderAndDrop = (h, frontmatter) => { | ||
| finally { | ||
| disposeHandle(h); | ||
| releaseHandle(h, hastMayHaveStubs); | ||
| } | ||
@@ -215,3 +216,3 @@ }; | ||
| catch (err) { | ||
| disposeHandle(r.hastHandle); | ||
| releaseHandle(r.hastHandle, hastMayHaveStubs); | ||
| throw err; | ||
@@ -221,3 +222,3 @@ } | ||
| return hastResult.then(() => renderAndDrop(r.hastHandle, r.frontmatter), (err) => { | ||
| disposeHandle(r.hastHandle); | ||
| releaseHandle(r.hastHandle, hastMayHaveStubs); | ||
| throw err; | ||
@@ -233,6 +234,6 @@ }); | ||
| export function mdxToJs(source, options = {}) { | ||
| const { mdastPlugins = [], hastPlugins = [], features, fileURL, ...mdxFields } = options; | ||
| const { mdastPlugins = [], hastPlugins = [], features, fileURL, data = {}, ...mdxFields } = options; | ||
| const hastMayHaveStubs = hastPlugins.length > 0; | ||
| const mdxOptions = mdxOptionsToNative(mdxFields); | ||
| const { features: nativeFeatures, convertOptions: nativeConvertOptions } = featuresToNative(features); | ||
| const data = {}; | ||
| const result = createHastHandleFromMdast(source, mdastPlugins, true, fileURL, nativeFeatures, nativeConvertOptions, data); | ||
@@ -245,3 +246,3 @@ const compileAndDrop = (h, frontmatter) => { | ||
| finally { | ||
| disposeHandle(h); | ||
| releaseHandle(h, hastMayHaveStubs); | ||
| } | ||
@@ -255,3 +256,3 @@ }; | ||
| catch (err) { | ||
| disposeHandle(r.hastHandle); | ||
| releaseHandle(r.hastHandle, hastMayHaveStubs); | ||
| throw err; | ||
@@ -261,3 +262,3 @@ } | ||
| return hastResult.then(() => compileAndDrop(r.hastHandle, r.frontmatter), (err) => { | ||
| disposeHandle(r.hastHandle); | ||
| releaseHandle(r.hastHandle, hastMayHaveStubs); | ||
| throw err; | ||
@@ -307,3 +308,4 @@ }); | ||
| : createMdastHandle(source, nativeFeatures); | ||
| // finally{dispose} is intentional: convertMdastToHastHandle empties the arena | ||
| const mdastMayHaveStubs = mdastPlugins.length > 0; | ||
| // finally{release} is intentional: convertMdastToHastHandle empties the arena | ||
| // on success, but if any step here throws the handle would otherwise leak. | ||
@@ -313,5 +315,5 @@ const finalize = (r) => { | ||
| const frontmatter = readFrontmatter(r.handle); | ||
| // The conversion empties the mdast arena; bump the epoch so a stub | ||
| // retained past the mdast pass fails with the retention error. | ||
| markHandleMutated(r.handle); | ||
| // convert empties the mdast arena, so invalidate any stub held past this point. | ||
| if (mdastMayHaveStubs) | ||
| markHandleMutated(r.handle); | ||
| const hastHandle = convertMdastToHastHandle(r.handle, nativeConvertOptions); | ||
@@ -321,3 +323,3 @@ return { hastHandle, frontmatter }; | ||
| finally { | ||
| disposeHandle(r.handle); | ||
| releaseHandle(r.handle, mdastMayHaveStubs); | ||
| } | ||
@@ -332,3 +334,3 @@ }; | ||
| return mdastResult.then(finalize, (err) => { | ||
| disposeHandle(mdastHandle); | ||
| releaseHandle(mdastHandle, mdastMayHaveStubs); | ||
| throw err; | ||
@@ -340,3 +342,3 @@ }); | ||
| catch (err) { | ||
| disposeHandle(mdastHandle); | ||
| releaseHandle(mdastHandle, mdastMayHaveStubs); | ||
| throw err; | ||
@@ -353,3 +355,3 @@ } | ||
| finally { | ||
| disposeHandle(handle); | ||
| releaseHandle(handle, true); | ||
| } | ||
@@ -364,3 +366,3 @@ } | ||
| finally { | ||
| disposeHandle(handle); | ||
| releaseHandle(handle, true); | ||
| } | ||
@@ -376,3 +378,3 @@ } | ||
| finally { | ||
| disposeHandle(handle); | ||
| releaseHandle(handle, true); | ||
| } | ||
@@ -388,4 +390,4 @@ } | ||
| finally { | ||
| disposeHandle(handle); | ||
| releaseHandle(handle, true); | ||
| } | ||
| } |
+6
-6
| { | ||
| "name": "satteri", | ||
| "version": "0.9.0", | ||
| "version": "0.9.1", | ||
| "description": "High-performance Markdown and MDX processing", | ||
@@ -83,7 +83,7 @@ "repository": { | ||
| "optionalDependencies": { | ||
| "@bruits/satteri-linux-x64-gnu": "0.9.0", | ||
| "@bruits/satteri-darwin-x64": "0.9.0", | ||
| "@bruits/satteri-darwin-arm64": "0.9.0", | ||
| "@bruits/satteri-win32-x64-msvc": "0.9.0", | ||
| "@bruits/satteri-wasm32-wasi": "0.9.0" | ||
| "@bruits/satteri-linux-x64-gnu": "0.9.1", | ||
| "@bruits/satteri-darwin-x64": "0.9.1", | ||
| "@bruits/satteri-darwin-arm64": "0.9.1", | ||
| "@bruits/satteri-win32-x64-msvc": "0.9.1", | ||
| "@bruits/satteri-wasm32-wasi": "0.9.1" | ||
| }, | ||
@@ -90,0 +90,0 @@ "scripts": { |
+21
-2
@@ -185,7 +185,23 @@ # satteri | ||
| 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. | ||
| 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. | ||
| By default keys are typed as `unknown`. To give a key a type, augment the `DataMap` interface, much like `vfile`'s `DataMap`: | ||
| 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. | ||
| ```ts | ||
| const data = { title: "Hello" }; | ||
| const rewriteTitle = defineMdastPlugin({ | ||
| name: "rewrite-title", | ||
| heading(_node, ctx) { | ||
| ctx.data.title = "Updated"; | ||
| }, | ||
| }); | ||
| const result = markdownToHtml("# A", { mdastPlugins: [rewriteTitle], data }); | ||
| console.log(result.data === data); // true, the seeded object is returned | ||
| console.log(data.title); // "Updated" | ||
| ``` | ||
| By default keys are typed as `unknown`. To give a key a type, augment the `DataMap` interface: | ||
| ```ts | ||
| declare module "satteri" { | ||
@@ -338,2 +354,3 @@ interface DataMap { | ||
| fileURL?: URL; | ||
| data?: Data; | ||
| } | ||
@@ -349,2 +366,4 @@ | ||
| `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). | ||
| `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. | ||
@@ -351,0 +370,0 @@ |
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 3 instances
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 2 instances
306903
0.55%6892
0.15%371
5.4%82
1.23%