@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.
- package/README.md +520 -0
- package/dist/types/ast.d.ts +181 -0
- package/dist/types/bake.d.ts +61 -0
- package/dist/types/compiler.d.ts +141 -0
- package/dist/types/component/index.d.ts +101 -0
- package/dist/types/directives.d.ts +126 -0
- package/dist/types/entities.d.ts +40 -0
- package/dist/types/footnotes.d.ts +83 -0
- package/dist/types/frontmatter.d.ts +67 -0
- package/dist/types/html.d.ts +72 -0
- package/dist/types/index.d.ts +30 -0
- package/dist/types/loader.d.ts +84 -0
- package/dist/types/mdx.d.ts +45 -0
- package/dist/types/parser.d.ts +116 -0
- package/dist/types/plugins/highlight.d.ts +64 -0
- package/dist/types/plugins/index.d.ts +64 -0
- package/dist/types/plugins/mermaid.d.ts +12 -0
- package/dist/types/scanner.d.ts +240 -0
- package/dist/types/to-html.d.ts +104 -0
- package/dist/types/to-md.d.ts +23 -0
- package/dist/types/to-vnode.d.ts +161 -0
- package/dist/types/utils.d.ts +63 -0
- package/docs/LOADER.md +92 -0
- package/docs/MD-FORMAT.md +502 -0
- package/docs/PLUGINS.md +277 -0
- package/package.json +80 -0
- package/schemas/jaren-md-ast.schema.json +296 -0
- package/src/ast.js +346 -0
- package/src/bake.js +104 -0
- package/src/compiler.js +167 -0
- package/src/component/index.js +191 -0
- package/src/directives.js +371 -0
- package/src/entities.js +107 -0
- package/src/footnotes.js +180 -0
- package/src/frontmatter.js +947 -0
- package/src/html.js +281 -0
- package/src/index.js +76 -0
- package/src/loader.js +0 -0
- package/src/mdx.js +219 -0
- package/src/parser.js +1685 -0
- package/src/plugins/highlight.js +325 -0
- package/src/plugins/index.js +75 -0
- package/src/plugins/mermaid.js +14 -0
- package/src/scanner.js +832 -0
- package/src/to-html.js +425 -0
- package/src/to-md.js +396 -0
- package/src/to-vnode.js +766 -0
- package/src/utils.js +107 -0
- 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;
|