@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,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown link `[text](url)` handlers and HTML-style tag handlers
|
|
3
|
+
* (`<tag>`, `<Tag />`, `</tag>`) for the streaming mdz parser.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {MdzNodeTypeContainer} from './mdz_opcodes.js';
|
|
9
|
+
import {
|
|
10
|
+
A_UPPER,
|
|
11
|
+
LEFT_BRACKET,
|
|
12
|
+
LEFT_PAREN,
|
|
13
|
+
NEWLINE,
|
|
14
|
+
RIGHT_ANGLE,
|
|
15
|
+
RIGHT_BRACKET,
|
|
16
|
+
RIGHT_PAREN,
|
|
17
|
+
SLASH,
|
|
18
|
+
SPACE,
|
|
19
|
+
Z_UPPER,
|
|
20
|
+
is_letter,
|
|
21
|
+
is_tag_name_char,
|
|
22
|
+
is_valid_path_char,
|
|
23
|
+
mdz_is_safe_reference,
|
|
24
|
+
mdz_is_url,
|
|
25
|
+
} from './mdz_helpers.js';
|
|
26
|
+
import {
|
|
27
|
+
type MdzStreamParserState,
|
|
28
|
+
type TryResult,
|
|
29
|
+
accumulate_text,
|
|
30
|
+
alloc_id,
|
|
31
|
+
emit,
|
|
32
|
+
ensure_paragraph,
|
|
33
|
+
flush_text,
|
|
34
|
+
is_run_boundary,
|
|
35
|
+
offset,
|
|
36
|
+
pop_stack_entry,
|
|
37
|
+
push_stack_entry,
|
|
38
|
+
revert_above,
|
|
39
|
+
} from './mdz_stream_parser_state.js';
|
|
40
|
+
|
|
41
|
+
// -- Links --
|
|
42
|
+
|
|
43
|
+
/** @nodocs */
|
|
44
|
+
export const try_link_open = (state: MdzStreamParserState): boolean => {
|
|
45
|
+
flush_text(state);
|
|
46
|
+
ensure_paragraph(state);
|
|
47
|
+
|
|
48
|
+
const id = alloc_id(state);
|
|
49
|
+
const link_start = offset(state);
|
|
50
|
+
emit(state, {type: 'open', id, node_type: 'Link', start: link_start});
|
|
51
|
+
push_stack_entry(state, id, 'Link', link_start, '[');
|
|
52
|
+
state.active_text_id = null;
|
|
53
|
+
state.pos++;
|
|
54
|
+
state.column++;
|
|
55
|
+
state.prev_char = LEFT_BRACKET;
|
|
56
|
+
return true;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Revert an optimistic Link container to its literal `[` and consume the
|
|
61
|
+
* `]` at `bracket_pos` as text. Used whenever a `]` doesn't form a valid
|
|
62
|
+
* `[text](url)` — missing `(`, empty url, invalid url char, etc.
|
|
63
|
+
*
|
|
64
|
+
* @nodocs
|
|
65
|
+
*/
|
|
66
|
+
export const abort_link_to_text = (
|
|
67
|
+
state: MdzStreamParserState,
|
|
68
|
+
link_stack_idx: number,
|
|
69
|
+
bracket_pos: number,
|
|
70
|
+
): void => {
|
|
71
|
+
flush_text(state);
|
|
72
|
+
revert_above(state, link_stack_idx);
|
|
73
|
+
const entry = pop_stack_entry(state);
|
|
74
|
+
emit(state, {type: 'revert', id: entry.id, replacement_text: '[', start: entry.start});
|
|
75
|
+
state.active_text_id = null;
|
|
76
|
+
accumulate_text(state, ']', offset(state, bracket_pos));
|
|
77
|
+
state.pos = bracket_pos + 1;
|
|
78
|
+
state.column++;
|
|
79
|
+
state.prev_char = RIGHT_BRACKET;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Try to complete a link after seeing `]`.
|
|
84
|
+
* Returns false if more input is needed (leaves pos at `]`).
|
|
85
|
+
*
|
|
86
|
+
* @nodocs
|
|
87
|
+
*/
|
|
88
|
+
export const try_complete_link = (state: MdzStreamParserState, link_stack_idx: number): boolean => {
|
|
89
|
+
const bracket_pos = state.pos;
|
|
90
|
+
|
|
91
|
+
// need at least one char after ]
|
|
92
|
+
if (bracket_pos + 1 >= state.buffer.length) return false;
|
|
93
|
+
|
|
94
|
+
const after_bracket = state.buffer.charCodeAt(bracket_pos + 1);
|
|
95
|
+
if (after_bracket !== LEFT_PAREN) {
|
|
96
|
+
abort_link_to_text(state, link_stack_idx, bracket_pos);
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// found ]( — scan for )
|
|
101
|
+
let i = bracket_pos + 2; // past ](
|
|
102
|
+
while (i < state.buffer.length) {
|
|
103
|
+
const c = state.buffer.charCodeAt(i);
|
|
104
|
+
if (c === RIGHT_PAREN) {
|
|
105
|
+
const reference = state.buffer.slice(bracket_pos + 2, i);
|
|
106
|
+
// validate reference: non-empty and only valid path chars
|
|
107
|
+
if (!reference.trim()) {
|
|
108
|
+
abort_link_to_text(state, link_stack_idx, bracket_pos);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
let valid = true;
|
|
112
|
+
for (let j = 0; j < reference.length; j++) {
|
|
113
|
+
if (!is_valid_path_char(reference.charCodeAt(j))) {
|
|
114
|
+
valid = false;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (!valid) {
|
|
119
|
+
abort_link_to_text(state, link_stack_idx, bracket_pos);
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
// Reject unsafe protocols (javascript:, data:, etc.) at parse time.
|
|
123
|
+
// `is_valid_path_char` permits `:` so we have to filter explicitly.
|
|
124
|
+
if (!mdz_is_safe_reference(reference)) {
|
|
125
|
+
abort_link_to_text(state, link_stack_idx, bracket_pos);
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// success — close the link with reference
|
|
130
|
+
flush_text(state);
|
|
131
|
+
revert_above(state, link_stack_idx);
|
|
132
|
+
const entry = pop_stack_entry(state);
|
|
133
|
+
const link_type = mdz_is_url(reference) ? 'external' : 'internal';
|
|
134
|
+
emit(state, {
|
|
135
|
+
type: 'close',
|
|
136
|
+
id: entry.id,
|
|
137
|
+
end: offset(state, i + 1),
|
|
138
|
+
reference,
|
|
139
|
+
link_type,
|
|
140
|
+
});
|
|
141
|
+
state.active_text_id = null;
|
|
142
|
+
state.pos = i + 1;
|
|
143
|
+
state.column += i + 1 - bracket_pos;
|
|
144
|
+
state.prev_char = RIGHT_PAREN;
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
if (c === NEWLINE || c === SPACE) {
|
|
148
|
+
// invalid character in URL (simplified check)
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
i++;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// didn't find ) — need more input or invalid
|
|
155
|
+
if (i >= state.buffer.length) return false; // need more input
|
|
156
|
+
|
|
157
|
+
// found invalid char before ) — revert
|
|
158
|
+
abort_link_to_text(state, link_stack_idx, bracket_pos);
|
|
159
|
+
return true;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// -- Tags --
|
|
163
|
+
|
|
164
|
+
/** @nodocs */
|
|
165
|
+
export const try_tag_open = (state: MdzStreamParserState): TryResult => {
|
|
166
|
+
const start = state.pos;
|
|
167
|
+
let i = start + 1; // past <
|
|
168
|
+
|
|
169
|
+
// tag name must start with letter. A `<` at the end of the buffer can't
|
|
170
|
+
// classify yet — it may be a tag open, or the closing tag of an open
|
|
171
|
+
// element (whose `</` dispatch needs the next char); consuming it as text
|
|
172
|
+
// here would eat the closer and strand the element. Hold, like the
|
|
173
|
+
// mid-name case below.
|
|
174
|
+
if (i >= state.buffer.length) return 'need_more';
|
|
175
|
+
if (!is_letter(state.buffer.charCodeAt(i))) {
|
|
176
|
+
return 'not_match';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// collect tag name
|
|
180
|
+
const name_start = i;
|
|
181
|
+
while (i < state.buffer.length && is_tag_name_char(state.buffer.charCodeAt(i))) {
|
|
182
|
+
i++;
|
|
183
|
+
}
|
|
184
|
+
if (i >= state.buffer.length) return 'need_more';
|
|
185
|
+
|
|
186
|
+
const name = state.buffer.slice(name_start, i);
|
|
187
|
+
|
|
188
|
+
// skip whitespace
|
|
189
|
+
while (i < state.buffer.length && state.buffer.charCodeAt(i) === SPACE) {
|
|
190
|
+
i++;
|
|
191
|
+
}
|
|
192
|
+
if (i >= state.buffer.length) return 'need_more';
|
|
193
|
+
|
|
194
|
+
// check for self-closing />
|
|
195
|
+
if (state.buffer.charCodeAt(i) === SLASH && i + 1 >= state.buffer.length) {
|
|
196
|
+
return 'need_more'; // `/` at the buffer end can't classify yet (`/>` or junk)
|
|
197
|
+
}
|
|
198
|
+
if (state.buffer.charCodeAt(i) === SLASH && state.buffer.charCodeAt(i + 1) === RIGHT_ANGLE) {
|
|
199
|
+
const first_char = name.charCodeAt(0);
|
|
200
|
+
const is_component = first_char >= A_UPPER && first_char <= Z_UPPER;
|
|
201
|
+
const node_type: MdzNodeTypeContainer = is_component ? 'Component' : 'Element';
|
|
202
|
+
|
|
203
|
+
flush_text(state);
|
|
204
|
+
ensure_paragraph(state);
|
|
205
|
+
const id = alloc_id(state);
|
|
206
|
+
const tag_start = offset(state, start);
|
|
207
|
+
emit(state, {type: 'open', id, node_type, start: tag_start, name});
|
|
208
|
+
emit(state, {type: 'close', id, end: offset(state, i + 2)});
|
|
209
|
+
state.active_text_id = null;
|
|
210
|
+
state.pos = i + 2;
|
|
211
|
+
state.column += i + 2 - start;
|
|
212
|
+
state.prev_char = RIGHT_ANGLE;
|
|
213
|
+
return 'consumed';
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// check for >
|
|
217
|
+
if (state.buffer.charCodeAt(i) !== RIGHT_ANGLE) {
|
|
218
|
+
return 'not_match';
|
|
219
|
+
}
|
|
220
|
+
i++; // past >
|
|
221
|
+
|
|
222
|
+
const first_char = name.charCodeAt(0);
|
|
223
|
+
const is_component = first_char >= A_UPPER && first_char <= Z_UPPER;
|
|
224
|
+
const node_type: MdzNodeTypeContainer = is_component ? 'Component' : 'Element';
|
|
225
|
+
|
|
226
|
+
flush_text(state);
|
|
227
|
+
ensure_paragraph(state);
|
|
228
|
+
const id = alloc_id(state);
|
|
229
|
+
const tag_start = offset(state, start);
|
|
230
|
+
const delimiter = state.buffer.slice(start, i); // e.g., "<Alert>"
|
|
231
|
+
emit(state, {type: 'open', id, node_type, start: tag_start, name});
|
|
232
|
+
push_stack_entry(state, id, node_type, tag_start, delimiter, name);
|
|
233
|
+
state.active_text_id = null;
|
|
234
|
+
state.pos = i;
|
|
235
|
+
state.column += i - start;
|
|
236
|
+
state.prev_char = RIGHT_ANGLE;
|
|
237
|
+
return 'consumed';
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
/** @nodocs */
|
|
241
|
+
export const try_close_tag = (state: MdzStreamParserState): TryResult => {
|
|
242
|
+
// we're at </
|
|
243
|
+
let i = state.pos + 2; // past </
|
|
244
|
+
|
|
245
|
+
if (i >= state.buffer.length) return 'need_more';
|
|
246
|
+
|
|
247
|
+
// collect tag name
|
|
248
|
+
const name_start = i;
|
|
249
|
+
while (i < state.buffer.length && is_tag_name_char(state.buffer.charCodeAt(i))) {
|
|
250
|
+
i++;
|
|
251
|
+
}
|
|
252
|
+
if (i >= state.buffer.length) return 'need_more';
|
|
253
|
+
if (state.buffer.charCodeAt(i) !== RIGHT_ANGLE) return 'not_match';
|
|
254
|
+
|
|
255
|
+
const name = state.buffer.slice(name_start, i);
|
|
256
|
+
i++; // past >
|
|
257
|
+
|
|
258
|
+
// find matching open tag on stack
|
|
259
|
+
let found_idx = -1;
|
|
260
|
+
for (let j = state.stack.length - 1; j >= 0; j--) {
|
|
261
|
+
const entry = state.stack[j]!;
|
|
262
|
+
if (
|
|
263
|
+
(entry.node_type === 'Element' || entry.node_type === 'Component') &&
|
|
264
|
+
entry.tag_name === name
|
|
265
|
+
) {
|
|
266
|
+
found_idx = j;
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
if (is_run_boundary(entry.node_type)) break;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (found_idx === -1) return 'not_match';
|
|
273
|
+
|
|
274
|
+
flush_text(state);
|
|
275
|
+
revert_above(state, found_idx);
|
|
276
|
+
const entry = pop_stack_entry(state);
|
|
277
|
+
emit(state, {type: 'close', id: entry.id, end: offset(state, i)});
|
|
278
|
+
state.active_text_id = null;
|
|
279
|
+
state.column += i - state.pos;
|
|
280
|
+
state.pos = i;
|
|
281
|
+
state.prev_char = RIGHT_ANGLE;
|
|
282
|
+
return 'consumed';
|
|
283
|
+
};
|