@fuzdev/mdz 0.1.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/LICENSE +21 -0
- package/README.md +32 -0
- package/dist/Mdz.svelte +39 -0
- package/dist/Mdz.svelte.d.ts +17 -0
- package/dist/Mdz.svelte.d.ts.map +1 -0
- package/dist/MdzNodeView.svelte +140 -0
- package/dist/MdzNodeView.svelte.d.ts +9 -0
- package/dist/MdzNodeView.svelte.d.ts.map +1 -0
- package/dist/MdzPrecompiled.svelte +33 -0
- package/dist/MdzPrecompiled.svelte.d.ts +17 -0
- package/dist/MdzPrecompiled.svelte.d.ts.map +1 -0
- package/dist/MdzRoot.svelte +40 -0
- package/dist/MdzRoot.svelte.d.ts +14 -0
- package/dist/MdzRoot.svelte.d.ts.map +1 -0
- package/dist/MdzStream.svelte +37 -0
- package/dist/MdzStream.svelte.d.ts +18 -0
- package/dist/MdzStream.svelte.d.ts.map +1 -0
- package/dist/MdzStreamNodeView.svelte +150 -0
- package/dist/MdzStreamNodeView.svelte.d.ts +9 -0
- package/dist/MdzStreamNodeView.svelte.d.ts.map +1 -0
- package/dist/context_helpers.d.ts +23 -0
- package/dist/context_helpers.d.ts.map +1 -0
- package/dist/context_helpers.js +19 -0
- package/dist/mdz.d.ts +152 -0
- package/dist/mdz.d.ts.map +1 -0
- package/dist/mdz.js +61 -0
- package/dist/mdz_contexts.d.ts +106 -0
- package/dist/mdz_contexts.d.ts.map +1 -0
- package/dist/mdz_contexts.js +42 -0
- package/dist/mdz_helpers.d.ts +321 -0
- package/dist/mdz_helpers.d.ts.map +1 -0
- package/dist/mdz_helpers.js +668 -0
- package/dist/mdz_lexer.d.ts +123 -0
- package/dist/mdz_lexer.d.ts.map +1 -0
- package/dist/mdz_lexer.js +1405 -0
- package/dist/mdz_opcodes.d.ts +193 -0
- package/dist/mdz_opcodes.d.ts.map +1 -0
- package/dist/mdz_opcodes.js +14 -0
- package/dist/mdz_opcodes_to_nodes.d.ts +26 -0
- package/dist/mdz_opcodes_to_nodes.d.ts.map +1 -0
- package/dist/mdz_opcodes_to_nodes.js +410 -0
- package/dist/mdz_stream_parser.d.ts +109 -0
- package/dist/mdz_stream_parser.d.ts.map +1 -0
- package/dist/mdz_stream_parser.js +699 -0
- package/dist/mdz_stream_parser_block.d.ts +99 -0
- package/dist/mdz_stream_parser_block.d.ts.map +1 -0
- package/dist/mdz_stream_parser_block.js +437 -0
- package/dist/mdz_stream_parser_inline.d.ts +98 -0
- package/dist/mdz_stream_parser_inline.d.ts.map +1 -0
- package/dist/mdz_stream_parser_inline.js +481 -0
- package/dist/mdz_stream_parser_link.d.ts +29 -0
- package/dist/mdz_stream_parser_link.d.ts.map +1 -0
- package/dist/mdz_stream_parser_link.js +227 -0
- package/dist/mdz_stream_parser_list.d.ts +106 -0
- package/dist/mdz_stream_parser_list.d.ts.map +1 -0
- package/dist/mdz_stream_parser_list.js +438 -0
- package/dist/mdz_stream_parser_state.d.ts +476 -0
- package/dist/mdz_stream_parser_state.d.ts.map +1 -0
- package/dist/mdz_stream_parser_state.js +651 -0
- package/dist/mdz_stream_parser_text.d.ts +25 -0
- package/dist/mdz_stream_parser_text.d.ts.map +1 -0
- package/dist/mdz_stream_parser_text.js +67 -0
- package/dist/mdz_stream_parser_url.d.ts +76 -0
- package/dist/mdz_stream_parser_url.d.ts.map +1 -0
- package/dist/mdz_stream_parser_url.js +328 -0
- package/dist/mdz_stream_state.svelte.d.ts +53 -0
- package/dist/mdz_stream_state.svelte.d.ts.map +1 -0
- package/dist/mdz_stream_state.svelte.js +360 -0
- package/dist/mdz_to_svelte.d.ts +71 -0
- package/dist/mdz_to_svelte.d.ts.map +1 -0
- package/dist/mdz_to_svelte.js +127 -0
- package/dist/mdz_token_parser.d.ts +20 -0
- package/dist/mdz_token_parser.d.ts.map +1 -0
- package/dist/mdz_token_parser.js +449 -0
- package/dist/svelte_preprocess_mdz.d.ts +80 -0
- package/dist/svelte_preprocess_mdz.d.ts.map +1 -0
- package/dist/svelte_preprocess_mdz.js +543 -0
- package/dist/tsdoc_mdz.d.ts +45 -0
- package/dist/tsdoc_mdz.d.ts.map +1 -0
- package/dist/tsdoc_mdz.js +88 -0
- package/package.json +131 -0
- package/src/lib/context_helpers.ts +52 -0
- package/src/lib/mdz.ts +190 -0
- package/src/lib/mdz_contexts.ts +99 -0
- package/src/lib/mdz_helpers.ts +727 -0
- package/src/lib/mdz_lexer.ts +1761 -0
- package/src/lib/mdz_opcodes.ts +227 -0
- package/src/lib/mdz_opcodes_to_nodes.ts +470 -0
- package/src/lib/mdz_stream_parser.ts +766 -0
- package/src/lib/mdz_stream_parser_block.ts +490 -0
- package/src/lib/mdz_stream_parser_inline.ts +527 -0
- package/src/lib/mdz_stream_parser_link.ts +283 -0
- package/src/lib/mdz_stream_parser_list.ts +525 -0
- package/src/lib/mdz_stream_parser_state.ts +957 -0
- package/src/lib/mdz_stream_parser_text.ts +95 -0
- package/src/lib/mdz_stream_parser_url.ts +386 -0
- package/src/lib/mdz_stream_state.svelte.ts +383 -0
- package/src/lib/mdz_to_svelte.ts +213 -0
- package/src/lib/mdz_token_parser.ts +544 -0
- package/src/lib/svelte_preprocess_mdz.ts +777 -0
- package/src/lib/tsdoc_mdz.ts +97 -0
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mdz token parser — consumes a `MdzToken[]` stream to build the `MdzNode[]` AST.
|
|
3
|
+
*
|
|
4
|
+
* Phase 2 of the synchronous parsing pipeline behind `mdz_parse`.
|
|
5
|
+
* Phase 1 (lexer) is in `mdz_lexer.ts`.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { has_non_whitespace, mdz_extract_single_tag, mdz_heading_id, mdz_merge_adjacent_text, mdz_push_merging_text, } from './mdz_helpers.js';
|
|
10
|
+
import {} from './mdz_lexer.js';
|
|
11
|
+
/**
|
|
12
|
+
* Builds an `MdzNode[]` tree from a lexed `MdzToken[]` stream.
|
|
13
|
+
* Used by `mdz_parse`, which should be preferred for simple usage.
|
|
14
|
+
*/
|
|
15
|
+
export class MdzTokenParser {
|
|
16
|
+
#tokens;
|
|
17
|
+
#index = 0;
|
|
18
|
+
constructor(tokens) {
|
|
19
|
+
this.#tokens = tokens;
|
|
20
|
+
}
|
|
21
|
+
parse() {
|
|
22
|
+
return this.#parse_block_stream(false);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Parse a run of block-level tokens into nodes — the document root, or a
|
|
26
|
+
* blockquote's contents (a quote's content is a mini-document, so both
|
|
27
|
+
* share one loop). When `in_blockquote`, a `blockquote_close` token ends
|
|
28
|
+
* the run; at the root the token stream's end does.
|
|
29
|
+
*/
|
|
30
|
+
#parse_block_stream(in_blockquote) {
|
|
31
|
+
const nodes = [];
|
|
32
|
+
const paragraph_children = [];
|
|
33
|
+
while (this.#index < this.#tokens.length) {
|
|
34
|
+
const token = this.#tokens[this.#index];
|
|
35
|
+
if (in_blockquote && token.type === 'blockquote_close') {
|
|
36
|
+
this.#index++;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
// Block-level tokens
|
|
40
|
+
if (token.type === 'heading_start') {
|
|
41
|
+
const flushed = this.#flush_paragraph(paragraph_children, true);
|
|
42
|
+
if (flushed)
|
|
43
|
+
nodes.push(flushed);
|
|
44
|
+
nodes.push(this.#parse_heading());
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (token.type === 'hr') {
|
|
48
|
+
const flushed = this.#flush_paragraph(paragraph_children, true);
|
|
49
|
+
if (flushed)
|
|
50
|
+
nodes.push(flushed);
|
|
51
|
+
nodes.push({ type: 'Hr', start: token.start, end: token.end });
|
|
52
|
+
this.#index++;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (token.type === 'codeblock') {
|
|
56
|
+
const flushed = this.#flush_paragraph(paragraph_children, true);
|
|
57
|
+
if (flushed)
|
|
58
|
+
nodes.push(flushed);
|
|
59
|
+
nodes.push({
|
|
60
|
+
type: 'Codeblock',
|
|
61
|
+
lang: token.lang,
|
|
62
|
+
content: token.content,
|
|
63
|
+
start: token.start,
|
|
64
|
+
end: token.end,
|
|
65
|
+
});
|
|
66
|
+
this.#index++;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (token.type === 'list_open') {
|
|
70
|
+
const flushed = this.#flush_paragraph(paragraph_children, true);
|
|
71
|
+
if (flushed)
|
|
72
|
+
nodes.push(flushed);
|
|
73
|
+
nodes.push(this.#parse_list());
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (token.type === 'blockquote_open') {
|
|
77
|
+
const flushed = this.#flush_paragraph(paragraph_children, true);
|
|
78
|
+
if (flushed)
|
|
79
|
+
nodes.push(flushed);
|
|
80
|
+
nodes.push(this.#parse_blockquote());
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (token.type === 'paragraph_break') {
|
|
84
|
+
const flushed = this.#flush_paragraph(paragraph_children, true);
|
|
85
|
+
if (flushed)
|
|
86
|
+
nodes.push(flushed);
|
|
87
|
+
this.#index++;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
// Inline tokens → paragraph children
|
|
91
|
+
const node = this.#parse_inline();
|
|
92
|
+
if (node)
|
|
93
|
+
paragraph_children.push(node);
|
|
94
|
+
}
|
|
95
|
+
// Flush remaining content
|
|
96
|
+
const final_paragraph = this.#flush_paragraph(paragraph_children, true);
|
|
97
|
+
if (final_paragraph)
|
|
98
|
+
nodes.push(final_paragraph);
|
|
99
|
+
return nodes;
|
|
100
|
+
}
|
|
101
|
+
#parse_blockquote() {
|
|
102
|
+
const open = this.#tokens[this.#index];
|
|
103
|
+
this.#index++;
|
|
104
|
+
const children = this.#parse_block_stream(true);
|
|
105
|
+
// children-derived end, like List — no-empty-start means children exist,
|
|
106
|
+
// with the open's position as a defensive fallback
|
|
107
|
+
const end = children.length > 0 ? children[children.length - 1].end : open.end;
|
|
108
|
+
return { type: 'Blockquote', children, start: open.start, end };
|
|
109
|
+
}
|
|
110
|
+
#parse_heading() {
|
|
111
|
+
const token = this.#tokens[this.#index];
|
|
112
|
+
const start = token.start;
|
|
113
|
+
const level = token.level;
|
|
114
|
+
this.#index++;
|
|
115
|
+
const children = [];
|
|
116
|
+
// Collect inline nodes until heading_end marker
|
|
117
|
+
while (this.#index < this.#tokens.length) {
|
|
118
|
+
const t = this.#tokens[this.#index];
|
|
119
|
+
if (t.type === 'heading_end') {
|
|
120
|
+
this.#index++; // consume the marker
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
const node = this.#parse_inline();
|
|
124
|
+
if (node)
|
|
125
|
+
children.push(node);
|
|
126
|
+
}
|
|
127
|
+
const merged = mdz_merge_adjacent_text(children);
|
|
128
|
+
const end = merged.length > 0 ? merged[merged.length - 1].end : start + level + 1;
|
|
129
|
+
return { type: 'Heading', level, id: mdz_heading_id(merged), children: merged, start, end };
|
|
130
|
+
}
|
|
131
|
+
#parse_list() {
|
|
132
|
+
const open = this.#tokens[this.#index];
|
|
133
|
+
this.#index++;
|
|
134
|
+
const children = [];
|
|
135
|
+
while (this.#index < this.#tokens.length) {
|
|
136
|
+
const t = this.#tokens[this.#index];
|
|
137
|
+
if (t.type === 'list_item_open') {
|
|
138
|
+
children.push(this.#parse_list_item());
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (t.type === 'list_close') {
|
|
142
|
+
this.#index++;
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
this.#index++; // skip anything unexpected (defensive)
|
|
146
|
+
}
|
|
147
|
+
const end = children.length > 0 ? children[children.length - 1].end : open.end;
|
|
148
|
+
if (open.ordered) {
|
|
149
|
+
return {
|
|
150
|
+
type: 'List',
|
|
151
|
+
ordered: true,
|
|
152
|
+
start_number: children[0]?.number ?? 1,
|
|
153
|
+
children,
|
|
154
|
+
start: open.start,
|
|
155
|
+
end,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return { type: 'List', ordered: false, children, start: open.start, end };
|
|
159
|
+
}
|
|
160
|
+
#parse_list_item() {
|
|
161
|
+
const open = this.#tokens[this.#index];
|
|
162
|
+
this.#index++;
|
|
163
|
+
// the item's first inline run is a direct child run; after a
|
|
164
|
+
// `paragraph_break`, subsequent runs become `Paragraph` block children
|
|
165
|
+
const children = [];
|
|
166
|
+
const paragraph = [];
|
|
167
|
+
let in_paragraph = false;
|
|
168
|
+
const flush = () => {
|
|
169
|
+
const flushed = this.#flush_paragraph(paragraph, true);
|
|
170
|
+
if (flushed)
|
|
171
|
+
children.push(flushed);
|
|
172
|
+
};
|
|
173
|
+
while (this.#index < this.#tokens.length) {
|
|
174
|
+
const t = this.#tokens[this.#index];
|
|
175
|
+
if (t.type === 'list_item_close') {
|
|
176
|
+
this.#index++;
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
if (t.type === 'list_open') {
|
|
180
|
+
flush();
|
|
181
|
+
in_paragraph = false;
|
|
182
|
+
children.push(this.#parse_list());
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (t.type === 'blockquote_open') {
|
|
186
|
+
flush();
|
|
187
|
+
in_paragraph = false;
|
|
188
|
+
children.push(this.#parse_blockquote());
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (t.type === 'codeblock') {
|
|
192
|
+
flush();
|
|
193
|
+
in_paragraph = false;
|
|
194
|
+
children.push({
|
|
195
|
+
type: 'Codeblock',
|
|
196
|
+
lang: t.lang,
|
|
197
|
+
content: t.content,
|
|
198
|
+
start: t.start,
|
|
199
|
+
end: t.end,
|
|
200
|
+
});
|
|
201
|
+
this.#index++;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if (t.type === 'paragraph_break') {
|
|
205
|
+
flush();
|
|
206
|
+
in_paragraph = true;
|
|
207
|
+
this.#index++;
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
const node = this.#parse_inline();
|
|
211
|
+
if (node) {
|
|
212
|
+
if (in_paragraph) {
|
|
213
|
+
paragraph.push(node);
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
mdz_push_merging_text(children, node);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
flush();
|
|
221
|
+
const end = children.length > 0 ? children[children.length - 1].end : open.end;
|
|
222
|
+
if (open.number !== undefined) {
|
|
223
|
+
return { type: 'ListItem', number: open.number, children, start: open.start, end };
|
|
224
|
+
}
|
|
225
|
+
return { type: 'ListItem', children, start: open.start, end };
|
|
226
|
+
}
|
|
227
|
+
#parse_inline() {
|
|
228
|
+
if (this.#index >= this.#tokens.length)
|
|
229
|
+
return null;
|
|
230
|
+
const token = this.#tokens[this.#index];
|
|
231
|
+
switch (token.type) {
|
|
232
|
+
case 'text':
|
|
233
|
+
this.#index++;
|
|
234
|
+
return { type: 'Text', content: token.content, start: token.start, end: token.end };
|
|
235
|
+
case 'code':
|
|
236
|
+
this.#index++;
|
|
237
|
+
return { type: 'Code', content: token.content, start: token.start, end: token.end };
|
|
238
|
+
case 'bold_open':
|
|
239
|
+
return this.#parse_delimiter_pair('bold_close', 'Bold', '**');
|
|
240
|
+
case 'italic_open':
|
|
241
|
+
return this.#parse_delimiter_pair('italic_close', 'Italic', '_');
|
|
242
|
+
case 'strikethrough_open':
|
|
243
|
+
return this.#parse_delimiter_pair('strikethrough_close', 'Strikethrough', '~~');
|
|
244
|
+
case 'link_text_open':
|
|
245
|
+
return this.#parse_link();
|
|
246
|
+
case 'autolink':
|
|
247
|
+
return this.#parse_autolink();
|
|
248
|
+
case 'tag_open':
|
|
249
|
+
return this.#parse_tag_node();
|
|
250
|
+
case 'tag_self_close':
|
|
251
|
+
return this.#parse_self_close_tag();
|
|
252
|
+
// Orphaned close tokens - treat as text
|
|
253
|
+
case 'bold_close':
|
|
254
|
+
this.#index++;
|
|
255
|
+
return { type: 'Text', content: '**', start: token.start, end: token.end };
|
|
256
|
+
case 'italic_close':
|
|
257
|
+
this.#index++;
|
|
258
|
+
return { type: 'Text', content: '_', start: token.start, end: token.end };
|
|
259
|
+
case 'strikethrough_close':
|
|
260
|
+
this.#index++;
|
|
261
|
+
return { type: 'Text', content: '~~', start: token.start, end: token.end };
|
|
262
|
+
case 'link_text_close':
|
|
263
|
+
this.#index++;
|
|
264
|
+
return { type: 'Text', content: ']', start: token.start, end: token.end };
|
|
265
|
+
case 'link_ref':
|
|
266
|
+
this.#index++;
|
|
267
|
+
return {
|
|
268
|
+
type: 'Text',
|
|
269
|
+
content: `(${token.reference})`,
|
|
270
|
+
start: token.start,
|
|
271
|
+
end: token.end,
|
|
272
|
+
};
|
|
273
|
+
case 'tag_close':
|
|
274
|
+
this.#index++;
|
|
275
|
+
return {
|
|
276
|
+
type: 'Text',
|
|
277
|
+
content: `</${token.name}>`,
|
|
278
|
+
start: token.start,
|
|
279
|
+
end: token.end,
|
|
280
|
+
};
|
|
281
|
+
default:
|
|
282
|
+
this.#index++;
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Parse a delimiter-paired inline (the cursor is on a
|
|
288
|
+
* `bold_open`/`italic_open`/`strikethrough_open` token) into its container
|
|
289
|
+
* node, consuming through the matching close token. An opener whose close
|
|
290
|
+
* token never arrives (block boundary or end of tokens first) degrades to
|
|
291
|
+
* the literal delimiter text — the parse bodies are identical across the
|
|
292
|
+
* three pairs, only the token/node types and delimiter text differ.
|
|
293
|
+
*/
|
|
294
|
+
#parse_delimiter_pair(close_type, node_type, delimiter) {
|
|
295
|
+
const start = this.#tokens[this.#index].start;
|
|
296
|
+
this.#index++;
|
|
297
|
+
const children = [];
|
|
298
|
+
while (this.#index < this.#tokens.length) {
|
|
299
|
+
const t = this.#tokens[this.#index];
|
|
300
|
+
if (t.type === close_type) {
|
|
301
|
+
this.#index++;
|
|
302
|
+
return { type: node_type, children, start, end: t.end };
|
|
303
|
+
}
|
|
304
|
+
if (t.type === 'paragraph_break' ||
|
|
305
|
+
t.type === 'heading_start' ||
|
|
306
|
+
t.type === 'hr' ||
|
|
307
|
+
t.type === 'codeblock' ||
|
|
308
|
+
t.type === 'list_open' ||
|
|
309
|
+
t.type === 'list_item_close' ||
|
|
310
|
+
t.type === 'blockquote_open') {
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
const node = this.#parse_inline();
|
|
314
|
+
if (node)
|
|
315
|
+
mdz_push_merging_text(children, node);
|
|
316
|
+
}
|
|
317
|
+
// Unclosed - treat as text
|
|
318
|
+
return { type: 'Text', content: delimiter, start, end: start + delimiter.length };
|
|
319
|
+
}
|
|
320
|
+
#parse_link() {
|
|
321
|
+
const open_token = this.#tokens[this.#index];
|
|
322
|
+
const start = open_token.start;
|
|
323
|
+
this.#index++;
|
|
324
|
+
const children = [];
|
|
325
|
+
while (this.#index < this.#tokens.length) {
|
|
326
|
+
const t = this.#tokens[this.#index];
|
|
327
|
+
if (t.type === 'link_text_close') {
|
|
328
|
+
this.#index++;
|
|
329
|
+
// Expect link_ref next
|
|
330
|
+
if (this.#index < this.#tokens.length && this.#tokens[this.#index].type === 'link_ref') {
|
|
331
|
+
const ref_token = this.#tokens[this.#index];
|
|
332
|
+
this.#index++;
|
|
333
|
+
return {
|
|
334
|
+
type: 'Link',
|
|
335
|
+
reference: ref_token.reference,
|
|
336
|
+
children,
|
|
337
|
+
link_type: ref_token.link_type,
|
|
338
|
+
start,
|
|
339
|
+
end: ref_token.end,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
// No link_ref - treat as text
|
|
343
|
+
return { type: 'Text', content: '[', start, end: start + 1 };
|
|
344
|
+
}
|
|
345
|
+
if (t.type === 'paragraph_break' ||
|
|
346
|
+
t.type === 'heading_start' ||
|
|
347
|
+
t.type === 'hr' ||
|
|
348
|
+
t.type === 'codeblock' ||
|
|
349
|
+
t.type === 'list_open' ||
|
|
350
|
+
t.type === 'list_item_close' ||
|
|
351
|
+
t.type === 'blockquote_open') {
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
const node = this.#parse_inline();
|
|
355
|
+
if (node)
|
|
356
|
+
mdz_push_merging_text(children, node);
|
|
357
|
+
}
|
|
358
|
+
return { type: 'Text', content: '[', start, end: start + 1 };
|
|
359
|
+
}
|
|
360
|
+
#parse_autolink() {
|
|
361
|
+
const token = this.#tokens[this.#index];
|
|
362
|
+
this.#index++;
|
|
363
|
+
return {
|
|
364
|
+
type: 'Link',
|
|
365
|
+
reference: token.reference,
|
|
366
|
+
children: [{ type: 'Text', content: token.reference, start: token.start, end: token.end }],
|
|
367
|
+
link_type: token.link_type,
|
|
368
|
+
start: token.start,
|
|
369
|
+
end: token.end,
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
#parse_tag_node() {
|
|
373
|
+
const open_token = this.#tokens[this.#index];
|
|
374
|
+
const start = open_token.start;
|
|
375
|
+
const tag_name = open_token.name;
|
|
376
|
+
const node_type = open_token.is_component ? 'Component' : 'Element';
|
|
377
|
+
this.#index++;
|
|
378
|
+
const children = [];
|
|
379
|
+
while (this.#index < this.#tokens.length) {
|
|
380
|
+
const t = this.#tokens[this.#index];
|
|
381
|
+
if (t.type === 'tag_close' && t.name === tag_name) {
|
|
382
|
+
this.#index++;
|
|
383
|
+
return { type: node_type, name: tag_name, children, start, end: t.end };
|
|
384
|
+
}
|
|
385
|
+
const node = this.#parse_inline();
|
|
386
|
+
if (node)
|
|
387
|
+
mdz_push_merging_text(children, node);
|
|
388
|
+
}
|
|
389
|
+
// Unclosed tag
|
|
390
|
+
return { type: 'Text', content: '<', start, end: start + 1 };
|
|
391
|
+
}
|
|
392
|
+
#parse_self_close_tag() {
|
|
393
|
+
const token = this.#tokens[this.#index];
|
|
394
|
+
const node_type = token.is_component ? 'Component' : 'Element';
|
|
395
|
+
this.#index++;
|
|
396
|
+
return { type: node_type, name: token.name, children: [], start: token.start, end: token.end };
|
|
397
|
+
}
|
|
398
|
+
// -- Paragraph flushing --
|
|
399
|
+
#flush_paragraph(paragraph_children, trim_trailing = false) {
|
|
400
|
+
if (paragraph_children.length === 0)
|
|
401
|
+
return null;
|
|
402
|
+
if (trim_trailing) {
|
|
403
|
+
// Trim trailing newlines from last text node. The end adjusts by the
|
|
404
|
+
// trimmed amount only — recomputing from the content length would
|
|
405
|
+
// clobber source-accurate spans where structural stripping (list
|
|
406
|
+
// continuation indent, quote prefixes) made content shorter than its
|
|
407
|
+
// source span.
|
|
408
|
+
const last = paragraph_children[paragraph_children.length - 1];
|
|
409
|
+
if (last.type === 'Text') {
|
|
410
|
+
const trimmed = last.content.replace(/\n+$/, '');
|
|
411
|
+
if (trimmed) {
|
|
412
|
+
if (trimmed !== last.content) {
|
|
413
|
+
last.end -= last.content.length - trimmed.length;
|
|
414
|
+
last.content = trimmed;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
paragraph_children.pop();
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
// Skip whitespace-only paragraphs — ASCII whitespace only, matching
|
|
422
|
+
// the grammar's blank-line definition and the streaming parser's
|
|
423
|
+
// `has_non_whitespace_content` (Unicode whitespace like NBSP is
|
|
424
|
+
// content; `.trim()` here would silently drop it)
|
|
425
|
+
const has_content = paragraph_children.some((n) => n.type !== 'Text' || has_non_whitespace(n.content));
|
|
426
|
+
if (!has_content) {
|
|
427
|
+
paragraph_children.length = 0;
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
// Single tag extraction (MDX convention)
|
|
432
|
+
const single_tag = mdz_extract_single_tag(paragraph_children);
|
|
433
|
+
if (single_tag) {
|
|
434
|
+
paragraph_children.length = 0;
|
|
435
|
+
return single_tag;
|
|
436
|
+
}
|
|
437
|
+
// Merge adjacent text nodes
|
|
438
|
+
const merged = mdz_merge_adjacent_text(paragraph_children.slice());
|
|
439
|
+
paragraph_children.length = 0;
|
|
440
|
+
if (merged.length === 0)
|
|
441
|
+
return null;
|
|
442
|
+
return {
|
|
443
|
+
type: 'Paragraph',
|
|
444
|
+
children: merged,
|
|
445
|
+
start: merged[0].start,
|
|
446
|
+
end: merged[merged.length - 1].end,
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelte preprocessor that compiles static `Mdz` content to Svelte markup at build time.
|
|
3
|
+
*
|
|
4
|
+
* Detects `Mdz` components with static string `content` props, parses the mdz content,
|
|
5
|
+
* renders each `MdzNode` to equivalent Svelte markup via `mdz_to_svelte`, and replaces
|
|
6
|
+
* the `Mdz` with `MdzPrecompiled` containing pre-rendered children.
|
|
7
|
+
*
|
|
8
|
+
* Also handles ternary chains (`content={a ? 'x' : b ? 'y' : 'z'}`) where all leaf
|
|
9
|
+
* values are statically resolvable strings, emitting `{#if a}markup_x{:else if b}markup_y{:else}markup_z{/if}`
|
|
10
|
+
* as children of a single `MdzPrecompiled`.
|
|
11
|
+
*
|
|
12
|
+
* Truly dynamic `content` props are left untouched.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
import { type PreprocessorGroup } from 'svelte/compiler';
|
|
17
|
+
/**
|
|
18
|
+
* Options for `svelte_preprocess_mdz`.
|
|
19
|
+
*/
|
|
20
|
+
export interface SveltePreprocessMdzOptions {
|
|
21
|
+
/** File patterns to exclude. */
|
|
22
|
+
exclude?: Array<string | RegExp>;
|
|
23
|
+
/**
|
|
24
|
+
* Component import mapping for mdz content.
|
|
25
|
+
* Key: component name as used in mdz (e.g., 'Alert').
|
|
26
|
+
* Value: import path (e.g., '$lib/Alert.svelte').
|
|
27
|
+
*
|
|
28
|
+
* If mdz content references a component not in this map,
|
|
29
|
+
* that `Mdz` usage is skipped (left as runtime).
|
|
30
|
+
*/
|
|
31
|
+
components?: Record<string, string>;
|
|
32
|
+
/**
|
|
33
|
+
* Allowed HTML elements in mdz content (e.g., `{aside: true, details: true}`).
|
|
34
|
+
* Values are boolean placeholders for future per-element configuration,
|
|
35
|
+
* mirroring the runtime `MdzElements` registry.
|
|
36
|
+
* If mdz content references an element not in this record,
|
|
37
|
+
* that `Mdz` usage is skipped (left as runtime).
|
|
38
|
+
*/
|
|
39
|
+
elements?: Record<string, boolean>;
|
|
40
|
+
/**
|
|
41
|
+
* Import sources that resolve to the `Mdz` component.
|
|
42
|
+
* Used to verify that `Mdz` in templates refers to mdz's `Mdz.svelte`.
|
|
43
|
+
*
|
|
44
|
+
* @default ['@fuzdev/mdz/Mdz.svelte']
|
|
45
|
+
*/
|
|
46
|
+
mdz_component_imports?: Array<string>;
|
|
47
|
+
/**
|
|
48
|
+
* Import path for the precompiled wrapper component.
|
|
49
|
+
*
|
|
50
|
+
* @default '@fuzdev/mdz/MdzPrecompiled.svelte'
|
|
51
|
+
*/
|
|
52
|
+
compiled_component_import?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Import path for the inline `code` component (receives `reference`).
|
|
55
|
+
* When omitted, inline code compiles to a plain `<code>` element, matching
|
|
56
|
+
* `Mdz`'s runtime default. Set e.g. to `@fuzdev/fuz_ui/DocsLink.svelte` to
|
|
57
|
+
* auto-link backticked identifiers.
|
|
58
|
+
*/
|
|
59
|
+
code_component_import?: string;
|
|
60
|
+
/**
|
|
61
|
+
* Import path for the codeblock component (receives `lang` and `content`).
|
|
62
|
+
* When omitted, code blocks compile to a plain `<pre><code>` element. Set
|
|
63
|
+
* e.g. to `@fuzdev/fuz_code/Code.svelte` for syntax highlighting.
|
|
64
|
+
*/
|
|
65
|
+
codeblock_component_import?: string;
|
|
66
|
+
/**
|
|
67
|
+
* How to handle errors during mdz parsing or rendering.
|
|
68
|
+
*
|
|
69
|
+
* @default 'throw' in CI, 'log' otherwise
|
|
70
|
+
*/
|
|
71
|
+
on_error?: 'log' | 'throw';
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Creates a Svelte preprocessor that compiles static `Mdz` content at build time.
|
|
75
|
+
*
|
|
76
|
+
* @param options - configuration for component/element resolution and file filtering
|
|
77
|
+
* @returns a Svelte `PreprocessorGroup` for use in `svelte.config.js`
|
|
78
|
+
*/
|
|
79
|
+
export declare const svelte_preprocess_mdz: (options?: SveltePreprocessMdzOptions) => PreprocessorGroup;
|
|
80
|
+
//# sourceMappingURL=svelte_preprocess_mdz.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svelte_preprocess_mdz.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/svelte_preprocess_mdz.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAAQ,KAAK,iBAAiB,EAAW,MAAM,iBAAiB,CAAC;AA+BxE;;GAEG;AACH,MAAM,WAAW,0BAA0B;IAC1C,gCAAgC;IAChC,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IAEjC;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEpC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEnC;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAEtC;;;;OAIG;IACH,yBAAyB,CAAC,EAAE,MAAM,CAAC;IAEnC;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B;;;;OAIG;IACH,0BAA0B,CAAC,EAAE,MAAM,CAAC;IAEpC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;CAC3B;AAID;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GACjC,UAAS,0BAA+B,KACtC,iBAyFF,CAAC"}
|