@fuzdev/mdz 0.2.0 → 0.4.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 +4 -0
- package/dist/Mdz.svelte +18 -9
- package/dist/Mdz.svelte.d.ts +9 -2
- package/dist/Mdz.svelte.d.ts.map +1 -1
- package/dist/MdzNodeView.svelte +129 -115
- package/dist/MdzNodeView.svelte.d.ts +4 -1
- package/dist/MdzNodeView.svelte.d.ts.map +1 -1
- package/dist/MdzPrecompiled.svelte +3 -3
- package/dist/MdzPrecompiled.svelte.d.ts.map +1 -1
- package/dist/MdzRoot.svelte +3 -3
- package/dist/MdzRoot.svelte.d.ts.map +1 -1
- package/dist/MdzStream.svelte +6 -6
- package/dist/MdzStream.svelte.d.ts.map +1 -1
- package/dist/MdzStreamNodeView.svelte +129 -118
- package/dist/MdzStreamNodeView.svelte.d.ts +4 -1
- package/dist/MdzStreamNodeView.svelte.d.ts.map +1 -1
- package/dist/context_helpers.js +1 -1
- package/dist/mdz.d.ts +6 -0
- package/dist/mdz.d.ts.map +1 -1
- package/dist/mdz.js +8 -0
- package/dist/mdz_contexts.d.ts.map +1 -1
- package/dist/mdz_debug_work.d.ts +38 -0
- package/dist/mdz_debug_work.d.ts.map +1 -0
- package/dist/mdz_debug_work.js +37 -0
- package/dist/mdz_helpers.d.ts +61 -0
- package/dist/mdz_helpers.d.ts.map +1 -1
- package/dist/mdz_helpers.js +74 -2
- package/dist/mdz_lexer.d.ts +12 -0
- package/dist/mdz_lexer.d.ts.map +1 -1
- package/dist/mdz_lexer.js +190 -40
- package/dist/mdz_opcodes.d.ts.map +1 -1
- package/dist/mdz_opcodes_to_nodes.d.ts.map +1 -1
- package/dist/mdz_opcodes_to_nodes.js +22 -17
- package/dist/mdz_stream_parser.d.ts.map +1 -1
- package/dist/mdz_stream_parser.js +8 -8
- package/dist/mdz_stream_parser_block.js +5 -5
- package/dist/mdz_stream_parser_inline.d.ts.map +1 -1
- package/dist/mdz_stream_parser_inline.js +22 -20
- package/dist/mdz_stream_parser_link.d.ts.map +1 -1
- package/dist/mdz_stream_parser_link.js +88 -56
- package/dist/mdz_stream_parser_list.d.ts.map +1 -1
- package/dist/mdz_stream_parser_list.js +23 -9
- package/dist/mdz_stream_parser_state.d.ts +29 -0
- package/dist/mdz_stream_parser_state.d.ts.map +1 -1
- package/dist/mdz_stream_parser_state.js +59 -10
- package/dist/mdz_stream_parser_table.d.ts +3 -2
- package/dist/mdz_stream_parser_table.d.ts.map +1 -1
- package/dist/mdz_stream_parser_table.js +24 -13
- package/dist/mdz_stream_parser_text.d.ts.map +1 -1
- package/dist/mdz_stream_parser_text.js +36 -25
- package/dist/mdz_stream_parser_url.js +7 -7
- package/dist/mdz_stream_state.svelte.d.ts.map +1 -1
- package/dist/mdz_to_svelte.d.ts +1 -1
- package/dist/mdz_to_svelte.d.ts.map +1 -1
- package/dist/mdz_to_svelte.js +28 -31
- package/dist/mdz_token_parser.d.ts.map +1 -1
- package/dist/mdz_token_parser.js +24 -25
- package/dist/svelte_preprocess_mdz.d.ts +2 -2
- package/dist/svelte_preprocess_mdz.d.ts.map +1 -1
- package/dist/svelte_preprocess_mdz.js +41 -47
- package/package.json +28 -40
- package/src/lib/context_helpers.ts +2 -2
- package/src/lib/mdz.ts +11 -2
- package/src/lib/mdz_contexts.ts +5 -5
- package/src/lib/mdz_debug_work.ts +38 -0
- package/src/lib/mdz_helpers.ts +126 -23
- package/src/lib/mdz_lexer.ts +243 -93
- package/src/lib/mdz_opcodes.ts +1 -1
- package/src/lib/mdz_opcodes_to_nodes.ts +35 -30
- package/src/lib/mdz_stream_parser.ts +17 -17
- package/src/lib/mdz_stream_parser_block.ts +14 -14
- package/src/lib/mdz_stream_parser_inline.ts +38 -34
- package/src/lib/mdz_stream_parser_link.ts +99 -66
- package/src/lib/mdz_stream_parser_list.ts +41 -29
- package/src/lib/mdz_stream_parser_state.ts +100 -28
- package/src/lib/mdz_stream_parser_table.ts +47 -36
- package/src/lib/mdz_stream_parser_text.ts +40 -39
- package/src/lib/mdz_stream_parser_url.ts +11 -11
- package/src/lib/mdz_stream_state.svelte.ts +8 -8
- package/src/lib/mdz_to_svelte.ts +38 -46
- package/src/lib/mdz_token_parser.ts +55 -58
- package/src/lib/svelte_preprocess_mdz.ts +92 -89
- package/src/lib/tsdoc_mdz.ts +1 -1
package/src/lib/mdz_helpers.ts
CHANGED
|
@@ -7,7 +7,13 @@
|
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
MdzNode,
|
|
12
|
+
MdzNodeText,
|
|
13
|
+
MdzNodeComponent,
|
|
14
|
+
MdzNodeElement,
|
|
15
|
+
MdzTableAlign
|
|
16
|
+
} from './mdz.ts';
|
|
11
17
|
|
|
12
18
|
// Character codes for performance
|
|
13
19
|
/** @nodocs */
|
|
@@ -99,6 +105,23 @@ export const MIN_CODEBLOCK_BACKTICKS = 3; // Code blocks require minimum 3 backt
|
|
|
99
105
|
export const MAX_LIST_NUMBER_DIGITS = 9; // Ordered list markers cap at 9 digits (CommonMark's cap)
|
|
100
106
|
/** @nodocs */
|
|
101
107
|
export const MAX_HEADING_LEVEL = 6; // Headings support levels 1-6
|
|
108
|
+
/**
|
|
109
|
+
* Cap on inline link/tag nesting depth. Reached at this many open
|
|
110
|
+
* `Link`/`Element`/`Component` containers, a further `[` or `<tag>` opener
|
|
111
|
+
* renders as literal text in both parsers rather than opening — a strict
|
|
112
|
+
* untrusted-content dialect's bound on pathological nesting (CommonMark
|
|
113
|
+
* permits such a limit). It bounds both the sync lexer's recursion depth (no
|
|
114
|
+
* stack overflow) and, with the failure memo, its revert re-scan work (linear
|
|
115
|
+
* instead of quadratic) on adversarial nested `[`/`<`. Deliberately far above
|
|
116
|
+
* any real content (inline nesting rarely exceeds a handful); only pathological
|
|
117
|
+
* input reaches it. Mirrored in the streaming parser so the two agree on where
|
|
118
|
+
* the cap bites. Delimiters (`**`/`_`/`~~`/`` ` ``) are uncounted: first-closer-wins
|
|
119
|
+
* keeps them from nesting deeply on their own, and mixed delimiter/link chains
|
|
120
|
+
* only deep-nest through the links, which this bounds.
|
|
121
|
+
*
|
|
122
|
+
* @nodocs
|
|
123
|
+
*/
|
|
124
|
+
export const MAX_INLINE_NESTING_DEPTH = 100;
|
|
102
125
|
/** @nodocs */
|
|
103
126
|
export const HTTPS_PREFIX_LENGTH = 8; // Length of "https://"
|
|
104
127
|
/** @nodocs */
|
|
@@ -180,7 +203,7 @@ export interface MdzBlockquotePrefix {
|
|
|
180
203
|
export const mdz_match_blockquote_prefix = (
|
|
181
204
|
text: string,
|
|
182
205
|
i: number,
|
|
183
|
-
forced: boolean
|
|
206
|
+
forced: boolean
|
|
184
207
|
): MdzBlockquotePrefix | 'pending' | null => {
|
|
185
208
|
let depth = 0;
|
|
186
209
|
let pos = i;
|
|
@@ -190,23 +213,23 @@ export const mdz_match_blockquote_prefix = (
|
|
|
190
213
|
depth++;
|
|
191
214
|
pos++;
|
|
192
215
|
if (pos >= text.length) {
|
|
193
|
-
return forced ? {depth, content_start: pos} : 'pending';
|
|
216
|
+
return forced ? { depth, content_start: pos } : 'pending';
|
|
194
217
|
}
|
|
195
218
|
const c = text.charCodeAt(pos);
|
|
196
219
|
if (c === RIGHT_ANGLE) continue; // tight run
|
|
197
220
|
if (c === SPACE) {
|
|
198
221
|
if (pos + 1 >= text.length) {
|
|
199
222
|
// could be a separator (next chunk starts with `>`) or the terminator
|
|
200
|
-
return forced ? {depth, content_start: pos + 1} : 'pending';
|
|
223
|
+
return forced ? { depth, content_start: pos + 1 } : 'pending';
|
|
201
224
|
}
|
|
202
225
|
if (text.charCodeAt(pos + 1) === RIGHT_ANGLE) {
|
|
203
226
|
pos++; // separator — continue the run at the next `>`
|
|
204
227
|
continue;
|
|
205
228
|
}
|
|
206
|
-
return {depth, content_start: pos + 1}; // terminating space
|
|
229
|
+
return { depth, content_start: pos + 1 }; // terminating space
|
|
207
230
|
}
|
|
208
231
|
if (c === NEWLINE) {
|
|
209
|
-
return {depth, content_start: pos}; // bare prefix line
|
|
232
|
+
return { depth, content_start: pos }; // bare prefix line
|
|
210
233
|
}
|
|
211
234
|
if (is_line_whitespace(c)) {
|
|
212
235
|
// tab/CR is not the marker space — valid only as tolerated trailing
|
|
@@ -216,17 +239,17 @@ export const mdz_match_blockquote_prefix = (
|
|
|
216
239
|
j++;
|
|
217
240
|
}
|
|
218
241
|
if (j >= text.length) {
|
|
219
|
-
return forced ? {depth, content_start: pos} : 'pending';
|
|
242
|
+
return forced ? { depth, content_start: pos } : 'pending';
|
|
220
243
|
}
|
|
221
244
|
if (text.charCodeAt(j) === NEWLINE) {
|
|
222
|
-
return {depth, content_start: pos};
|
|
245
|
+
return { depth, content_start: pos };
|
|
223
246
|
}
|
|
224
247
|
// trailing content after non-space whitespace — fall through to backtrack
|
|
225
248
|
}
|
|
226
249
|
// invalid char directly after a `>`: this `>` is content; the separator
|
|
227
250
|
// space before it (if any) re-serves as the terminator
|
|
228
251
|
if (depth >= 2 && text.charCodeAt(pos - 2) === SPACE) {
|
|
229
|
-
return {depth: depth - 1, content_start: pos - 1};
|
|
252
|
+
return { depth: depth - 1, content_start: pos - 1 };
|
|
230
253
|
}
|
|
231
254
|
return null;
|
|
232
255
|
}
|
|
@@ -243,7 +266,7 @@ export const mdz_match_blockquote_prefix = (
|
|
|
243
266
|
export const mdz_blockquote_line_has_content = (
|
|
244
267
|
text: string,
|
|
245
268
|
content_start: number,
|
|
246
|
-
forced: boolean
|
|
269
|
+
forced: boolean
|
|
247
270
|
): boolean | 'pending' => {
|
|
248
271
|
let i = content_start;
|
|
249
272
|
while (i < text.length && is_line_whitespace(text.charCodeAt(i))) {
|
|
@@ -274,8 +297,8 @@ export const mdz_blockquote_strip_width = (text: string, i: number): number =>
|
|
|
274
297
|
* @nodocs
|
|
275
298
|
*/
|
|
276
299
|
export const mdz_remap_segments = (
|
|
277
|
-
segments: Array<{inner_start: number; source_start: number}>,
|
|
278
|
-
p: number
|
|
300
|
+
segments: Array<{ inner_start: number; source_start: number }>,
|
|
301
|
+
p: number
|
|
279
302
|
): number => {
|
|
280
303
|
let lo = 0;
|
|
281
304
|
let hi = segments.length - 1;
|
|
@@ -297,13 +320,13 @@ export const mdz_remap_segments = (
|
|
|
297
320
|
const trim_table_cell = (
|
|
298
321
|
text: string,
|
|
299
322
|
start: number,
|
|
300
|
-
end: number
|
|
301
|
-
): {start: number; end: number} => {
|
|
323
|
+
end: number
|
|
324
|
+
): { start: number; end: number } => {
|
|
302
325
|
let s = start;
|
|
303
326
|
let e = end;
|
|
304
327
|
while (s < e && is_line_whitespace(text.charCodeAt(s))) s++;
|
|
305
328
|
while (e > s && is_line_whitespace(text.charCodeAt(e - 1))) e--;
|
|
306
|
-
return {start: s, end: e};
|
|
329
|
+
return { start: s, end: e };
|
|
307
330
|
};
|
|
308
331
|
|
|
309
332
|
/**
|
|
@@ -326,10 +349,10 @@ const trim_table_cell = (
|
|
|
326
349
|
export const mdz_split_table_row = (
|
|
327
350
|
text: string,
|
|
328
351
|
line_start: number,
|
|
329
|
-
line_end: number
|
|
330
|
-
): Array<{start: number; end: number}> | null => {
|
|
352
|
+
line_end: number
|
|
353
|
+
): Array<{ start: number; end: number }> | null => {
|
|
331
354
|
if (line_start >= line_end || text.charCodeAt(line_start) !== PIPE) return null;
|
|
332
|
-
const cells: Array<{start: number; end: number}> = [];
|
|
355
|
+
const cells: Array<{ start: number; end: number }> = [];
|
|
333
356
|
let i = line_start + 1; // past the leading pipe
|
|
334
357
|
let cell_start = i;
|
|
335
358
|
while (i < line_end) {
|
|
@@ -398,7 +421,7 @@ const table_delimiter_align = (text: string, start: number, end: number): MdzTab
|
|
|
398
421
|
export const mdz_parse_table_delimiter = (
|
|
399
422
|
text: string,
|
|
400
423
|
line_start: number,
|
|
401
|
-
line_end: number
|
|
424
|
+
line_end: number
|
|
402
425
|
): Array<MdzTableAlign> | null => {
|
|
403
426
|
const cells = mdz_split_table_row(text, line_start, line_end);
|
|
404
427
|
if (cells === null) return null;
|
|
@@ -547,6 +570,40 @@ const PATH_CHAR_TABLE: Uint8Array = (() => {
|
|
|
547
570
|
export const is_valid_path_char = (char_code: number): boolean =>
|
|
548
571
|
char_code < 128 && PATH_CHAR_TABLE[char_code] === 1;
|
|
549
572
|
|
|
573
|
+
// Character classes for the plain-text run scanners (the hottest loops in
|
|
574
|
+
// both parsers) — one table load per character instead of a comparison chain.
|
|
575
|
+
// A zero class is the common case: the char can't end or interrupt a text run.
|
|
576
|
+
|
|
577
|
+
/** Always ends a text run: `` ` `` `*` `_` `~` `[` `]` `<`. @nodocs */
|
|
578
|
+
export const TEXT_CLASS_STOP = 1;
|
|
579
|
+
/** `\n` — run handling is context-dependent (soft break, paragraph break, block lookahead). @nodocs */
|
|
580
|
+
export const TEXT_CLASS_NEWLINE = 2;
|
|
581
|
+
/** `h`/`H` — potential URL scheme start, needs the prefix probe. @nodocs */
|
|
582
|
+
export const TEXT_CLASS_URL = 4;
|
|
583
|
+
/** `/` and `.` — potential path start, needs the boundary probe. @nodocs */
|
|
584
|
+
export const TEXT_CLASS_PATH = 8;
|
|
585
|
+
/** `\` — a literal-pipe escape candidate inside table cells only. @nodocs */
|
|
586
|
+
export const TEXT_CLASS_BACKSLASH = 16;
|
|
587
|
+
|
|
588
|
+
/** @nodocs */
|
|
589
|
+
export const TEXT_SCAN_CLASS: Uint8Array = (() => {
|
|
590
|
+
const t = new Uint8Array(128);
|
|
591
|
+
t[BACKTICK] = TEXT_CLASS_STOP;
|
|
592
|
+
t[ASTERISK] = TEXT_CLASS_STOP;
|
|
593
|
+
t[UNDERSCORE] = TEXT_CLASS_STOP;
|
|
594
|
+
t[TILDE] = TEXT_CLASS_STOP;
|
|
595
|
+
t[LEFT_BRACKET] = TEXT_CLASS_STOP;
|
|
596
|
+
t[RIGHT_BRACKET] = TEXT_CLASS_STOP;
|
|
597
|
+
t[LEFT_ANGLE] = TEXT_CLASS_STOP;
|
|
598
|
+
t[NEWLINE] = TEXT_CLASS_NEWLINE;
|
|
599
|
+
t[H_LOWER] = TEXT_CLASS_URL;
|
|
600
|
+
t[H_UPPER] = TEXT_CLASS_URL;
|
|
601
|
+
t[SLASH] = TEXT_CLASS_PATH;
|
|
602
|
+
t[PERIOD] = TEXT_CLASS_PATH;
|
|
603
|
+
t[BACKSLASH] = TEXT_CLASS_BACKSLASH;
|
|
604
|
+
return t;
|
|
605
|
+
})();
|
|
606
|
+
|
|
550
607
|
/**
|
|
551
608
|
* Trim trailing punctuation from URL/path per RFC 3986 and GFM rules.
|
|
552
609
|
* - Trims simple trailing: .,;:!?]
|
|
@@ -718,7 +775,7 @@ const VOID_ELEMENTS = new Set([
|
|
|
718
775
|
'meta',
|
|
719
776
|
'source',
|
|
720
777
|
'track',
|
|
721
|
-
'wbr'
|
|
778
|
+
'wbr'
|
|
722
779
|
]);
|
|
723
780
|
|
|
724
781
|
/**
|
|
@@ -760,6 +817,52 @@ export const mdz_resolve_relative_path = (reference: string, base: string): stri
|
|
|
760
817
|
return segments.join('/');
|
|
761
818
|
};
|
|
762
819
|
|
|
820
|
+
/**
|
|
821
|
+
* How a `Link` node's `reference` should render, shared by all three renderers
|
|
822
|
+
* (`MdzNodeView`, `MdzStreamNodeView`, `mdz_to_svelte`) so the safety gate and
|
|
823
|
+
* the resolve-vs-raw classification can't drift between them.
|
|
824
|
+
* @nodocs
|
|
825
|
+
*/
|
|
826
|
+
export type MdzLinkRender =
|
|
827
|
+
/** Unsafe protocol — render children only, no `<a>`. */
|
|
828
|
+
| { kind: 'unsafe' }
|
|
829
|
+
/** Route/relative path — wrap in `resolve()` (needs `$app/paths`). */
|
|
830
|
+
| { kind: 'resolve'; href: string }
|
|
831
|
+
/** Internal fragment/query/relative/bare ref — raw `href`, no `resolve()`. */
|
|
832
|
+
| { kind: 'internal'; href: string }
|
|
833
|
+
/** External link — raw `href` plus `target="_blank" rel="noopener"`. */
|
|
834
|
+
| { kind: 'external'; href: string };
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* Classify a `Link` reference into how it should render. Pure — no rendering,
|
|
838
|
+
* no context; `base` is the resolved base path (or `undefined`).
|
|
839
|
+
* @nodocs
|
|
840
|
+
*/
|
|
841
|
+
export const mdz_classify_link = (
|
|
842
|
+
reference: string,
|
|
843
|
+
link_type: 'internal' | 'external' | undefined,
|
|
844
|
+
base: string | undefined
|
|
845
|
+
): MdzLinkRender => {
|
|
846
|
+
if (!mdz_is_safe_reference(reference)) return { kind: 'unsafe' };
|
|
847
|
+
if (link_type === 'internal') {
|
|
848
|
+
if (reference.startsWith('.') && base) {
|
|
849
|
+
return { kind: 'resolve', href: mdz_resolve_relative_path(reference, base) };
|
|
850
|
+
}
|
|
851
|
+
// fragment/query/relative/bare — `resolve()` only accepts absolute paths or
|
|
852
|
+
// route ids and throws on anything else
|
|
853
|
+
if (
|
|
854
|
+
reference.startsWith('#') ||
|
|
855
|
+
reference.startsWith('?') ||
|
|
856
|
+
reference.startsWith('.') ||
|
|
857
|
+
!reference.startsWith('/')
|
|
858
|
+
) {
|
|
859
|
+
return { kind: 'internal', href: reference };
|
|
860
|
+
}
|
|
861
|
+
return { kind: 'resolve', href: reference };
|
|
862
|
+
}
|
|
863
|
+
return { kind: 'external', href: reference };
|
|
864
|
+
};
|
|
865
|
+
|
|
763
866
|
/**
|
|
764
867
|
* Push a node into a children array, coalescing with the previous Text node.
|
|
765
868
|
* Mutates `prev.content` and `prev.end` when both are Text, avoiding array growth
|
|
@@ -804,10 +907,10 @@ export const mdz_merge_adjacent_text = (nodes: Array<MdzNode>): Array<MdzNode> =
|
|
|
804
907
|
type: 'Text',
|
|
805
908
|
content: pending.content + node.content,
|
|
806
909
|
start: pending.start,
|
|
807
|
-
end: node.end
|
|
910
|
+
end: node.end
|
|
808
911
|
};
|
|
809
912
|
} else {
|
|
810
|
-
pending = {...node};
|
|
913
|
+
pending = { ...node };
|
|
811
914
|
}
|
|
812
915
|
} else {
|
|
813
916
|
if (pending) {
|
|
@@ -830,7 +933,7 @@ export const mdz_merge_adjacent_text = (nodes: Array<MdzNode>): Array<MdzNode> =
|
|
|
830
933
|
* or `null` if the content should be wrapped in a paragraph
|
|
831
934
|
*/
|
|
832
935
|
export const mdz_extract_single_tag = (
|
|
833
|
-
nodes: Array<MdzNode
|
|
936
|
+
nodes: Array<MdzNode>
|
|
834
937
|
): MdzNodeComponent | MdzNodeElement | null => {
|
|
835
938
|
let tag: MdzNodeComponent | MdzNodeElement | null = null;
|
|
836
939
|
|