@tabnas/markdown 0.6.1 → 0.7.1
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/dist/block.d.ts +257 -0
- package/dist/block.js +213 -68
- package/dist/block.js.map +1 -1
- package/dist/engine-block.d.ts +69 -0
- package/dist/engine-block.js +99 -0
- package/dist/engine-block.js.map +1 -0
- package/dist/engine-inline.d.ts +25 -0
- package/dist/engine-inline.js +237 -0
- package/dist/engine-inline.js.map +1 -0
- package/dist/inline.d.ts +250 -0
- package/dist/inline.js +395 -215
- package/dist/inline.js.map +1 -1
- package/dist/markdown.d.ts +2 -3
- package/dist/markdown.js +91 -63
- package/dist/markdown.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/src/block.ts +224 -75
- package/src/engine-block.ts +151 -0
- package/src/engine-inline.ts +284 -0
- package/src/inline.ts +422 -218
- package/src/markdown.ts +95 -65
package/src/markdown.ts
CHANGED
|
@@ -17,8 +17,10 @@
|
|
|
17
17
|
// they are imported as such. Without `import type`, Node's type stripping
|
|
18
18
|
// keeps them in the emitted import and demands runtime exports that do not
|
|
19
19
|
// exist.
|
|
20
|
-
import type { Tabnas, Plugin
|
|
20
|
+
import type { Tabnas, Plugin } from '@tabnas/parser'
|
|
21
21
|
|
|
22
|
+
import { makeMdActions, makeMdLineMatcher } from './engine-block.ts'
|
|
23
|
+
import { makeInlineTn, parseInlinesEngine } from './engine-inline.ts'
|
|
22
24
|
import { parse } from './commonmark.ts'
|
|
23
25
|
import { toAst } from './ast.ts'
|
|
24
26
|
import { renderHTML } from './html.ts'
|
|
@@ -40,49 +42,16 @@ export type MarkdownOptions = {
|
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
// ---------------------------------------------------------------------------
|
|
43
|
-
// Grammar
|
|
45
|
+
// Grammar.
|
|
44
46
|
//
|
|
45
|
-
// The
|
|
46
|
-
// the
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
# Markdown entry rule — CommonMark 0.31.2
|
|
54
|
-
#
|
|
55
|
-
# This grammar is deliberately trivial, and it is NOT where the parser
|
|
56
|
-
# lives. Block structure is decided by the two-phase line algorithm of
|
|
57
|
-
# spec Appendix A, implemented in block.ts / block.go, and inline
|
|
58
|
-
# structure by the delimiter and bracket stacks in inline.ts / inline.go.
|
|
59
|
-
# None of that is expressible as declarative alts, which is why none of
|
|
60
|
-
# it is here.
|
|
61
|
-
#
|
|
62
|
-
# The markdown rule exists only to give the engine an entry point and to
|
|
63
|
-
# consume the token stream so its trailing-content check passes. The bo
|
|
64
|
-
# action reads the whole source via ctx.src() and hands it to the parser.
|
|
65
|
-
# Everything the rule does is in those two lines below.
|
|
66
|
-
#
|
|
67
|
-
# Consequences worth knowing rather than discovering:
|
|
68
|
-
# - The railroad diagram generated from this file is empty: a bare
|
|
69
|
-
# track with no boxes on it. That is accurate, not a rendering fault.
|
|
70
|
-
# - Editing this file changes documentation, not behaviour. The one
|
|
71
|
-
# exception is the open/close alts, which really are the rule.
|
|
72
|
-
#
|
|
73
|
-
# The file is kept because ts/embed-grammar.js embeds it verbatim into
|
|
74
|
-
# both runtimes as grammarText (see AGENTS.md). It may not contain
|
|
75
|
-
# backticks: the Go copy is a raw string literal.
|
|
76
|
-
{
|
|
77
|
-
rule: markdown: open: [
|
|
78
|
-
{ s: '#ZZ' }
|
|
79
|
-
]
|
|
80
|
-
rule: markdown: close: [
|
|
81
|
-
{ s: '#ZZ' }
|
|
82
|
-
]
|
|
83
|
-
}
|
|
84
|
-
`
|
|
85
|
-
// --- END EMBEDDED markdown-grammar.jsonic ---
|
|
47
|
+
// The grammar is LIVE and lives in code: the `markdown`/`line` rules below,
|
|
48
|
+
// the `mdLine` matcher (engine-block.ts) and the inline instance's rule and
|
|
49
|
+
// matcher set (engine-inline.ts). `debug.model()` serializes it and
|
|
50
|
+
// `ts/tools/gen-railroad.mjs` draws ts/doc/grammar.{svg,txt} from a live
|
|
51
|
+
// instance, so the documentation is derived from the rules the engine
|
|
52
|
+
// actually parses with. The historical `markdown-grammar.jsonic` file and
|
|
53
|
+
// its embed step are gone: they documented an inert single-rule bypass that
|
|
54
|
+
// no longer exists (dx-report §47).
|
|
86
55
|
|
|
87
56
|
// ---------------------------------------------------------------------------
|
|
88
57
|
// Public parse API (engine-free)
|
|
@@ -114,44 +83,105 @@ export function parseTree(src: string, opts?: MarkdownOptions | ParserOptions):
|
|
|
114
83
|
// ---------------------------------------------------------------------------
|
|
115
84
|
// Plugin wiring
|
|
116
85
|
//
|
|
117
|
-
// The
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
86
|
+
// The engine parse IS the parse: the `mdLine` custom matcher (engine-block.ts)
|
|
87
|
+
// lexes one `#LB` token per physical line, `parse.prepare` seeds a fresh
|
|
88
|
+
// `BlockParser` on `ctx.u.md`, the `line` rule feeds each matched line to the
|
|
89
|
+
// shared `incorporateLine`, and the `markdown` rule's close action on `#ZZ`
|
|
90
|
+
// finalizes, runs the shared inline phase, and projects the public AST into
|
|
91
|
+
// the rule's node. No recognition or block decision happens here — the
|
|
92
|
+
// engine owns tokenization, dispatch and state carriage; the algorithm stays
|
|
93
|
+
// in the engine-free core (the anti-drift rule, dx-report §42), which is
|
|
94
|
+
// what keeps this path byte-identical to `parseDocument` (asserted by the
|
|
95
|
+
// differential gate and the engine-path conformance harness).
|
|
122
96
|
|
|
123
97
|
const Markdown: Plugin = (tn: Tabnas, options?: MarkdownOptions) => {
|
|
124
98
|
const opts = resolveOptions(options)
|
|
125
99
|
|
|
100
|
+
// Human descriptions for the block token, surfaced in railroad diagram
|
|
101
|
+
// legends (read off the live config by @tabnas/railroad).
|
|
102
|
+
tn.options({
|
|
103
|
+
config: {
|
|
104
|
+
modify: {
|
|
105
|
+
'md-tokendesc': (cfg: any) => {
|
|
106
|
+
cfg.tokenDesc = Object.assign(cfg.tokenDesc || {}, {
|
|
107
|
+
'#LB': 'one physical line (text, blank and table-arming bits in use)',
|
|
108
|
+
})
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
// The inline phase's own engine instance — nested parsing, one instance
|
|
115
|
+
// per plugin install, reused across every parse and every leaf block (see
|
|
116
|
+
// engine-inline.ts).
|
|
117
|
+
const inlineTn = makeInlineTn(opts)
|
|
118
|
+
const actions = makeMdActions(opts, (doc, refmap) =>
|
|
119
|
+
parseInlinesEngine(inlineTn, doc, refmap, opts),
|
|
120
|
+
)
|
|
121
|
+
|
|
126
122
|
tn.options({ rule: { start: 'markdown' } })
|
|
127
123
|
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
//
|
|
124
|
+
// Every built-in matcher is off — deliberate configuration, not defense.
|
|
125
|
+
// Markdown's block alphabet is *lines*, so the one registered matcher is
|
|
126
|
+
// the complete lexical description of this phase: `mdLine`, at an order
|
|
127
|
+
// ahead of every built-in, consuming each physical line whole. (The
|
|
128
|
+
// built-ins would misread the syntax anyway: backtick code spans lex as
|
|
129
|
+
// unterminated strings, `# heading` as a comment, `1. list` as a number.)
|
|
132
130
|
tn.options({
|
|
131
|
+
space: { lex: false },
|
|
132
|
+
line: { lex: false },
|
|
133
133
|
string: { lex: false },
|
|
134
134
|
comment: { lex: false },
|
|
135
135
|
number: { lex: false },
|
|
136
136
|
value: { lex: false },
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
'@markdown-bo': (r: Rule, ctx: Context) => {
|
|
142
|
-
const src: string = ctx.src()
|
|
143
|
-
r.node = parseDocument(src, opts)
|
|
137
|
+
text: { lex: false },
|
|
138
|
+
lex: {
|
|
139
|
+
emptyResult: { type: 'document', children: [] },
|
|
140
|
+
match: { mdLine: { order: 1e5, make: makeMdLineMatcher } },
|
|
144
141
|
},
|
|
145
|
-
|
|
142
|
+
parse: { prepare: { 'md-reset': actions.prepare } },
|
|
143
|
+
})
|
|
146
144
|
|
|
147
145
|
tn.rule('markdown', (rs) => {
|
|
148
146
|
return rs
|
|
149
147
|
.clear()
|
|
150
|
-
.
|
|
151
|
-
.
|
|
152
|
-
|
|
148
|
+
.open([{ p: 'line' }])
|
|
149
|
+
.close([{ s: '#ZZ', a: actions.finish }, {}])
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
// One `#LB` consumed per iteration, tail-recursing via `r:` until only
|
|
153
|
+
// `#ZZ` remains; the empty alts are the fall-through that hands control
|
|
154
|
+
// back to `markdown`'s close.
|
|
155
|
+
//
|
|
156
|
+
// The first alt is the GFM extension seam: tagged `g: 'gfm'`, gated on
|
|
157
|
+
// the token's `tblArm` bit, arming the shared table probe for this line.
|
|
158
|
+
// It is injected exactly the way a downstream dialect would extend this
|
|
159
|
+
// grammar — an alt ahead of the base one, subtractable by its group tag —
|
|
160
|
+
// and `gfm: false` removes it below via `rule.exclude`, so the base
|
|
161
|
+
// dialect is the grammar minus the tagged alts, visibly (`debug.model()`).
|
|
162
|
+
tn.rule('line', (rs) => {
|
|
163
|
+
return rs
|
|
164
|
+
.clear()
|
|
165
|
+
.open([
|
|
166
|
+
{
|
|
167
|
+
s: '#LB',
|
|
168
|
+
c: (_r: any, ctx: any) => true === ctx.t0.use.tblArm,
|
|
169
|
+
r: 'line',
|
|
170
|
+
a: actions.lineGfm,
|
|
171
|
+
g: 'gfm',
|
|
172
|
+
},
|
|
173
|
+
{ s: '#LB', r: 'line', a: actions.line },
|
|
174
|
+
{},
|
|
175
|
+
])
|
|
176
|
+
.close([{}])
|
|
153
177
|
})
|
|
154
178
|
|
|
179
|
+
// The GFM dialect is subtracted, not branched around: without the option,
|
|
180
|
+
// the tagged alts above simply do not exist in this instance's grammar.
|
|
181
|
+
if (true !== opts.gfm) {
|
|
182
|
+
tn.options({ rule: { exclude: 'gfm' } })
|
|
183
|
+
}
|
|
184
|
+
|
|
155
185
|
// Not part of the tabnas contract, but useful for tests, docs and callers
|
|
156
186
|
// that want HTML or the native tree without a second import.
|
|
157
187
|
;(tn as any).markdown = {
|
|
@@ -164,7 +194,7 @@ const Markdown: Plugin = (tn: Tabnas, options?: MarkdownOptions) => {
|
|
|
164
194
|
|
|
165
195
|
Markdown.defaults = { gfm: true, breaks: false } as MarkdownOptions
|
|
166
196
|
|
|
167
|
-
export { VERSION, Markdown
|
|
197
|
+
export { VERSION, Markdown }
|
|
168
198
|
|
|
169
199
|
export type {
|
|
170
200
|
DocumentNode,
|
|
@@ -203,4 +233,4 @@ export { renderHTML } from './html.ts'
|
|
|
203
233
|
// VERSION is this package's version. It MUST equal package.json "version":
|
|
204
234
|
// the release orchestrator rewrites both, and the version test fails the
|
|
205
235
|
// build if they drift. Mirrors `const VERSION` in go/markdown.go.
|
|
206
|
-
const VERSION = '0.
|
|
236
|
+
const VERSION = '0.7.1'
|