@jarenjs/md 0.34.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.
Files changed (49) hide show
  1. package/README.md +520 -0
  2. package/dist/types/ast.d.ts +181 -0
  3. package/dist/types/bake.d.ts +61 -0
  4. package/dist/types/compiler.d.ts +141 -0
  5. package/dist/types/component/index.d.ts +101 -0
  6. package/dist/types/directives.d.ts +126 -0
  7. package/dist/types/entities.d.ts +40 -0
  8. package/dist/types/footnotes.d.ts +83 -0
  9. package/dist/types/frontmatter.d.ts +67 -0
  10. package/dist/types/html.d.ts +72 -0
  11. package/dist/types/index.d.ts +30 -0
  12. package/dist/types/loader.d.ts +84 -0
  13. package/dist/types/mdx.d.ts +45 -0
  14. package/dist/types/parser.d.ts +116 -0
  15. package/dist/types/plugins/highlight.d.ts +64 -0
  16. package/dist/types/plugins/index.d.ts +64 -0
  17. package/dist/types/plugins/mermaid.d.ts +12 -0
  18. package/dist/types/scanner.d.ts +240 -0
  19. package/dist/types/to-html.d.ts +104 -0
  20. package/dist/types/to-md.d.ts +23 -0
  21. package/dist/types/to-vnode.d.ts +161 -0
  22. package/dist/types/utils.d.ts +63 -0
  23. package/docs/LOADER.md +92 -0
  24. package/docs/MD-FORMAT.md +502 -0
  25. package/docs/PLUGINS.md +277 -0
  26. package/package.json +80 -0
  27. package/schemas/jaren-md-ast.schema.json +296 -0
  28. package/src/ast.js +346 -0
  29. package/src/bake.js +104 -0
  30. package/src/compiler.js +167 -0
  31. package/src/component/index.js +191 -0
  32. package/src/directives.js +371 -0
  33. package/src/entities.js +107 -0
  34. package/src/footnotes.js +180 -0
  35. package/src/frontmatter.js +947 -0
  36. package/src/html.js +281 -0
  37. package/src/index.js +76 -0
  38. package/src/loader.js +0 -0
  39. package/src/mdx.js +219 -0
  40. package/src/parser.js +1685 -0
  41. package/src/plugins/highlight.js +325 -0
  42. package/src/plugins/index.js +75 -0
  43. package/src/plugins/mermaid.js +14 -0
  44. package/src/scanner.js +832 -0
  45. package/src/to-html.js +425 -0
  46. package/src/to-md.js +396 -0
  47. package/src/to-vnode.js +766 -0
  48. package/src/utils.js +107 -0
  49. package/styles/md.css +238 -0
@@ -0,0 +1,181 @@
1
+ /**
2
+ * @file The Markdown AST vocabulary: node constructors, shape guards
3
+ * and compiled walkers.
4
+ *
5
+ * Every node is a plain JSON object with a `type` discriminator;
6
+ * container nodes hold ordered content in `children`, literal nodes in
7
+ * `value` (normative vocabulary in docs/MD-FORMAT.md §4). The
8
+ * constructors exist so every node of a type is born with the same
9
+ * hidden class — the walkers and render tables then stay monomorphic.
10
+ */
11
+ /** The document format version this package produces. */
12
+ export declare const MD_VERSION = "0.1";
13
+ export type MdNode = {
14
+ type: string;
15
+ [member: string]: any;
16
+ };
17
+ export type MdDocument = {
18
+ $md: string;
19
+ frontmatter: any;
20
+ ast: MdNode[];
21
+ meta: {
22
+ sourceUrl: string | null;
23
+ hash: string;
24
+ frontmatterLang: 'yaml' | 'json' | 'toml' | null;
25
+ };
26
+ };
27
+ /**
28
+ * @typedef {{ type: string, [member: string]: any }} MdNode
29
+ */
30
+ /**
31
+ * @typedef {{ $md: string, frontmatter: any, ast: MdNode[],
32
+ * meta: { sourceUrl: string|null, hash: string,
33
+ * frontmatterLang: 'yaml'|'json'|'toml'|null } }} MdDocument
34
+ */
35
+ /**
36
+ * Does this node hold its content in `children`? Plugin/custom nodes
37
+ * count when they actually carry a children array.
38
+ * @param {MdNode} node
39
+ * @returns {boolean}
40
+ */
41
+ export declare function isContainerNode(node: MdNode): boolean;
42
+ /** @param {MdNode[]} children @returns {MdNode} */
43
+ export declare function paragraph(children: MdNode[]): MdNode;
44
+ /** @param {number} depth @param {MdNode[]} children @returns {MdNode} */
45
+ export declare function heading(depth: number, children: MdNode[]): MdNode;
46
+ /** @returns {MdNode} */
47
+ export declare function thematicBreak(): MdNode;
48
+ /** @param {MdNode[]} children @returns {MdNode} */
49
+ export declare function blockquote(children: MdNode[]): MdNode;
50
+ /**
51
+ * @param {boolean} ordered
52
+ * @param {number|null} start
53
+ * @param {boolean} tight
54
+ * @param {MdNode[]} children
55
+ * @returns {MdNode}
56
+ */
57
+ export declare function list(ordered: boolean, start: number | null, tight: boolean, children: MdNode[]): MdNode;
58
+ /** @param {boolean|null} checked @param {MdNode[]} children @returns {MdNode} */
59
+ export declare function listItem(checked: boolean | null, children: MdNode[]): MdNode;
60
+ /**
61
+ * @param {string|null} lang
62
+ * @param {string|null} meta
63
+ * @param {string} value
64
+ * @returns {MdNode}
65
+ */
66
+ export declare function code(lang: string | null, meta: string | null, value: string): MdNode;
67
+ /** @param {string} value @returns {MdNode} */
68
+ export declare function htmlBlock(value: string): MdNode;
69
+ /**
70
+ * @param {(string|null)[]} align
71
+ * @param {MdNode[]} children
72
+ * @returns {MdNode}
73
+ */
74
+ export declare function table(align: (string | null)[], children: MdNode[]): MdNode;
75
+ /** @param {MdNode[]} children @returns {MdNode} */
76
+ export declare function tableRow(children: MdNode[]): MdNode;
77
+ /** @param {MdNode[]} children @returns {MdNode} */
78
+ export declare function tableCell(children: MdNode[]): MdNode;
79
+ /** @param {string} value @returns {MdNode} */
80
+ export declare function text(value: string): MdNode;
81
+ /** @param {MdNode[]} children @returns {MdNode} */
82
+ export declare function emphasis(children: MdNode[]): MdNode;
83
+ /** @param {MdNode[]} children @returns {MdNode} */
84
+ export declare function strong(children: MdNode[]): MdNode;
85
+ /** @param {MdNode[]} children @returns {MdNode} */
86
+ export declare function strikethrough(children: MdNode[]): MdNode;
87
+ /**
88
+ * @param {string} url
89
+ * @param {string|null} title
90
+ * @param {MdNode[]} children
91
+ * @returns {MdNode}
92
+ */
93
+ export declare function link(url: string, title: string | null, children: MdNode[]): MdNode;
94
+ /**
95
+ * A GFM literal autolink (`www.example.com`, `a@b.test`): a `link`, not
96
+ * a type of its own — every consumer, plugin and schema would otherwise
97
+ * have to learn a second spelling of the same thing. The `auto` flag is
98
+ * carried for the ONE consumer that has to tell them apart, the
99
+ * canonical printer, which prints it back bare (MD-FORMAT.md §4.7).
100
+ * @param {string} url the resolved destination (scheme inserted)
101
+ * @param {string} literal the text as the author wrote it
102
+ * @returns {MdNode}
103
+ */
104
+ export declare function autolink(url: string, literal: string): MdNode;
105
+ /**
106
+ * A GFM footnote definition: block content collected out of the flow
107
+ * and rendered once, at the end, if something cites it.
108
+ * @param {string} identifier the normalized label (matching key)
109
+ * @param {string} label the label as written
110
+ * @param {MdNode[]} children
111
+ * @returns {MdNode}
112
+ */
113
+ export declare function footnoteDefinition(identifier: string, label: string, children: MdNode[]): MdNode;
114
+ /**
115
+ * A GFM footnote reference: the citation mark in the text.
116
+ * @param {string} identifier the normalized label (matching key)
117
+ * @param {string} label the label as written
118
+ * @returns {MdNode}
119
+ */
120
+ export declare function footnoteReference(identifier: string, label: string): MdNode;
121
+ /**
122
+ * @param {string} url
123
+ * @param {string|null} title
124
+ * @param {string} alt
125
+ * @returns {MdNode}
126
+ */
127
+ export declare function image(url: string, title: string | null, alt: string): MdNode;
128
+ /** @param {string} value @returns {MdNode} */
129
+ export declare function inlineCode(value: string): MdNode;
130
+ /** @returns {MdNode} */
131
+ export declare function hardBreak(): MdNode;
132
+ /** @returns {MdNode} */
133
+ export declare function softBreak(): MdNode;
134
+ /**
135
+ * The generic escape hatch for constructs without a compiled-in plugin
136
+ * vocabulary (MD-FORMAT §4.4).
137
+ * @param {string} name
138
+ * @param {any} data
139
+ * @param {MdNode[]} [children]
140
+ * @returns {MdNode}
141
+ */
142
+ export declare function custom(name: string, data: any, children?: MdNode[]): MdNode;
143
+ /**
144
+ * Walk an AST (a node or an array of nodes) in document order, calling
145
+ * `visitor(node, parent, index)` pre-order. Returning `false` from the
146
+ * visitor skips the node's children.
147
+ *
148
+ * @param {MdNode | MdNode[]} root
149
+ * @param {(node: MdNode, parent: MdNode|null, index: number) => (boolean|void)} visitor
150
+ */
151
+ export declare function walkAst(root: MdNode | MdNode[], visitor: (node: MdNode, parent: MdNode | null, index: number) => (boolean | void)): void;
152
+ export type MdVisitFn = (node: MdNode, parent: MdNode | null, index: number) => (boolean | void);
153
+ export type MdVisitSpec = MdVisitFn | {
154
+ enter?: MdVisitFn;
155
+ exit?: MdVisitFn;
156
+ };
157
+ /**
158
+ * @typedef {(node: MdNode, parent: MdNode|null, index: number) => (boolean|void)} MdVisitFn
159
+ * @typedef {MdVisitFn | { enter?: MdVisitFn, exit?: MdVisitFn }} MdVisitSpec
160
+ */
161
+ /**
162
+ * Compile a per-type visitor spec into a dispatch table and walk with
163
+ * it. Handlers are keyed by node type, `'*'` matches every type; a
164
+ * handler is a function (pre-order) or `{ enter, exit }`. An `enter`
165
+ * returning `false` skips the children (exit still runs).
166
+ *
167
+ * The table is built once per call — pass the same spec object to reuse
168
+ * the compiled form across documents (a `WeakMap` memo keeps this
169
+ * allocation-free on repeat visits).
170
+ *
171
+ * @param {MdNode | MdNode[]} root
172
+ * @param {Record<string, MdVisitSpec>} visitors
173
+ */
174
+ export declare function visitAst(root: MdNode | MdNode[], visitors: Record<string, MdVisitSpec>): void;
175
+ /**
176
+ * The plain-text content of a node subtree (alt-text derivation,
177
+ * heading slugs, search indexing).
178
+ * @param {MdNode | MdNode[]} root
179
+ * @returns {string}
180
+ */
181
+ export declare function textOf(root: MdNode | MdNode[]): string;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * @file `bake` — write a derived value back into the source text.
3
+ *
4
+ * A directive carries a value a machine derives and a human reads
5
+ * (directives.js). Baking materializes the current value INTO the
6
+ * committed file, which is the whole point: the document needs no
7
+ * runtime, GitHub renders it correctly, and the next re-derivation shows
8
+ * up as a diff a reviewer can look at instead of a number that quietly
9
+ * stopped being true.
10
+ *
11
+ * Two properties make it usable on documents people wrote by hand:
12
+ *
13
+ * - **byte-local.** Only the spans between markers change. `toMarkdown`
14
+ * is a canonicalizing printer, so re-printing a README would reflow
15
+ * every list and re-wrap every table — correct markdown, and a diff
16
+ * nobody can review. So `bake` splices the SOURCE, and a document
17
+ * with no directives comes back the same bytes it went in as.
18
+ * - **idempotent.** Baking an already-baked document changes nothing,
19
+ * which is what lets a `--check` mode be "bake and compare".
20
+ *
21
+ * The trust level is different from mdx's, and the difference is the
22
+ * point: an mdx interpolation lands in a TEXT node and is never re-read
23
+ * as markdown, because it may carry untrusted data. A baked body is
24
+ * spliced into the source and WILL be re-parsed as markdown — a fact
25
+ * that is a whole table is exactly the use case. `bake` is therefore a
26
+ * build-time tool for input you control, and it says so here rather than
27
+ * leaving the two to be confused.
28
+ */
29
+ export type BakeResult = {
30
+ text: string;
31
+ changed: boolean;
32
+ diagnostics: string[];
33
+ applied: {
34
+ key: string;
35
+ from: string;
36
+ to: string;
37
+ }[];
38
+ };
39
+ /**
40
+ * @typedef {{ text: string, changed: boolean, diagnostics: string[],
41
+ * applied: { key: string, from: string, to: string }[] }} BakeResult
42
+ */
43
+ /**
44
+ * Replace every directive body in `source` with a freshly resolved
45
+ * value.
46
+ *
47
+ * `resolve(key, directive)` returns the text to place between the
48
+ * markers, or `undefined` to leave that directive untouched. Throwing is
49
+ * allowed and is reported as a diagnostic — one bad derivation must not
50
+ * cost the rest of the document.
51
+ *
52
+ * @param {string} source the document text
53
+ * @param {{ ns: string, resolve: (key: string, directive: any) => (string|undefined),
54
+ * parseOptions?: any }} options
55
+ * @returns {BakeResult}
56
+ */
57
+ export declare function bake(source: string, options: {
58
+ ns: string;
59
+ resolve: (key: string, directive: any) => (string | undefined);
60
+ parseOptions?: any;
61
+ }): BakeResult;
@@ -0,0 +1,141 @@
1
+ /**
2
+ * @file compileMarkdown: parse once, then hand out specialized
3
+ * closures.
4
+ *
5
+ * A CompiledMd is the package's unit of work: the parsed document plus
6
+ * lazily-built, cached projections (vnode tree, canonical Markdown,
7
+ * frontmatter externals). Every projection is computed at most once
8
+ * per compiled document — calling `toVnode()` twice returns the same
9
+ * reference, which is what the view patcher's `===` fast path wants.
10
+ */
11
+ export type MdNode = import('./ast.js').MdNode;
12
+ export type MdDocument = import('./ast.js').MdDocument;
13
+ export type MdParseOptions = import('./parser.js').MdParseOptions;
14
+ export type MdCompileOptions = MdParseOptions & {
15
+ retainSource?: boolean;
16
+ html?: 'skip' | 'text';
17
+ headingIds?: boolean;
18
+ slugPrefix?: string;
19
+ headingAnchors?: boolean;
20
+ footnotesLabel?: string;
21
+ keyed?: boolean;
22
+ };
23
+ export type CompiledMd = {
24
+ /**
25
+ * the parsed document
26
+ */
27
+ doc: MdDocument;
28
+ /**
29
+ * `doc.ast`
30
+ */
31
+ ast: MdNode[];
32
+ /**
33
+ * `doc.frontmatter`
34
+ */
35
+ frontmatter: any;
36
+ /**
37
+ * `doc.meta.hash`
38
+ */
39
+ hash: string;
40
+ /**
41
+ * the source text (null when `retainSource: false`)
42
+ */
43
+ source: string | null;
44
+ /**
45
+ * the compiled plugin tables (internal contract)
46
+ */
47
+ tables: any;
48
+ /**
49
+ * cached vnode projection
50
+ */
51
+ toVnode: () => any;
52
+ /**
53
+ * cached canonical Markdown
54
+ */
55
+ toMarkdown: () => string;
56
+ /**
57
+ * pre-order walker over the AST
58
+ */
59
+ walk: (visitor: any) => void;
60
+ /**
61
+ * compiled per-type visitor
62
+ */
63
+ visit: (visitors: any) => void;
64
+ /**
65
+ * frontmatter as JSLT/query externals
66
+ */
67
+ externals: () => Record<string, any>;
68
+ };
69
+ /**
70
+ * @typedef {import('./ast.js').MdNode} MdNode
71
+ * @typedef {import('./ast.js').MdDocument} MdDocument
72
+ * @typedef {import('./parser.js').MdParseOptions} MdParseOptions
73
+ */
74
+ /**
75
+ * @typedef {MdParseOptions & { retainSource?: boolean,
76
+ * html?: 'skip'|'text', headingIds?: boolean, slugPrefix?: string,
77
+ * headingAnchors?: boolean, footnotesLabel?: string,
78
+ * keyed?: boolean }} MdCompileOptions
79
+ */
80
+ /**
81
+ * The compiled closure bundle.
82
+ * @typedef {object} CompiledMd
83
+ * @property {MdDocument} doc the parsed document
84
+ * @property {MdNode[]} ast `doc.ast`
85
+ * @property {any} frontmatter `doc.frontmatter`
86
+ * @property {string} hash `doc.meta.hash`
87
+ * @property {string|null} source the source text (null when `retainSource: false`)
88
+ * @property {any} tables the compiled plugin tables (internal contract)
89
+ * @property {() => any} toVnode cached vnode projection
90
+ * @property {() => string} toMarkdown cached canonical Markdown
91
+ * @property {(visitor: any) => void} walk pre-order walker over the AST
92
+ * @property {(visitors: any) => void} visit compiled per-type visitor
93
+ * @property {() => Record<string, any>} externals frontmatter as JSLT/query externals
94
+ */
95
+ /**
96
+ * Compile Markdown source (or an already-parsed MdDocument) into a
97
+ * closure bundle.
98
+ *
99
+ * @example
100
+ * const md = compileMarkdown('# Hi\n\nSome *text*.');
101
+ * md.toVnode(); // ['article', { class: 'md' }, ...]
102
+ * md.toMarkdown(); // '# Hi\n\nSome *text*.\n'
103
+ * md.externals(); // {} — no frontmatter
104
+ *
105
+ * @param {string | MdDocument} sourceOrDoc
106
+ * @param {MdCompileOptions} [options]
107
+ * @returns {CompiledMd}
108
+ */
109
+ export declare function compileMarkdown(sourceOrDoc: string | MdDocument, options?: MdCompileOptions): CompiledMd;
110
+ /**
111
+ * Flatten frontmatter into an externals object for the query/JSLT
112
+ * engines (MD-FORMAT.md §3.4). Only a frontmatter *object* contributes
113
+ * members; the engine-reserved names `root` and `path` are dropped.
114
+ * @param {any} frontmatter
115
+ * @returns {Record<string, any>}
116
+ */
117
+ export declare function frontmatterExternals(frontmatter: any): Record<string, any>;
118
+ /**
119
+ * Emit a `@jarenjs/forms`-consumable structure from a document whose
120
+ * frontmatter declares a schema (`$schema` object member or `form:`
121
+ * key). The caller injects the forms module (or the two functions it
122
+ * needs) — `@jarenjs/md` stays dependency-free:
123
+ *
124
+ * @example
125
+ * import * as forms from '@jarenjs/forms';
126
+ * const form = mdToForm(md.doc, forms);
127
+ * // { schema, fields, data } or null when no schema is declared
128
+ *
129
+ * @param {MdDocument} doc
130
+ * @param {{ buildFormModel: (schema: any) => any,
131
+ * createInitialData: (schema: any) => any }} forms
132
+ * @returns {{ schema: any, fields: any, data: any } | null}
133
+ */
134
+ export declare function mdToForm(doc: MdDocument, forms: {
135
+ buildFormModel: (schema: any) => any;
136
+ createInitialData: (schema: any) => any;
137
+ }): {
138
+ schema: any;
139
+ fields: any;
140
+ data: any;
141
+ } | null;
@@ -0,0 +1,101 @@
1
+ /**
2
+ * @file The Markdown VISUAL COMPONENT — part two of the package.
3
+ *
4
+ * Everything below this line is presentation glue; the engine
5
+ * (`@jarenjs/md`) neither knows nor needs any of it. The component
6
+ * layer packages the engine for hosts that render:
7
+ *
8
+ * - `createMdComponent()` — a batteries-included bundle: a memoized
9
+ * `view()` projection for `@jarenjs/app` viewModels (reference-
10
+ * stable, so unchanged sources patch in O(1)), `effects` entries
11
+ * for the app effect registry (`md-load`, `md-parse`), and a
12
+ * `hydrate()` pass for app-managed DOM;
13
+ * - `styles/md.css` — the component's stylesheet (`.md` content
14
+ * rhythm, `tok-*` token colors, mermaid placeholder), light/dark.
15
+ *
16
+ * The boundary is deliberate: the engine stays a headless data
17
+ * toolchain (text ↔ AST ↔ vnode values), the component owns defaults,
18
+ * memoization policy, CSS and app-registry shapes. See
19
+ * ARCHITECTURE.md §"Engine and component".
20
+ */
21
+ export type MdDocument = import('../ast.js').MdDocument;
22
+ export type CompiledMd = import('../compiler.js').CompiledMd;
23
+ export type MdCompileOptions = import('../compiler.js').MdCompileOptions;
24
+ export type MdComponentOptions = MdCompileOptions & {
25
+ base?: string | URL;
26
+ fetch?: typeof globalThis.fetch;
27
+ cache?: any;
28
+ memoLimit?: number;
29
+ onHydrateError?: (err: any) => void;
30
+ };
31
+ export type MdComponent = {
32
+ /**
33
+ * the compiled-in plugin set
34
+ */
35
+ plugins: any[];
36
+ /**
37
+ * memoized vnode projection
38
+ */
39
+ view: (sourceOrDoc: any) => any;
40
+ /**
41
+ * memoized compile
42
+ */
43
+ compile: (source: string) => CompiledMd;
44
+ /**
45
+ * `md-load` and `md-parse` for `createApp({ effects })`
46
+ */
47
+ effects: Record<string, (props: any, dispatch: any) => any>;
48
+ /**
49
+ * run plugin hydrate hooks
50
+ * over already-mounted DOM (no-op without hydratable plugins)
51
+ */
52
+ hydrate: (container: any) => void;
53
+ };
54
+ /**
55
+ * @typedef {import('../ast.js').MdDocument} MdDocument
56
+ * @typedef {import('../compiler.js').CompiledMd} CompiledMd
57
+ * @typedef {import('../compiler.js').MdCompileOptions} MdCompileOptions
58
+ */
59
+ /**
60
+ * @typedef {MdCompileOptions & {
61
+ * base?: string | URL,
62
+ * fetch?: typeof globalThis.fetch,
63
+ * cache?: any,
64
+ * memoLimit?: number,
65
+ * onHydrateError?: (err: any) => void,
66
+ * }} MdComponentOptions
67
+ */
68
+ /**
69
+ * The component bundle.
70
+ * @typedef {object} MdComponent
71
+ * @property {any[]} plugins the compiled-in plugin set
72
+ * @property {(sourceOrDoc: any) => any} view memoized vnode projection
73
+ * @property {(source: string) => CompiledMd} compile memoized compile
74
+ * @property {Record<string, (props: any, dispatch: any) => any>} effects
75
+ * `md-load` and `md-parse` for `createApp({ effects })`
76
+ * @property {(container: any) => void} hydrate run plugin hydrate hooks
77
+ * over already-mounted DOM (no-op without hydratable plugins)
78
+ */
79
+ /**
80
+ * Create the Markdown component: one object that plugs the engine
81
+ * into an `@jarenjs/app` document (or any view-owning host).
82
+ *
83
+ * @example
84
+ * const md = createMdComponent();
85
+ * createApp(appDoc, {
86
+ * effects: { ...md.effects },
87
+ * viewModel: (state) => ({ ...state, article: md.view(state.articleSource) }),
88
+ * });
89
+ * // an action loads a document:
90
+ * // { "effects": [{ "run": "md-load", "with": { "url": "$.url", "done": "article/loaded" } }] }
91
+ *
92
+ * @param {MdComponentOptions} [options]
93
+ * @returns {MdComponent}
94
+ */
95
+ export declare function createMdComponent(options?: MdComponentOptions): MdComponent;
96
+ /**
97
+ * The component's default plugin set: syntax highlighting on. A single
98
+ * shared array so `buildPluginTables` (and every memo hanging off it)
99
+ * compiles exactly once per process.
100
+ */
101
+ export declare const DEFAULT_PLUGINS: import("../plugins/index.js").MdPlugin[];
@@ -0,0 +1,126 @@
1
+ /**
2
+ * @file Directives: comment-carried data that a machine derives and a
3
+ * human reads.
4
+ *
5
+ * ```markdown
6
+ * Jaren is <!--bm:jsonpath.ctsRatio-->23.1<!--/bm-->x faster on the CTS mean.
7
+ * ```
8
+ *
9
+ * Every markdown renderer on earth drops HTML comments, so GitHub, an
10
+ * editor preview and npm all show `Jaren is 23.1x faster` — plain,
11
+ * correct, static text with no runtime and no template syntax leaking
12
+ * into the prose. A directive-aware consumer reads the marker instead
13
+ * and can re-derive the value; `bake` writes the fresh value back into
14
+ * the source, so a re-derivation is a reviewable diff rather than a
15
+ * silent drift.
16
+ *
17
+ * That is the gap this closes for mdx, whose `{$.path}` spelling renders
18
+ * as literal gibberish anywhere the transform has not run — usable only
19
+ * in documents nobody reads raw.
20
+ *
21
+ * **The layer never interprets the payload.** `bm` puts a fact key
22
+ * there, `mdx` puts a query expression; the vocabulary belongs to the
23
+ * consumer, and this module owns exactly one thing — the marker grammar
24
+ * and the pairing — so two consumers cannot disagree about what a
25
+ * directive is.
26
+ *
27
+ * There are two ways in, because there are two questions:
28
+ *
29
+ * - `scanDirectives(doc)` / `replaceDirectives(doc, …)` work on the
30
+ * AST, which is what a *rendering* consumer has;
31
+ * - `scanSourceDirectives(text)` works on the source, which is what a
32
+ * *rewriting* consumer needs. The AST carries no source offsets (it
33
+ * is plain JSON built for structural sharing, MD-FORMAT §1.1/§6), and
34
+ * giving it any would change every node's shape and therefore every
35
+ * content-hash key — so `bake` splices bytes instead, and both
36
+ * scanners read the same grammar from the same function.
37
+ */
38
+ export type MdNode = import('./ast.js').MdNode;
39
+ export type AstDirective = {
40
+ ns: string;
41
+ key: string;
42
+ scope: 'block' | 'inline';
43
+ path: number[];
44
+ open: number;
45
+ close: number;
46
+ nodes: MdNode[];
47
+ };
48
+ export type SourceDirective = {
49
+ ns: string;
50
+ key: string;
51
+ scope: 'block' | 'inline';
52
+ start: number;
53
+ end: number;
54
+ bodyStart: number;
55
+ bodyEnd: number;
56
+ body: string;
57
+ };
58
+ /**
59
+ * Classify one comment. The single source of truth for what a directive
60
+ * marker IS — both scanners and every consumer go through here.
61
+ * @param {string} text the comment, `<!--` and `-->` included
62
+ * @returns {{ ns: string, key: string, closing: boolean } | null}
63
+ */
64
+ export declare function parseMarker(text: string): {
65
+ ns: string;
66
+ key: string;
67
+ closing: boolean;
68
+ } | null;
69
+ /**
70
+ * Find every directive in a parsed document.
71
+ *
72
+ * A block directive's markers are `html` block nodes with body blocks
73
+ * between them; an inline directive's are `html` inline nodes inside one
74
+ * paragraph. Markers are paired within ONE container — an opener in a
75
+ * blockquote and a closer outside it are two unpaired markers, not one
76
+ * directive spanning a boundary that does not exist in the tree.
77
+ *
78
+ * @param {any} docOrAst an MdDocument, a CompiledMd or an AST array
79
+ * @param {{ ns?: string }} [options] restrict to one namespace
80
+ * @returns {{ directives: AstDirective[], diagnostics: string[] }}
81
+ */
82
+ export declare function scanDirectives(docOrAst: any, options?: {
83
+ ns?: string;
84
+ }): {
85
+ directives: AstDirective[];
86
+ diagnostics: string[];
87
+ };
88
+ /**
89
+ * Replace every directive body, returning a NEW document.
90
+ *
91
+ * The transform is pure and preserves reference equality for everything
92
+ * it does not touch, so the vnode emitter's per-node memo and the view
93
+ * patcher's `===` fast path are unaffected — the same contract the mdx
94
+ * pass keeps (MD-FORMAT §6).
95
+ *
96
+ * `replace` returns the nodes to put between the markers. Returning
97
+ * `undefined` leaves the directive alone.
98
+ *
99
+ * @param {any} docOrAst
100
+ * @param {{ ns?: string }} options
101
+ * @param {(directive: AstDirective) => (MdNode[]|undefined)} replace
102
+ * @returns {any} the same shape that came in (document or array)
103
+ */
104
+ export declare function replaceDirectives(docOrAst: any, options: {
105
+ ns?: string;
106
+ }, replace: (directive: AstDirective) => (MdNode[] | undefined)): any;
107
+ /**
108
+ * Find every directive in SOURCE TEXT, with the offsets a rewriter
109
+ * needs.
110
+ *
111
+ * Comments inside fenced code are skipped: a fence showing a directive
112
+ * as an EXAMPLE is documentation about the layer, not an instance of it,
113
+ * and rewriting one would corrupt the very docs that explain it.
114
+ * (Indented code is not skipped — see the package README's note; the
115
+ * cross-check in `bake` is what catches the difference.)
116
+ *
117
+ * @param {string} source
118
+ * @param {{ ns?: string }} [options]
119
+ * @returns {{ directives: SourceDirective[], diagnostics: string[] }}
120
+ */
121
+ export declare function scanSourceDirectives(source: string, options?: {
122
+ ns?: string;
123
+ }): {
124
+ directives: SourceDirective[];
125
+ diagnostics: string[];
126
+ };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @file The HTML5 named character references, as CommonMark requires
3
+ * them (its "entity and numeric character references" section defers to
4
+ * the WHATWG HTML standard's table, all 2125 of them — a
5
+ * pragmatic subset is a dialect, not CommonMark).
6
+ *
7
+ * GENERATED by `scripts/generate-md-entities.js`; do not edit by hand.
8
+ *
9
+ * The table is packed as one string and split on first use rather than
10
+ * written as 2125 object properties: the packed form is a
11
+ * quarter of the source bytes and one allocation, and a document with no
12
+ * entity in it never pays for the Map at all.
13
+ */
14
+ /**
15
+ * The named character reference table, built on first use.
16
+ * @returns {Map<string, string>}
17
+ */
18
+ export declare function namedEntities(): Map<string, string>;
19
+ /**
20
+ * Decode one character reference at `pos` (`&...;`). Returns null when it
21
+ * is not a valid reference — an `&` that starts nothing is ordinary text,
22
+ * which is why this reports rather than throws.
23
+ * @param {string} src
24
+ * @param {number} pos
25
+ * @returns {{ value: string, end: number } | null}
26
+ */
27
+ export declare function scanEntity(src: string, pos: number): {
28
+ value: string;
29
+ end: number;
30
+ } | null;
31
+ /**
32
+ * Resolve backslash escapes and character references in a run of text.
33
+ *
34
+ * CommonMark processes both in the three places that are NOT inline
35
+ * content — link destinations, link titles and fence info strings — so
36
+ * they share this one pass rather than each re-deriving it.
37
+ * @param {string} text
38
+ * @returns {string}
39
+ */
40
+ export declare function decodeReferences(text: string): string;