@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
package/docs/PLUGINS.md
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# The Jaren Markdown Plugin Contract
|
|
2
|
+
|
|
3
|
+
**Version 0.1 — Specification**
|
|
4
|
+
|
|
5
|
+
Plugins are the extensibility story of `@jarenjs/md`, and they follow
|
|
6
|
+
the package's one rule: **everything is declared up front and baked into
|
|
7
|
+
compiled closures**. There is no runtime registration, no mutation of a
|
|
8
|
+
live parser, no dynamic lookup on the hot path. A plugin is data; the
|
|
9
|
+
compiler turns it into table entries.
|
|
10
|
+
|
|
11
|
+
## 1. The plugin object
|
|
12
|
+
|
|
13
|
+
A plugin is a plain frozen object created by `definePlugin(spec)`:
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
definePlugin({
|
|
17
|
+
name: 'mermaid', // unique, kebab-case (required)
|
|
18
|
+
fences: ['mermaid'], // fenced-code claims by info-string first word
|
|
19
|
+
blocks: [ /* block rule descriptors, §3 */ ],
|
|
20
|
+
inlines: [ /* inline rule descriptors, §4 */ ],
|
|
21
|
+
node: 'mermaid', // the AST type this plugin emits
|
|
22
|
+
render: (node, h, ctx) => vnode, // pure, synchronous vnode renderer
|
|
23
|
+
toHtml: (node, ctx) => '<figure>…', // pure HTML-string renderer, for toHtml
|
|
24
|
+
hydrate: async (el, node, ctx) => {}, // optional browser-only upgrade
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`definePlugin` validates the shape (name present and kebab-case, rule
|
|
29
|
+
descriptors well-formed, `render` and `toHtml` functions when present)
|
|
30
|
+
and returns `Object.freeze`d data. Every member except `name` is
|
|
31
|
+
optional.
|
|
32
|
+
|
|
33
|
+
Plugins are passed as `options.plugins: MdPlugin[]` to `parseMarkdown`,
|
|
34
|
+
`compileMarkdown` and `loadMarkdown`. At compile time they merge into
|
|
35
|
+
six prebuilt tables:
|
|
36
|
+
|
|
37
|
+
| table | indexed by | consulted |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| fence claims | info-string first word | when a fenced code block closes |
|
|
40
|
+
| block starts | first non-space character | once per unclaimed line |
|
|
41
|
+
| inline scans | trigger character | from the inline scanner's dispatch |
|
|
42
|
+
| renders | AST `type` | by the vnode emitter |
|
|
43
|
+
| html renderers | AST `type` | by the string emitter (`toHtml`) |
|
|
44
|
+
| hydrators | AST `type` | by `createMdRenderer` after mount |
|
|
45
|
+
|
|
46
|
+
All six are built once per compile; the hot loops do only indexed
|
|
47
|
+
lookups. Two plugins claiming the same fence word, block character
|
|
48
|
+
*and* matching the same line, inline character, or node type: the
|
|
49
|
+
**first plugin in the array wins** (deterministic, documented, no
|
|
50
|
+
merging magic).
|
|
51
|
+
|
|
52
|
+
## 2. Fence claims
|
|
53
|
+
|
|
54
|
+
`fences: ['mermaid']` claims fenced code blocks whose info-string first
|
|
55
|
+
word matches. The block is scanned exactly like a code fence (the
|
|
56
|
+
scanner owns fence semantics — nesting, indentation, closing rules);
|
|
57
|
+
only the resulting node changes:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
```mermaid → { type: 'mermaid', value: 'graph TD; …', meta: null }
|
|
61
|
+
graph TD; A-->B
|
|
62
|
+
```
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
A claimed fence MUST produce `{ type: plugin.node, value, meta }` where
|
|
66
|
+
`meta` is the info string after the first word (or `null`). Without the
|
|
67
|
+
plugin, the same source is a plain `code` node with `lang: 'mermaid'` —
|
|
68
|
+
**degradation is always graceful**.
|
|
69
|
+
|
|
70
|
+
## 3. Block rule descriptors
|
|
71
|
+
|
|
72
|
+
For syntax that is not a fence (callout blocks, directives, sidenotes):
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
{
|
|
76
|
+
chars: ':', // trigger characters (first non-space char)
|
|
77
|
+
start: (line, ctx) => node|null, // claim the line: return a fresh node or null
|
|
78
|
+
continue: (node, line, ctx) => boolean, // does this line still belong?
|
|
79
|
+
close: (node, ctx) => void, // finalize (parse buffered text, etc.)
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
- `start` runs only when the line's first non-space character is in
|
|
84
|
+
`chars` and no core construct with higher precedence claimed it. It
|
|
85
|
+
MUST return a new AST node (which the parser appends to the current
|
|
86
|
+
container) or `null` to decline.
|
|
87
|
+
- `continue` runs per subsequent line while the block is open. Return
|
|
88
|
+
`true` to consume the line and stay open, `'end'` to consume the line
|
|
89
|
+
*and* close (a closing fence), or `false` to close without consuming
|
|
90
|
+
— the line then re-processes as a fresh block start.
|
|
91
|
+
- `close` runs once when the block closes (end of input included).
|
|
92
|
+
- `ctx` is `{ frontmatter, options }` — read-only compile context.
|
|
93
|
+
|
|
94
|
+
## 4. Inline rule descriptors
|
|
95
|
+
|
|
96
|
+
```js
|
|
97
|
+
{
|
|
98
|
+
char: '$', // single trigger character
|
|
99
|
+
scan: (src, pos, ctx) => ({ node, end }) | null,
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`scan` is called when the inline scanner meets `char` at `pos` in the
|
|
104
|
+
raw inline text `src`. Return the inline AST node and the exclusive end
|
|
105
|
+
offset, or `null` to decline (the character then flows into plain text).
|
|
106
|
+
`scan` MUST NOT look behind `pos` except to inspect delimiter context
|
|
107
|
+
and MUST NOT allocate on the decline path.
|
|
108
|
+
|
|
109
|
+
## 5. Rendering and hydration
|
|
110
|
+
|
|
111
|
+
`render(node, h, ctx) => vnode` maps the plugin's node type to a
|
|
112
|
+
[view vnode](../../../packages/view/docs/VIEW-FORMAT.md). It MUST be pure and
|
|
113
|
+
synchronous — SSR through `renderToString` and the DOM patcher both
|
|
114
|
+
call it, and its output for equal input SHOULD be reference-equal
|
|
115
|
+
(cache by `node` reference or content hash) so re-patches hit the O(1)
|
|
116
|
+
fast path. `h` is `@jarenjs/view`'s element constructor; `ctx` carries
|
|
117
|
+
`{ options, hash, sanitizeUrl }` where `hash(str)` is the package content
|
|
118
|
+
hash.
|
|
119
|
+
|
|
120
|
+
A plugin `render` **shadows the core emitter for its node type**, so the
|
|
121
|
+
core's URL filtering does not run for it. A plugin that writes an
|
|
122
|
+
`href`/`src` from document content MUST put it through
|
|
123
|
+
`ctx.sanitizeUrl(url)` — the active policy, host override included — and
|
|
124
|
+
drop the attribute when it returns `null` (MD-FORMAT §4.3). A URL the
|
|
125
|
+
plugin composes itself from a trusted constant needs no filtering.
|
|
126
|
+
|
|
127
|
+
### 5.1 Rendering to a string
|
|
128
|
+
|
|
129
|
+
`toHtml(node, ctx) => string` is the same contract for the string
|
|
130
|
+
emitter: pure, synchronous, and shadowing the core emitter for its node
|
|
131
|
+
type — so the URL rule above applies unchanged, and a plugin that writes
|
|
132
|
+
markup MUST escape document content itself (`escapeText`/`escapeAttribute`
|
|
133
|
+
from `@jarenjs/view`). `ctx` is the emission context: `{ options, html,
|
|
134
|
+
sanitizeUrl, headingIds, slugPrefix, headingAnchors }`, where `html` is
|
|
135
|
+
the raw-HTML policy in force.
|
|
136
|
+
|
|
137
|
+
The two hooks are independent. A plugin MAY provide either, and the
|
|
138
|
+
emitters degrade differently when one is missing:
|
|
139
|
+
|
|
140
|
+
- no `toHtml`, and the node type is one the CORE emitter knows (the
|
|
141
|
+
highlight plugin claims `code`): the core emitter prints it — the
|
|
142
|
+
content is never lost, it simply arrives without the plugin's
|
|
143
|
+
decoration;
|
|
144
|
+
- no `toHtml`, and the type is the plugin's own (`mermaid`): `toHtml`
|
|
145
|
+
emits `<!-- unsupported plugin node: <type> -->`. The gap is SHOWN,
|
|
146
|
+
because a silently dropped diagram looks like a document that never
|
|
147
|
+
had one.
|
|
148
|
+
|
|
149
|
+
Anything asynchronous or DOM-dependent goes in
|
|
150
|
+
`hydrate(el, node, ctx)`, which `createMdRenderer` invokes **after**
|
|
151
|
+
the patcher mounts the element. A hydratable render marks its root element with
|
|
152
|
+
`'data-md-hydrate': plugin.name` and `'data-md-hash': contentHash`; the
|
|
153
|
+
renderer finds marked elements, skips those whose hash it already
|
|
154
|
+
hydrated, and calls the plugin. `hydrate` MAY be async; failures are
|
|
155
|
+
contained per element (reported through `options.onHydrateError`,
|
|
156
|
+
default `console.error`).
|
|
157
|
+
|
|
158
|
+
## 6. The reference plugins
|
|
159
|
+
|
|
160
|
+
Both ship from `@jarenjs/md/plugins` and are the canonical templates
|
|
161
|
+
for third-party plugins (math, callouts/admonitions, embeds). GFM
|
|
162
|
+
footnotes are NOT a plugin — they are part of the dialect
|
|
163
|
+
([MD-FORMAT.md](MD-FORMAT.md) §4.6), gated on `gfm` like tables are.
|
|
164
|
+
|
|
165
|
+
### 6.1 mermaidPlugin({ theme })
|
|
166
|
+
|
|
167
|
+
The **native** plugin, re-exported from `@jarenjs/mermaid/plugin`. It
|
|
168
|
+
parses the fence source with the in-house headless Mermaid engine and
|
|
169
|
+
emits **pure-vnode SVG** synchronously — no injected `mermaid` instance,
|
|
170
|
+
no CDN global, no `innerHTML`.
|
|
171
|
+
|
|
172
|
+
- `render` claims `mermaid`/`mmd` fences and returns
|
|
173
|
+
`div.md-mermaid.mermaid-block > svg`, keyed by content hash. Because it
|
|
174
|
+
is pure and synchronous, a Markdown document containing a `mermaid`
|
|
175
|
+
fence renders to a full SVG string through **SSR with no browser**.
|
|
176
|
+
Text and attribute values are escaped by the view serializer, and
|
|
177
|
+
only `http(s)`/relative link `href`s survive, so there is no
|
|
178
|
+
raw-`innerHTML` injection surface.
|
|
179
|
+
- There is **no `hydrate`** — the render is already complete.
|
|
180
|
+
Optional client-only enhancements (pan/zoom) are reserved for a future
|
|
181
|
+
interactivity plugin.
|
|
182
|
+
- The dependency arrow is **md → mermaid**:
|
|
183
|
+
`@jarenjs/mermaid/plugin` returns a self-frozen `MdPlugin`-shaped
|
|
184
|
+
object *without* importing `definePlugin`, so there is no cycle;
|
|
185
|
+
`@jarenjs/md` re-exports it and adds `@jarenjs/mermaid` to its
|
|
186
|
+
dependencies. Consumers who never use it tree-shake it away
|
|
187
|
+
(`sideEffects:false`). Mermaid stays **opt-in** — it is not in
|
|
188
|
+
`DEFAULT_PLUGINS`.
|
|
189
|
+
|
|
190
|
+
**Transformed-diagram round-trip.** There is no per-plugin `toMarkdown`
|
|
191
|
+
hook; a `mermaid` fence round-trips through `toMarkdown` generically as
|
|
192
|
+
long as its source lives in `node.value` (which it does). So a JSLT
|
|
193
|
+
transform that *rewrites* a diagram must refresh the fence source with
|
|
194
|
+
the canonical printer. `@jarenjs/mermaid/plugin` exports the primitive
|
|
195
|
+
for exactly this:
|
|
196
|
+
|
|
197
|
+
```js
|
|
198
|
+
import { parseMermaid, toMermaid } from '@jarenjs/mermaid';
|
|
199
|
+
import { refreshMermaidFence } from '@jarenjs/mermaid/plugin';
|
|
200
|
+
|
|
201
|
+
const doc = parseMermaid(fenceNode.value);
|
|
202
|
+
const edited = transform(doc); // JSLT / hand edit of the AST
|
|
203
|
+
const fresh = refreshMermaidFence(fenceNode, edited); // { type:'mermaid', value: toMermaid(edited) }
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
The refreshed `value` re-emits through the core fence printer
|
|
207
|
+
(`to-md.js`), so `toMarkdown` prints the new diagram.
|
|
208
|
+
|
|
209
|
+
### 6.2 highlightPlugin({ grammars, adapter })
|
|
210
|
+
|
|
211
|
+
Claims nothing — it **renders** `code` nodes, replacing the default
|
|
212
|
+
code renderer through the render table.
|
|
213
|
+
|
|
214
|
+
**Built-in mode**: a zero-dependency single-pass tokenizer driven by
|
|
215
|
+
compact grammar tables (keyword set, string/comment/number delimiters,
|
|
216
|
+
punctuation classes), compiled to closures once per compile. Shipped
|
|
217
|
+
grammars: `js`/`ts` (+`jsx`/`mjs`), `json`, `josl`/`toml`, `html`/`xml`,
|
|
218
|
+
`css`, `md`/`markdown`, `bash`/`sh`/`shell`.
|
|
219
|
+
|
|
220
|
+
**Adapter mode**: `adapter: (code, lang) => Token[] | null` plugs
|
|
221
|
+
shiki/prism/highlight.js behind the same token contract; `null` falls
|
|
222
|
+
back to the built-in grammar for that `lang`, then to plain text. The
|
|
223
|
+
adapter is captured at compile time, never re-registered.
|
|
224
|
+
|
|
225
|
+
**The token contract** (normative): a flat array of
|
|
226
|
+
`{ kind, value }` covering the source exactly, in order. Kinds and
|
|
227
|
+
their CSS classes:
|
|
228
|
+
|
|
229
|
+
| kind | class | meaning |
|
|
230
|
+
|---|---|---|
|
|
231
|
+
| `kw` | `tok-kw` | keyword |
|
|
232
|
+
| `str` | `tok-str` | string literal |
|
|
233
|
+
| `num` | `tok-num` | number literal |
|
|
234
|
+
| `com` | `tok-com` | comment |
|
|
235
|
+
| `pun` | `tok-pun` | punctuation/brackets |
|
|
236
|
+
| `id` | `tok-id` | identifier |
|
|
237
|
+
| `op` | `tok-op` | operator |
|
|
238
|
+
| `lit` | `tok-lit` | language literal (`true`, `null`, …) |
|
|
239
|
+
|
|
240
|
+
Tokens render as `<span class="tok-{kind}">` children of the `<code>`
|
|
241
|
+
element (plain `id` runs render as bare text nodes to keep the vnode
|
|
242
|
+
small); the fence language renders as `class="language-{lang}"` on the
|
|
243
|
+
`code` element, claimed fences aside.
|
|
244
|
+
|
|
245
|
+
## 7. Writing your own (non-normative)
|
|
246
|
+
|
|
247
|
+
A callout plugin in full:
|
|
248
|
+
|
|
249
|
+
```js
|
|
250
|
+
import { definePlugin } from '@jarenjs/md';
|
|
251
|
+
|
|
252
|
+
export const calloutPlugin = () => definePlugin({
|
|
253
|
+
name: 'callout',
|
|
254
|
+
node: 'callout',
|
|
255
|
+
blocks: [{
|
|
256
|
+
chars: ':',
|
|
257
|
+
start: (line) => {
|
|
258
|
+
const m = /^:::\s*(\w+)\s*$/.exec(line);
|
|
259
|
+
return m ? { type: 'callout', kind: m[1], lines: [] } : null;
|
|
260
|
+
},
|
|
261
|
+
continue: (node, line) => {
|
|
262
|
+
if (/^\s*:::\s*$/.test(line)) return 'end';
|
|
263
|
+
node.lines.push(line);
|
|
264
|
+
return true;
|
|
265
|
+
},
|
|
266
|
+
close: (node) => {
|
|
267
|
+
node.value = node.lines.join('\n');
|
|
268
|
+
delete node.lines;
|
|
269
|
+
},
|
|
270
|
+
}],
|
|
271
|
+
render: (node, h) =>
|
|
272
|
+
h('aside', { class: `md-callout md-callout-${node.kind}` }, node.value),
|
|
273
|
+
});
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Everything the core constructs get, a plugin gets: table dispatch, SSR
|
|
277
|
+
purity, graceful degradation, and content-hash keys.
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jarenjs/md",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "0.34.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.js",
|
|
7
|
+
"types": "./dist/types/index.d.ts",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/types/index.d.ts",
|
|
12
|
+
"default": "./src/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./plugins": {
|
|
15
|
+
"types": "./dist/types/plugins/index.d.ts",
|
|
16
|
+
"default": "./src/plugins/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./html": {
|
|
19
|
+
"types": "./dist/types/html.d.ts",
|
|
20
|
+
"default": "./src/html.js"
|
|
21
|
+
},
|
|
22
|
+
"./mdx": {
|
|
23
|
+
"types": "./dist/types/mdx.d.ts",
|
|
24
|
+
"default": "./src/mdx.js"
|
|
25
|
+
},
|
|
26
|
+
"./directives": {
|
|
27
|
+
"types": "./dist/types/directives.d.ts",
|
|
28
|
+
"default": "./src/directives.js"
|
|
29
|
+
},
|
|
30
|
+
"./component": {
|
|
31
|
+
"types": "./dist/types/component/index.d.ts",
|
|
32
|
+
"default": "./src/component/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./styles/md.css": "./styles/md.css",
|
|
35
|
+
"./schemas/*": "./schemas/*",
|
|
36
|
+
"./package.json": "./package.json"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist/types/",
|
|
40
|
+
"src/",
|
|
41
|
+
"docs/",
|
|
42
|
+
"schemas/",
|
|
43
|
+
"styles/"
|
|
44
|
+
],
|
|
45
|
+
"description": "Markdown + frontmatter as JSON documents: a compile-to-closures parser whose AST feeds the Jaren query, JSLT, view and forms engines natively",
|
|
46
|
+
"author": "joham",
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "git+https://github.com/jklarenbeek/jarenjs.git",
|
|
50
|
+
"directory": "components/md"
|
|
51
|
+
},
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=24"
|
|
55
|
+
},
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public",
|
|
58
|
+
"registry": "https://registry.npmjs.org/"
|
|
59
|
+
},
|
|
60
|
+
"keywords": [
|
|
61
|
+
"jaren",
|
|
62
|
+
"json",
|
|
63
|
+
"markdown",
|
|
64
|
+
"commonmark",
|
|
65
|
+
"gfm",
|
|
66
|
+
"frontmatter",
|
|
67
|
+
"ast",
|
|
68
|
+
"parser"
|
|
69
|
+
],
|
|
70
|
+
"scripts": {
|
|
71
|
+
"build": "npm run build:types",
|
|
72
|
+
"build:types": "tsc -p tsconfig.json",
|
|
73
|
+
"prepack": "npm run build:types"
|
|
74
|
+
},
|
|
75
|
+
"dependencies": {
|
|
76
|
+
"@jarenjs/core": "^0.34.0",
|
|
77
|
+
"@jarenjs/mermaid": "^0.34.0",
|
|
78
|
+
"@jarenjs/view": "^0.34.0"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://jarenjs.dev/schemas/jaren-md-ast/0.1",
|
|
4
|
+
"title": "Jaren Markdown document",
|
|
5
|
+
"description": "The MdDocument envelope and AST node vocabulary of @jarenjs/md (normative prose in docs/MD-FORMAT.md). Core node types validate strictly; unknown types pass as extension nodes so compiled-in plugins can extend the vocabulary.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["$md", "frontmatter", "ast", "meta"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"$md": { "const": "0.1" },
|
|
10
|
+
"frontmatter": true,
|
|
11
|
+
"ast": {
|
|
12
|
+
"type": "array",
|
|
13
|
+
"items": { "$ref": "#/$defs/blockNode" }
|
|
14
|
+
},
|
|
15
|
+
"meta": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"required": ["sourceUrl", "hash"],
|
|
18
|
+
"properties": {
|
|
19
|
+
"sourceUrl": { "type": ["string", "null"] },
|
|
20
|
+
"hash": { "type": "string" },
|
|
21
|
+
"frontmatterLang": { "enum": ["yaml", "json", "toml", null] }
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"$defs": {
|
|
26
|
+
"blockNode": {
|
|
27
|
+
"anyOf": [
|
|
28
|
+
{ "$ref": "#/$defs/paragraph" },
|
|
29
|
+
{ "$ref": "#/$defs/heading" },
|
|
30
|
+
{ "$ref": "#/$defs/thematicBreak" },
|
|
31
|
+
{ "$ref": "#/$defs/blockquote" },
|
|
32
|
+
{ "$ref": "#/$defs/list" },
|
|
33
|
+
{ "$ref": "#/$defs/code" },
|
|
34
|
+
{ "$ref": "#/$defs/html" },
|
|
35
|
+
{ "$ref": "#/$defs/table" },
|
|
36
|
+
{ "$ref": "#/$defs/footnoteDefinition" },
|
|
37
|
+
{ "$ref": "#/$defs/custom" },
|
|
38
|
+
{ "$ref": "#/$defs/extensionNode" }
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"inlineNode": {
|
|
42
|
+
"anyOf": [
|
|
43
|
+
{ "$ref": "#/$defs/text" },
|
|
44
|
+
{ "$ref": "#/$defs/emphasis" },
|
|
45
|
+
{ "$ref": "#/$defs/strong" },
|
|
46
|
+
{ "$ref": "#/$defs/strikethrough" },
|
|
47
|
+
{ "$ref": "#/$defs/link" },
|
|
48
|
+
{ "$ref": "#/$defs/image" },
|
|
49
|
+
{ "$ref": "#/$defs/inlineCode" },
|
|
50
|
+
{ "$ref": "#/$defs/break" },
|
|
51
|
+
{ "$ref": "#/$defs/softBreak" },
|
|
52
|
+
{ "$ref": "#/$defs/html" },
|
|
53
|
+
{ "$ref": "#/$defs/footnoteReference" },
|
|
54
|
+
{ "$ref": "#/$defs/custom" },
|
|
55
|
+
{ "$ref": "#/$defs/extensionNode" }
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
"inlineChildren": {
|
|
59
|
+
"type": "array",
|
|
60
|
+
"items": { "$ref": "#/$defs/inlineNode" }
|
|
61
|
+
},
|
|
62
|
+
"blockChildren": {
|
|
63
|
+
"type": "array",
|
|
64
|
+
"items": { "$ref": "#/$defs/blockNode" }
|
|
65
|
+
},
|
|
66
|
+
"paragraph": {
|
|
67
|
+
"type": "object",
|
|
68
|
+
"required": ["type", "children"],
|
|
69
|
+
"properties": {
|
|
70
|
+
"type": { "const": "paragraph" },
|
|
71
|
+
"children": { "$ref": "#/$defs/inlineChildren" }
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"heading": {
|
|
75
|
+
"type": "object",
|
|
76
|
+
"required": ["type", "depth", "children"],
|
|
77
|
+
"properties": {
|
|
78
|
+
"type": { "const": "heading" },
|
|
79
|
+
"depth": { "type": "integer", "minimum": 1, "maximum": 6 },
|
|
80
|
+
"children": { "$ref": "#/$defs/inlineChildren" }
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"thematicBreak": {
|
|
84
|
+
"type": "object",
|
|
85
|
+
"required": ["type"],
|
|
86
|
+
"properties": { "type": { "const": "thematicBreak" } }
|
|
87
|
+
},
|
|
88
|
+
"blockquote": {
|
|
89
|
+
"type": "object",
|
|
90
|
+
"required": ["type", "children"],
|
|
91
|
+
"properties": {
|
|
92
|
+
"type": { "const": "blockquote" },
|
|
93
|
+
"children": { "$ref": "#/$defs/blockChildren" }
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"list": {
|
|
97
|
+
"type": "object",
|
|
98
|
+
"required": ["type", "ordered", "start", "tight", "children"],
|
|
99
|
+
"properties": {
|
|
100
|
+
"type": { "const": "list" },
|
|
101
|
+
"ordered": { "type": "boolean" },
|
|
102
|
+
"start": { "type": ["integer", "null"] },
|
|
103
|
+
"tight": { "type": "boolean" },
|
|
104
|
+
"children": {
|
|
105
|
+
"type": "array",
|
|
106
|
+
"items": { "$ref": "#/$defs/listItem" }
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
"listItem": {
|
|
111
|
+
"type": "object",
|
|
112
|
+
"required": ["type", "checked", "children"],
|
|
113
|
+
"properties": {
|
|
114
|
+
"type": { "const": "listItem" },
|
|
115
|
+
"checked": { "type": ["boolean", "null"] },
|
|
116
|
+
"children": { "$ref": "#/$defs/blockChildren" }
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
"code": {
|
|
120
|
+
"type": "object",
|
|
121
|
+
"required": ["type", "lang", "meta", "value"],
|
|
122
|
+
"properties": {
|
|
123
|
+
"type": { "const": "code" },
|
|
124
|
+
"lang": { "type": ["string", "null"] },
|
|
125
|
+
"meta": { "type": ["string", "null"] },
|
|
126
|
+
"value": { "type": "string" }
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
"html": {
|
|
130
|
+
"type": "object",
|
|
131
|
+
"required": ["type", "value"],
|
|
132
|
+
"properties": {
|
|
133
|
+
"type": { "const": "html" },
|
|
134
|
+
"value": { "type": "string" }
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
"table": {
|
|
138
|
+
"type": "object",
|
|
139
|
+
"required": ["type", "align", "children"],
|
|
140
|
+
"properties": {
|
|
141
|
+
"type": { "const": "table" },
|
|
142
|
+
"align": {
|
|
143
|
+
"type": "array",
|
|
144
|
+
"items": { "enum": ["left", "right", "center", null] }
|
|
145
|
+
},
|
|
146
|
+
"children": {
|
|
147
|
+
"type": "array",
|
|
148
|
+
"items": { "$ref": "#/$defs/tableRow" }
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
"tableRow": {
|
|
153
|
+
"type": "object",
|
|
154
|
+
"required": ["type", "children"],
|
|
155
|
+
"properties": {
|
|
156
|
+
"type": { "const": "tableRow" },
|
|
157
|
+
"children": {
|
|
158
|
+
"type": "array",
|
|
159
|
+
"items": { "$ref": "#/$defs/tableCell" }
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
"tableCell": {
|
|
164
|
+
"type": "object",
|
|
165
|
+
"required": ["type", "children"],
|
|
166
|
+
"properties": {
|
|
167
|
+
"type": { "const": "tableCell" },
|
|
168
|
+
"children": { "$ref": "#/$defs/inlineChildren" }
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
"text": {
|
|
172
|
+
"type": "object",
|
|
173
|
+
"required": ["type", "value"],
|
|
174
|
+
"properties": {
|
|
175
|
+
"type": { "const": "text" },
|
|
176
|
+
"value": { "type": "string" }
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
"emphasis": {
|
|
180
|
+
"type": "object",
|
|
181
|
+
"required": ["type", "children"],
|
|
182
|
+
"properties": {
|
|
183
|
+
"type": { "const": "emphasis" },
|
|
184
|
+
"children": { "$ref": "#/$defs/inlineChildren" }
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
"strong": {
|
|
188
|
+
"type": "object",
|
|
189
|
+
"required": ["type", "children"],
|
|
190
|
+
"properties": {
|
|
191
|
+
"type": { "const": "strong" },
|
|
192
|
+
"children": { "$ref": "#/$defs/inlineChildren" }
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
"strikethrough": {
|
|
196
|
+
"type": "object",
|
|
197
|
+
"required": ["type", "children"],
|
|
198
|
+
"properties": {
|
|
199
|
+
"type": { "const": "strikethrough" },
|
|
200
|
+
"children": { "$ref": "#/$defs/inlineChildren" }
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
"link": {
|
|
204
|
+
"type": "object",
|
|
205
|
+
"required": ["type", "url", "title", "children"],
|
|
206
|
+
"properties": {
|
|
207
|
+
"type": { "const": "link" },
|
|
208
|
+
"url": { "type": "string" },
|
|
209
|
+
"title": { "type": ["string", "null"] },
|
|
210
|
+
"auto": {
|
|
211
|
+
"type": "boolean",
|
|
212
|
+
"description": "present and true on a GFM literal autolink (bare www./http:///email); the canonical printer prints such a link back bare, and nothing else reads it"
|
|
213
|
+
},
|
|
214
|
+
"children": { "$ref": "#/$defs/inlineChildren" }
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
"footnoteDefinition": {
|
|
218
|
+
"type": "object",
|
|
219
|
+
"required": ["type", "identifier", "label", "children"],
|
|
220
|
+
"properties": {
|
|
221
|
+
"type": { "const": "footnoteDefinition" },
|
|
222
|
+
"identifier": { "type": "string" },
|
|
223
|
+
"label": { "type": "string" },
|
|
224
|
+
"children": { "$ref": "#/$defs/blockChildren" }
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
"footnoteReference": {
|
|
228
|
+
"type": "object",
|
|
229
|
+
"required": ["type", "identifier", "label"],
|
|
230
|
+
"properties": {
|
|
231
|
+
"type": { "const": "footnoteReference" },
|
|
232
|
+
"identifier": { "type": "string" },
|
|
233
|
+
"label": { "type": "string" }
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
"image": {
|
|
237
|
+
"type": "object",
|
|
238
|
+
"required": ["type", "url", "title", "alt"],
|
|
239
|
+
"properties": {
|
|
240
|
+
"type": { "const": "image" },
|
|
241
|
+
"url": { "type": "string" },
|
|
242
|
+
"title": { "type": ["string", "null"] },
|
|
243
|
+
"alt": { "type": "string" }
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
"inlineCode": {
|
|
247
|
+
"type": "object",
|
|
248
|
+
"required": ["type", "value"],
|
|
249
|
+
"properties": {
|
|
250
|
+
"type": { "const": "inlineCode" },
|
|
251
|
+
"value": { "type": "string" }
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
"break": {
|
|
255
|
+
"type": "object",
|
|
256
|
+
"required": ["type"],
|
|
257
|
+
"properties": { "type": { "const": "break" } }
|
|
258
|
+
},
|
|
259
|
+
"softBreak": {
|
|
260
|
+
"type": "object",
|
|
261
|
+
"required": ["type"],
|
|
262
|
+
"properties": { "type": { "const": "softBreak" } }
|
|
263
|
+
},
|
|
264
|
+
"custom": {
|
|
265
|
+
"type": "object",
|
|
266
|
+
"required": ["type", "name", "data"],
|
|
267
|
+
"properties": {
|
|
268
|
+
"type": { "const": "custom" },
|
|
269
|
+
"name": { "type": "string" },
|
|
270
|
+
"data": true,
|
|
271
|
+
"children": {
|
|
272
|
+
"type": "array",
|
|
273
|
+
"items": { "$ref": "#/$defs/blockNode" }
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
"extensionNode": {
|
|
278
|
+
"type": "object",
|
|
279
|
+
"required": ["type"],
|
|
280
|
+
"properties": {
|
|
281
|
+
"type": {
|
|
282
|
+
"type": "string",
|
|
283
|
+
"not": {
|
|
284
|
+
"enum": [
|
|
285
|
+
"paragraph", "heading", "thematicBreak", "blockquote",
|
|
286
|
+
"list", "listItem", "code", "html", "table", "tableRow",
|
|
287
|
+
"tableCell", "text", "emphasis", "strong", "strikethrough",
|
|
288
|
+
"link", "image", "inlineCode", "break", "softBreak", "custom",
|
|
289
|
+
"footnoteDefinition", "footnoteReference"
|
|
290
|
+
]
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|