@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/dist/mdz_helpers.js
CHANGED
|
@@ -96,6 +96,23 @@ export const MIN_CODEBLOCK_BACKTICKS = 3; // Code blocks require minimum 3 backt
|
|
|
96
96
|
export const MAX_LIST_NUMBER_DIGITS = 9; // Ordered list markers cap at 9 digits (CommonMark's cap)
|
|
97
97
|
/** @nodocs */
|
|
98
98
|
export const MAX_HEADING_LEVEL = 6; // Headings support levels 1-6
|
|
99
|
+
/**
|
|
100
|
+
* Cap on inline link/tag nesting depth. Reached at this many open
|
|
101
|
+
* `Link`/`Element`/`Component` containers, a further `[` or `<tag>` opener
|
|
102
|
+
* renders as literal text in both parsers rather than opening — a strict
|
|
103
|
+
* untrusted-content dialect's bound on pathological nesting (CommonMark
|
|
104
|
+
* permits such a limit). It bounds both the sync lexer's recursion depth (no
|
|
105
|
+
* stack overflow) and, with the failure memo, its revert re-scan work (linear
|
|
106
|
+
* instead of quadratic) on adversarial nested `[`/`<`. Deliberately far above
|
|
107
|
+
* any real content (inline nesting rarely exceeds a handful); only pathological
|
|
108
|
+
* input reaches it. Mirrored in the streaming parser so the two agree on where
|
|
109
|
+
* the cap bites. Delimiters (`**`/`_`/`~~`/`` ` ``) are uncounted: first-closer-wins
|
|
110
|
+
* keeps them from nesting deeply on their own, and mixed delimiter/link chains
|
|
111
|
+
* only deep-nest through the links, which this bounds.
|
|
112
|
+
*
|
|
113
|
+
* @nodocs
|
|
114
|
+
*/
|
|
115
|
+
export const MAX_INLINE_NESTING_DEPTH = 100;
|
|
99
116
|
/** @nodocs */
|
|
100
117
|
export const HTTPS_PREFIX_LENGTH = 8; // Length of "https://"
|
|
101
118
|
/** @nodocs */
|
|
@@ -495,6 +512,37 @@ const PATH_CHAR_TABLE = (() => {
|
|
|
495
512
|
})();
|
|
496
513
|
/** @nodocs */
|
|
497
514
|
export const is_valid_path_char = (char_code) => char_code < 128 && PATH_CHAR_TABLE[char_code] === 1;
|
|
515
|
+
// Character classes for the plain-text run scanners (the hottest loops in
|
|
516
|
+
// both parsers) — one table load per character instead of a comparison chain.
|
|
517
|
+
// A zero class is the common case: the char can't end or interrupt a text run.
|
|
518
|
+
/** Always ends a text run: `` ` `` `*` `_` `~` `[` `]` `<`. @nodocs */
|
|
519
|
+
export const TEXT_CLASS_STOP = 1;
|
|
520
|
+
/** `\n` — run handling is context-dependent (soft break, paragraph break, block lookahead). @nodocs */
|
|
521
|
+
export const TEXT_CLASS_NEWLINE = 2;
|
|
522
|
+
/** `h`/`H` — potential URL scheme start, needs the prefix probe. @nodocs */
|
|
523
|
+
export const TEXT_CLASS_URL = 4;
|
|
524
|
+
/** `/` and `.` — potential path start, needs the boundary probe. @nodocs */
|
|
525
|
+
export const TEXT_CLASS_PATH = 8;
|
|
526
|
+
/** `\` — a literal-pipe escape candidate inside table cells only. @nodocs */
|
|
527
|
+
export const TEXT_CLASS_BACKSLASH = 16;
|
|
528
|
+
/** @nodocs */
|
|
529
|
+
export const TEXT_SCAN_CLASS = (() => {
|
|
530
|
+
const t = new Uint8Array(128);
|
|
531
|
+
t[BACKTICK] = TEXT_CLASS_STOP;
|
|
532
|
+
t[ASTERISK] = TEXT_CLASS_STOP;
|
|
533
|
+
t[UNDERSCORE] = TEXT_CLASS_STOP;
|
|
534
|
+
t[TILDE] = TEXT_CLASS_STOP;
|
|
535
|
+
t[LEFT_BRACKET] = TEXT_CLASS_STOP;
|
|
536
|
+
t[RIGHT_BRACKET] = TEXT_CLASS_STOP;
|
|
537
|
+
t[LEFT_ANGLE] = TEXT_CLASS_STOP;
|
|
538
|
+
t[NEWLINE] = TEXT_CLASS_NEWLINE;
|
|
539
|
+
t[H_LOWER] = TEXT_CLASS_URL;
|
|
540
|
+
t[H_UPPER] = TEXT_CLASS_URL;
|
|
541
|
+
t[SLASH] = TEXT_CLASS_PATH;
|
|
542
|
+
t[PERIOD] = TEXT_CLASS_PATH;
|
|
543
|
+
t[BACKSLASH] = TEXT_CLASS_BACKSLASH;
|
|
544
|
+
return t;
|
|
545
|
+
})();
|
|
498
546
|
/**
|
|
499
547
|
* Trim trailing punctuation from URL/path per RFC 3986 and GFM rules.
|
|
500
548
|
* - Trims simple trailing: .,;:!?]
|
|
@@ -655,7 +703,7 @@ const VOID_ELEMENTS = new Set([
|
|
|
655
703
|
'meta',
|
|
656
704
|
'source',
|
|
657
705
|
'track',
|
|
658
|
-
'wbr'
|
|
706
|
+
'wbr'
|
|
659
707
|
]);
|
|
660
708
|
/**
|
|
661
709
|
* Check if an element name is an HTML void element (`br`, `img`, `hr`, ...).
|
|
@@ -699,6 +747,30 @@ export const mdz_resolve_relative_path = (reference, base) => {
|
|
|
699
747
|
segments.push('');
|
|
700
748
|
return segments.join('/');
|
|
701
749
|
};
|
|
750
|
+
/**
|
|
751
|
+
* Classify a `Link` reference into how it should render. Pure — no rendering,
|
|
752
|
+
* no context; `base` is the resolved base path (or `undefined`).
|
|
753
|
+
* @nodocs
|
|
754
|
+
*/
|
|
755
|
+
export const mdz_classify_link = (reference, link_type, base) => {
|
|
756
|
+
if (!mdz_is_safe_reference(reference))
|
|
757
|
+
return { kind: 'unsafe' };
|
|
758
|
+
if (link_type === 'internal') {
|
|
759
|
+
if (reference.startsWith('.') && base) {
|
|
760
|
+
return { kind: 'resolve', href: mdz_resolve_relative_path(reference, base) };
|
|
761
|
+
}
|
|
762
|
+
// fragment/query/relative/bare — `resolve()` only accepts absolute paths or
|
|
763
|
+
// route ids and throws on anything else
|
|
764
|
+
if (reference.startsWith('#') ||
|
|
765
|
+
reference.startsWith('?') ||
|
|
766
|
+
reference.startsWith('.') ||
|
|
767
|
+
!reference.startsWith('/')) {
|
|
768
|
+
return { kind: 'internal', href: reference };
|
|
769
|
+
}
|
|
770
|
+
return { kind: 'resolve', href: reference };
|
|
771
|
+
}
|
|
772
|
+
return { kind: 'external', href: reference };
|
|
773
|
+
};
|
|
702
774
|
/**
|
|
703
775
|
* Push a node into a children array, coalescing with the previous Text node.
|
|
704
776
|
* Mutates `prev.content` and `prev.end` when both are Text, avoiding array growth
|
|
@@ -741,7 +813,7 @@ export const mdz_merge_adjacent_text = (nodes) => {
|
|
|
741
813
|
type: 'Text',
|
|
742
814
|
content: pending.content + node.content,
|
|
743
815
|
start: pending.start,
|
|
744
|
-
end: node.end
|
|
816
|
+
end: node.end
|
|
745
817
|
};
|
|
746
818
|
}
|
|
747
819
|
else {
|
package/dist/mdz_lexer.d.ts
CHANGED
|
@@ -141,6 +141,18 @@ export interface MdzTokenTableCellClose extends MdzTokenBase {
|
|
|
141
141
|
export declare class MdzLexer {
|
|
142
142
|
#private;
|
|
143
143
|
constructor(text: string);
|
|
144
|
+
/**
|
|
145
|
+
* Rebind to a new text, so one lexer instance can be reused across inputs —
|
|
146
|
+
* the streaming table path reuses one across a table's rows via
|
|
147
|
+
* `MdzTableCellParser` instead of allocating a lexer (and its memo `Map`)
|
|
148
|
+
* per row. The search memos survive when the text is identical (they're
|
|
149
|
+
* valid over the immutable text — the common case for rows parsed from one
|
|
150
|
+
* buffered chunk) and clear otherwise. Per-parse cursor state is reset by
|
|
151
|
+
* `lex_table_cell` itself.
|
|
152
|
+
*
|
|
153
|
+
* @nodocs
|
|
154
|
+
*/
|
|
155
|
+
rebind(text: string): void;
|
|
144
156
|
tokenize(): Array<MdzToken>;
|
|
145
157
|
/**
|
|
146
158
|
* Tokenize a table cell's inline content `[cell_start, cell_end)` as a
|
package/dist/mdz_lexer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mdz_lexer.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_lexer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"mdz_lexer.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_lexer.ts"],"names":[],"mappings":"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"}
|
package/dist/mdz_lexer.js
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
|
-
import { mdz_is_url, mdz_is_safe_reference, is_letter, is_tag_name_char, is_word_char, is_valid_path_char, mdz_trim_url_punctuation, BACKTICK, ASTERISK, UNDERSCORE, TILDE, NEWLINE, HYPHEN, HASH, SPACE, LEFT_ANGLE, RIGHT_ANGLE, SLASH, PERIOD, LEFT_BRACKET, LEFT_PAREN, RIGHT_BRACKET, A_UPPER, Z_UPPER, ZERO, ONE, NINE, HR_HYPHEN_COUNT, MIN_CODEBLOCK_BACKTICKS, MAX_HEADING_LEVEL, MAX_LIST_NUMBER_DIGITS, match_url_prefix_case_insensitive, is_at_absolute_path, is_at_relative_path, is_line_whitespace, mdz_match_blockquote_prefix, mdz_blockquote_line_has_content, mdz_blockquote_strip_width, mdz_remap_segments, mdz_split_table_row, mdz_parse_table_delimiter, PIPE,
|
|
9
|
+
import { mdz_is_url, mdz_is_safe_reference, is_letter, is_tag_name_char, is_word_char, is_valid_path_char, mdz_trim_url_punctuation, BACKTICK, ASTERISK, UNDERSCORE, TILDE, NEWLINE, HYPHEN, HASH, SPACE, LEFT_ANGLE, RIGHT_ANGLE, SLASH, PERIOD, LEFT_BRACKET, LEFT_PAREN, RIGHT_BRACKET, A_UPPER, Z_UPPER, ZERO, ONE, NINE, HR_HYPHEN_COUNT, MIN_CODEBLOCK_BACKTICKS, MAX_HEADING_LEVEL, MAX_INLINE_NESTING_DEPTH, MAX_LIST_NUMBER_DIGITS, match_url_prefix_case_insensitive, is_at_absolute_path, is_at_relative_path, is_line_whitespace, mdz_match_blockquote_prefix, mdz_blockquote_line_has_content, mdz_blockquote_strip_width, mdz_remap_segments, mdz_split_table_row, mdz_parse_table_delimiter, PIPE, TEXT_CLASS_BACKSLASH, TEXT_CLASS_NEWLINE, TEXT_CLASS_STOP, TEXT_CLASS_URL, TEXT_SCAN_CLASS } from './mdz_helpers.js';
|
|
10
|
+
import { mdz_debug_work } from './mdz_debug_work.js';
|
|
10
11
|
//
|
|
11
12
|
// Lexer
|
|
12
13
|
//
|
|
@@ -39,9 +40,67 @@ export class MdzLexer {
|
|
|
39
40
|
* and `\|` unescapes to a literal pipe.
|
|
40
41
|
*/
|
|
41
42
|
#in_table_cell = false;
|
|
43
|
+
/**
|
|
44
|
+
* Start offsets of markdown-link opens (`[`) that definitively failed — so
|
|
45
|
+
* a re-lex from before the `[` short-circuits to literal text instead of
|
|
46
|
+
* re-attempting (and re-descending into) the same failing link. Without it,
|
|
47
|
+
* the revert-and-re-tokenize failure path is exponential on nested `[`
|
|
48
|
+
* (a ~30-byte `[`×n input hangs). A link's outcome (does a valid `](ref)`
|
|
49
|
+
* follow?) is independent of `#max_search_index`, so `start` alone keys it.
|
|
50
|
+
* The outcome is a pure function of the immutable text, so the memo is valid
|
|
51
|
+
* for the whole lex and across table-cell ranges; cleared on `rebind`.
|
|
52
|
+
*/
|
|
53
|
+
#failed_link_starts = new Set();
|
|
54
|
+
/**
|
|
55
|
+
* The tag analogue of `#failed_link_starts`, keyed `${start}:${bound}`. A
|
|
56
|
+
* tag's closer search respects `#max_search_index`, so the same `start` can
|
|
57
|
+
* fail under a narrow bound yet succeed under the wider one a re-lex uses —
|
|
58
|
+
* the bound is part of the key.
|
|
59
|
+
*/
|
|
60
|
+
#failed_tag = new Set();
|
|
61
|
+
/**
|
|
62
|
+
* Current link/tag nesting depth — the number of open
|
|
63
|
+
* `link_text_open`/`tag_open` containers being recursed into. Bumped around
|
|
64
|
+
* `#tokenize_markdown_link`/`#tokenize_tag`'s children loops (not by
|
|
65
|
+
* delimiters, which can't nest deeply — see `MAX_INLINE_NESTING_DEPTH`). At
|
|
66
|
+
* the cap a further `[`/`<` renders literal instead of opening, bounding
|
|
67
|
+
* recursion depth (no stack overflow) and, with the failure memos, the
|
|
68
|
+
* revert re-scan (linear, not quadratic) on adversarial nesting.
|
|
69
|
+
*/
|
|
70
|
+
#inline_depth = 0;
|
|
71
|
+
/**
|
|
72
|
+
* Start offsets of `[`/`<` openers suppressed by the nesting-depth cap
|
|
73
|
+
* (`#inline_depth >= MAX_INLINE_NESTING_DEPTH`) — position-keyed like
|
|
74
|
+
* `#failed_link_starts` so a re-lex from a shallower depth short-circuits to
|
|
75
|
+
* literal text rather than re-opening the construct (which would reintroduce
|
|
76
|
+
* the quadratic the cap removes). A suppression is bound-independent, so
|
|
77
|
+
* `start` alone keys it; cleared on `rebind`.
|
|
78
|
+
*/
|
|
79
|
+
#depth_capped = new Set();
|
|
42
80
|
constructor(text) {
|
|
43
81
|
this.#text = text;
|
|
44
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Rebind to a new text, so one lexer instance can be reused across inputs —
|
|
85
|
+
* the streaming table path reuses one across a table's rows via
|
|
86
|
+
* `MdzTableCellParser` instead of allocating a lexer (and its memo `Map`)
|
|
87
|
+
* per row. The search memos survive when the text is identical (they're
|
|
88
|
+
* valid over the immutable text — the common case for rows parsed from one
|
|
89
|
+
* buffered chunk) and clear otherwise. Per-parse cursor state is reset by
|
|
90
|
+
* `lex_table_cell` itself.
|
|
91
|
+
*
|
|
92
|
+
* @nodocs
|
|
93
|
+
*/
|
|
94
|
+
rebind(text) {
|
|
95
|
+
if (this.#text === text)
|
|
96
|
+
return;
|
|
97
|
+
this.#text = text;
|
|
98
|
+
this.#search_memo.clear();
|
|
99
|
+
this.#break_memo = null;
|
|
100
|
+
this.#failed_link_starts.clear();
|
|
101
|
+
this.#failed_tag.clear();
|
|
102
|
+
this.#depth_capped.clear();
|
|
103
|
+
}
|
|
45
104
|
/**
|
|
46
105
|
* Memoized `String#indexOf` over the immutable text.
|
|
47
106
|
*
|
|
@@ -58,6 +117,10 @@ export class MdzLexer {
|
|
|
58
117
|
return memo.result;
|
|
59
118
|
}
|
|
60
119
|
const result = this.#text.indexOf(needle, from);
|
|
120
|
+
if (mdz_debug_work.enabled) {
|
|
121
|
+
// scan span: `indexOf` examined [from, result] (found) or [from, EOF)
|
|
122
|
+
mdz_debug_work.total += (result === -1 ? this.#text.length : result) - from;
|
|
123
|
+
}
|
|
61
124
|
if (memo === undefined) {
|
|
62
125
|
this.#search_memo.set(needle, { from, result });
|
|
63
126
|
}
|
|
@@ -110,6 +173,9 @@ export class MdzLexer {
|
|
|
110
173
|
*/
|
|
111
174
|
lex_table_cell(cell_start, cell_end) {
|
|
112
175
|
this.#tokens = [];
|
|
176
|
+
// depth is balanced back to 0 per cell, but pin it so a future early-return
|
|
177
|
+
// between an `#inline_depth++` and its decrement can't leak into the next cell
|
|
178
|
+
this.#inline_depth = 0;
|
|
113
179
|
this.#tokenize_cell_inline(cell_start, cell_end);
|
|
114
180
|
return this.#tokens;
|
|
115
181
|
}
|
|
@@ -195,7 +261,7 @@ export class MdzLexer {
|
|
|
195
261
|
type: 'heading_start',
|
|
196
262
|
level: hash_count,
|
|
197
263
|
start,
|
|
198
|
-
end: this.#index
|
|
264
|
+
end: this.#index // end of "## " prefix
|
|
199
265
|
});
|
|
200
266
|
// Find end-of-line to bound nested tokenizers (prevents tag scanner from scanning past heading)
|
|
201
267
|
let eol = this.#text.indexOf('\n', this.#index);
|
|
@@ -498,7 +564,12 @@ export class MdzLexer {
|
|
|
498
564
|
*/
|
|
499
565
|
#tokenize_item_table(first, marker_indent) {
|
|
500
566
|
const head = this.#match_item_table_head(first, marker_indent);
|
|
501
|
-
this.#tokens.push({
|
|
567
|
+
this.#tokens.push({
|
|
568
|
+
type: 'table_open',
|
|
569
|
+
align: head.align,
|
|
570
|
+
start: first,
|
|
571
|
+
end: head.header_end
|
|
572
|
+
});
|
|
502
573
|
this.#emit_table_row(head.header_cells, true, first, head.header_end);
|
|
503
574
|
this.#index = head.delim_end;
|
|
504
575
|
let table_end = head.delim_end;
|
|
@@ -708,7 +779,7 @@ export class MdzLexer {
|
|
|
708
779
|
start: i,
|
|
709
780
|
marker_end: after,
|
|
710
781
|
content_start: after,
|
|
711
|
-
empty: true
|
|
782
|
+
empty: true
|
|
712
783
|
};
|
|
713
784
|
}
|
|
714
785
|
if (this.#text.charCodeAt(after) !== SPACE)
|
|
@@ -737,7 +808,7 @@ export class MdzLexer {
|
|
|
737
808
|
start: i,
|
|
738
809
|
marker_end: after,
|
|
739
810
|
content_start: after,
|
|
740
|
-
empty: true
|
|
811
|
+
empty: true
|
|
741
812
|
};
|
|
742
813
|
}
|
|
743
814
|
if (this.#text.charCodeAt(after) !== SPACE)
|
|
@@ -777,7 +848,7 @@ export class MdzLexer {
|
|
|
777
848
|
type: 'list_open',
|
|
778
849
|
ordered: marker.ordered,
|
|
779
850
|
start: marker.start,
|
|
780
|
-
end: marker.start
|
|
851
|
+
end: marker.start
|
|
781
852
|
});
|
|
782
853
|
this.#list_item_open(marker);
|
|
783
854
|
levels.push({ indent, ordered: marker.ordered });
|
|
@@ -786,7 +857,12 @@ export class MdzLexer {
|
|
|
786
857
|
#list_item_open(marker) {
|
|
787
858
|
const end = marker.empty ? marker.marker_end : marker.content_start;
|
|
788
859
|
if (marker.ordered) {
|
|
789
|
-
this.#tokens.push({
|
|
860
|
+
this.#tokens.push({
|
|
861
|
+
type: 'list_item_open',
|
|
862
|
+
number: marker.number,
|
|
863
|
+
start: marker.start,
|
|
864
|
+
end
|
|
865
|
+
});
|
|
790
866
|
}
|
|
791
867
|
else {
|
|
792
868
|
this.#tokens.push({ type: 'list_item_open', start: marker.start, end });
|
|
@@ -946,7 +1022,7 @@ export class MdzLexer {
|
|
|
946
1022
|
: content_lines.join('\n');
|
|
947
1023
|
return {
|
|
948
1024
|
token: { type: 'codeblock', lang, content, start: first, end: m },
|
|
949
|
-
end: m
|
|
1025
|
+
end: m
|
|
950
1026
|
};
|
|
951
1027
|
}
|
|
952
1028
|
}
|
|
@@ -968,7 +1044,7 @@ export class MdzLexer {
|
|
|
968
1044
|
content = content.slice(0, -1);
|
|
969
1045
|
return {
|
|
970
1046
|
token: { type: 'codeblock', lang, content, start: first, end: this.#text.length },
|
|
971
|
-
end: this.#text.length
|
|
1047
|
+
end: this.#text.length
|
|
972
1048
|
};
|
|
973
1049
|
}
|
|
974
1050
|
/**
|
|
@@ -1178,7 +1254,7 @@ export class MdzLexer {
|
|
|
1178
1254
|
this.#tokens.push({
|
|
1179
1255
|
type: 'italic_close',
|
|
1180
1256
|
start: close_index,
|
|
1181
|
-
end: close_index + 1
|
|
1257
|
+
end: close_index + 1
|
|
1182
1258
|
});
|
|
1183
1259
|
this.#index = close_index + 1;
|
|
1184
1260
|
}
|
|
@@ -1188,18 +1264,36 @@ export class MdzLexer {
|
|
|
1188
1264
|
type: 'text',
|
|
1189
1265
|
content: '_',
|
|
1190
1266
|
start,
|
|
1191
|
-
end: start + 1
|
|
1267
|
+
end: start + 1
|
|
1192
1268
|
};
|
|
1193
1269
|
}
|
|
1194
1270
|
}
|
|
1195
1271
|
#tokenize_markdown_link() {
|
|
1196
1272
|
const start = this.#index;
|
|
1273
|
+
// a link open at this offset already failed, or was suppressed by the
|
|
1274
|
+
// nesting cap — emit `[` as text instead of re-descending into the same
|
|
1275
|
+
// failing children (exponential on nested `[`); the short-circuit
|
|
1276
|
+
// reproduces the revert's exact net effect (`[` text, cursor at start + 1)
|
|
1277
|
+
if (this.#failed_link_starts.has(start) || this.#depth_capped.has(start)) {
|
|
1278
|
+
this.#index = start + 1;
|
|
1279
|
+
this.#emit_text('[', start);
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
// nesting-depth cap: too deep to open — render literal and record the
|
|
1283
|
+
// suppression so a shallower re-lex short-circuits here too
|
|
1284
|
+
if (this.#inline_depth >= MAX_INLINE_NESTING_DEPTH) {
|
|
1285
|
+
this.#depth_capped.add(start);
|
|
1286
|
+
this.#index = start + 1;
|
|
1287
|
+
this.#emit_text('[', start);
|
|
1288
|
+
return;
|
|
1289
|
+
}
|
|
1197
1290
|
this.#index++; // consume [ (dispatch guarantees it)
|
|
1198
1291
|
// Emit link_text_open
|
|
1199
1292
|
this.#tokens.push({ type: 'link_text_open', start, end: this.#index });
|
|
1200
1293
|
// Tokenize children until ] — only `]` delimits link text; a bare `)`
|
|
1201
1294
|
// is ordinary content (matching the streaming parser, which has no `)`
|
|
1202
1295
|
// dispatch case — the reference scan finds its own `)` after `](`)
|
|
1296
|
+
this.#inline_depth++;
|
|
1203
1297
|
while (this.#index < this.#text.length) {
|
|
1204
1298
|
if (this.#text.charCodeAt(this.#index) === RIGHT_BRACKET)
|
|
1205
1299
|
break;
|
|
@@ -1207,6 +1301,7 @@ export class MdzLexer {
|
|
|
1207
1301
|
break;
|
|
1208
1302
|
this.#tokenize_inline();
|
|
1209
1303
|
}
|
|
1304
|
+
this.#inline_depth--;
|
|
1210
1305
|
// Check for ]
|
|
1211
1306
|
if (this.#index >= this.#text.length || this.#text.charCodeAt(this.#index) !== RIGHT_BRACKET) {
|
|
1212
1307
|
// Revert - remove link_text_open and all children tokens added
|
|
@@ -1218,7 +1313,7 @@ export class MdzLexer {
|
|
|
1218
1313
|
this.#tokens.push({
|
|
1219
1314
|
type: 'link_text_close',
|
|
1220
1315
|
start: bracket_close_start,
|
|
1221
|
-
end: this.#index
|
|
1316
|
+
end: this.#index
|
|
1222
1317
|
});
|
|
1223
1318
|
// Check for (
|
|
1224
1319
|
if (this.#index >= this.#text.length || this.#text.charCodeAt(this.#index) !== LEFT_PAREN) {
|
|
@@ -1259,10 +1354,17 @@ export class MdzLexer {
|
|
|
1259
1354
|
reference,
|
|
1260
1355
|
link_type,
|
|
1261
1356
|
start: bracket_close_start + 1, // after ]
|
|
1262
|
-
end: this.#index
|
|
1357
|
+
end: this.#index
|
|
1263
1358
|
});
|
|
1264
1359
|
}
|
|
1265
1360
|
#revert_tokens_from_link_open(start) {
|
|
1361
|
+
// record the failure so a re-lex from before this `[` short-circuits.
|
|
1362
|
+
// Keyed by `start` alone — unlike `#failed_tag`'s `start:bound` — because a
|
|
1363
|
+
// link's outcome is bound-independent: the `]`-terminator scan (the children
|
|
1364
|
+
// loop) runs to `text.length` and never consults `#max_search_index`, so a
|
|
1365
|
+
// wider-bound re-lex can't move it. The bound reaches nested tags inside the
|
|
1366
|
+
// link text, but re-attempting those wider never flips the link's net result.
|
|
1367
|
+
this.#failed_link_starts.add(start);
|
|
1266
1368
|
// Find and remove link_text_open and all tokens after it
|
|
1267
1369
|
let open_idx = -1;
|
|
1268
1370
|
for (let i = this.#tokens.length - 1; i >= 0; i--) {
|
|
@@ -1279,6 +1381,30 @@ export class MdzLexer {
|
|
|
1279
1381
|
}
|
|
1280
1382
|
#tokenize_tag() {
|
|
1281
1383
|
const start = this.#index;
|
|
1384
|
+
// short-circuit a re-attempt of a tag open that already failed at this
|
|
1385
|
+
// offset+bound — re-descending into the same failing children is
|
|
1386
|
+
// exponential on nested same-name tags. Keyed by
|
|
1387
|
+
// `#max_search_index` (captured as `bound`) because the closer search is
|
|
1388
|
+
// bound-sensitive, so a wider-bound re-lex can legitimately succeed. The
|
|
1389
|
+
// key string is built only when the memo is non-empty (adversarial
|
|
1390
|
+
// input); prose never allocates it. The short-circuit reproduces a
|
|
1391
|
+
// revert's exact net effect (`<` text, cursor at start + 1).
|
|
1392
|
+
const bound = this.#max_search_index;
|
|
1393
|
+
if (this.#depth_capped.has(start) ||
|
|
1394
|
+
(this.#failed_tag.size !== 0 && this.#failed_tag.has(`${start}:${bound}`))) {
|
|
1395
|
+
this.#index = start + 1;
|
|
1396
|
+
this.#emit_text('<', start);
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1399
|
+
// nesting-depth cap: too deep to open a tag — render literal and record
|
|
1400
|
+
// the suppression (position-keyed) so a shallower re-lex short-circuits
|
|
1401
|
+
// here too, keeping the revert re-scan linear
|
|
1402
|
+
if (this.#inline_depth >= MAX_INLINE_NESTING_DEPTH) {
|
|
1403
|
+
this.#depth_capped.add(start);
|
|
1404
|
+
this.#index = start + 1;
|
|
1405
|
+
this.#emit_text('<', start);
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1282
1408
|
this.#index++; // consume <
|
|
1283
1409
|
// Tag name must start with a letter
|
|
1284
1410
|
if (this.#index >= this.#text.length || !is_letter(this.#text.charCodeAt(this.#index))) {
|
|
@@ -1309,12 +1435,13 @@ export class MdzLexer {
|
|
|
1309
1435
|
name: tag_name,
|
|
1310
1436
|
is_component,
|
|
1311
1437
|
start,
|
|
1312
|
-
end: this.#index
|
|
1438
|
+
end: this.#index
|
|
1313
1439
|
});
|
|
1314
1440
|
return;
|
|
1315
1441
|
}
|
|
1316
1442
|
// Check for >
|
|
1317
1443
|
if (this.#index >= this.#text.length || this.#text.charCodeAt(this.#index) !== RIGHT_ANGLE) {
|
|
1444
|
+
this.#failed_tag.add(`${start}:${bound}`);
|
|
1318
1445
|
this.#index = start + 1;
|
|
1319
1446
|
this.#emit_text('<', start);
|
|
1320
1447
|
return;
|
|
@@ -1332,11 +1459,13 @@ export class MdzLexer {
|
|
|
1332
1459
|
const search_limit = Math.min(this.#max_search_index, this.#text.length);
|
|
1333
1460
|
const closing_tag_pos = this.#index_of(closing_tag, this.#index);
|
|
1334
1461
|
if (closing_tag_pos === -1 || closing_tag_pos > search_limit) {
|
|
1462
|
+
this.#failed_tag.add(`${start}:${bound}`);
|
|
1335
1463
|
this.#index = start + 1;
|
|
1336
1464
|
this.#emit_text('<', start);
|
|
1337
1465
|
return;
|
|
1338
1466
|
}
|
|
1339
1467
|
if (this.#has_paragraph_break_between(this.#index, closing_tag_pos)) {
|
|
1468
|
+
this.#failed_tag.add(`${start}:${bound}`);
|
|
1340
1469
|
this.#index = start + 1;
|
|
1341
1470
|
this.#emit_text('<', start);
|
|
1342
1471
|
return;
|
|
@@ -1350,8 +1479,10 @@ export class MdzLexer {
|
|
|
1350
1479
|
// the code span). Recomputed per iteration because a nested same-name
|
|
1351
1480
|
// tag legitimately consumes the nearest closer (`<b>x<b>y</b></b>`).
|
|
1352
1481
|
const saved_max = this.#max_search_index;
|
|
1482
|
+
this.#inline_depth++;
|
|
1353
1483
|
while (this.#index < this.#text.length) {
|
|
1354
1484
|
if (this.#match(closing_tag)) {
|
|
1485
|
+
this.#inline_depth--;
|
|
1355
1486
|
this.#max_search_index = saved_max;
|
|
1356
1487
|
const close_start = this.#index;
|
|
1357
1488
|
this.#index += closing_tag.length;
|
|
@@ -1359,7 +1490,7 @@ export class MdzLexer {
|
|
|
1359
1490
|
type: 'tag_close',
|
|
1360
1491
|
name: tag_name,
|
|
1361
1492
|
start: close_start,
|
|
1362
|
-
end: this.#index
|
|
1493
|
+
end: this.#index
|
|
1363
1494
|
});
|
|
1364
1495
|
return;
|
|
1365
1496
|
}
|
|
@@ -1372,11 +1503,13 @@ export class MdzLexer {
|
|
|
1372
1503
|
this.#max_search_index = next_close;
|
|
1373
1504
|
this.#tokenize_inline();
|
|
1374
1505
|
}
|
|
1506
|
+
this.#inline_depth--;
|
|
1375
1507
|
// No valid closer left — revert: drop the open token and all consumed
|
|
1376
1508
|
// children tokens, emit `<` as text, and re-lex from the next char
|
|
1377
1509
|
// (the `#revert_tokens_from_link_open` pattern; the old fallthrough
|
|
1378
1510
|
// left a dangling `tag_open` whose parser fallback discarded content)
|
|
1379
1511
|
this.#max_search_index = saved_max;
|
|
1512
|
+
this.#failed_tag.add(`${start}:${bound}`);
|
|
1380
1513
|
this.#tokens.splice(open_token_index);
|
|
1381
1514
|
this.#index = start + 1;
|
|
1382
1515
|
this.#emit_text('<', start);
|
|
@@ -1400,15 +1533,30 @@ export class MdzLexer {
|
|
|
1400
1533
|
// content: parts accumulate around each stripped region
|
|
1401
1534
|
let parts = null;
|
|
1402
1535
|
let part_start = start;
|
|
1403
|
-
|
|
1536
|
+
// in a table cell the trimmed cell end is a hard stop (no newline to
|
|
1537
|
+
// halt on mid-line) — hoisted to the loop bound
|
|
1538
|
+
const scan_end = this.#in_table_cell
|
|
1539
|
+
? Math.min(this.#max_search_index, this.#text.length)
|
|
1540
|
+
: this.#text.length;
|
|
1541
|
+
while (this.#index < scan_end) {
|
|
1404
1542
|
const char_code = this.#text.charCodeAt(this.#index);
|
|
1405
|
-
//
|
|
1406
|
-
//
|
|
1407
|
-
//
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1543
|
+
// one table load classifies the char; zero (the common case — the
|
|
1544
|
+
// char can't end or interrupt the run) skips the dispatch below.
|
|
1545
|
+
// `TEXT_SCAN_CLASS` is shared with the streaming scanner
|
|
1546
|
+
// (`consume_text_run` in `mdz_stream_parser_text.ts`); the two dispatch
|
|
1547
|
+
// bodies intentionally differ around it — this one handles `\|` escapes
|
|
1548
|
+
// and list-run strip, that one URL speculation across chunk boundaries.
|
|
1549
|
+
const char_class = char_code < 128 ? TEXT_SCAN_CLASS[char_code] : 0;
|
|
1550
|
+
if (char_class === 0) {
|
|
1551
|
+
this.#index++;
|
|
1552
|
+
continue;
|
|
1553
|
+
}
|
|
1554
|
+
// Stop at special characters
|
|
1555
|
+
if ((char_class & TEXT_CLASS_STOP) !== 0)
|
|
1556
|
+
break;
|
|
1557
|
+
if ((char_class & TEXT_CLASS_BACKSLASH) !== 0) {
|
|
1558
|
+
// `\|` is a literal pipe — the only mdz escape, scoped to table cells
|
|
1559
|
+
if (this.#in_table_cell &&
|
|
1412
1560
|
this.#index + 1 < this.#max_search_index &&
|
|
1413
1561
|
this.#text.charCodeAt(this.#index + 1) === PIPE) {
|
|
1414
1562
|
(parts ??= []).push(this.#text.slice(part_start, this.#index));
|
|
@@ -1417,18 +1565,10 @@ export class MdzLexer {
|
|
|
1417
1565
|
part_start = this.#index;
|
|
1418
1566
|
continue;
|
|
1419
1567
|
}
|
|
1568
|
+
this.#index++;
|
|
1569
|
+
continue;
|
|
1420
1570
|
}
|
|
1421
|
-
|
|
1422
|
-
if (char_code === BACKTICK ||
|
|
1423
|
-
char_code === ASTERISK ||
|
|
1424
|
-
char_code === UNDERSCORE ||
|
|
1425
|
-
char_code === TILDE ||
|
|
1426
|
-
char_code === LEFT_BRACKET ||
|
|
1427
|
-
char_code === RIGHT_BRACKET ||
|
|
1428
|
-
char_code === LEFT_ANGLE) {
|
|
1429
|
-
break;
|
|
1430
|
-
}
|
|
1431
|
-
if (char_code === NEWLINE) {
|
|
1571
|
+
if ((char_class & TEXT_CLASS_NEWLINE) !== 0) {
|
|
1432
1572
|
if (this.#list_run_strip) {
|
|
1433
1573
|
if (this.#index >= this.#max_search_index)
|
|
1434
1574
|
break; // the run's terminator
|
|
@@ -1465,15 +1605,25 @@ export class MdzLexer {
|
|
|
1465
1605
|
break;
|
|
1466
1606
|
}
|
|
1467
1607
|
}
|
|
1608
|
+
this.#index++; // single newline — ordinary text
|
|
1609
|
+
continue;
|
|
1610
|
+
}
|
|
1611
|
+
// URL or internal path mid-text (class guard avoids probing every char)
|
|
1612
|
+
if ((char_class & TEXT_CLASS_URL) !== 0) {
|
|
1613
|
+
if (this.#is_at_url())
|
|
1614
|
+
break;
|
|
1615
|
+
}
|
|
1616
|
+
else if (char_code === SLASH) {
|
|
1617
|
+
if (is_at_absolute_path(this.#text, this.#index))
|
|
1618
|
+
break;
|
|
1468
1619
|
}
|
|
1469
|
-
|
|
1470
|
-
if (((char_code === 104 /* h */ || char_code === 72) /* H */ && this.#is_at_url()) ||
|
|
1471
|
-
(char_code === SLASH && is_at_absolute_path(this.#text, this.#index)) ||
|
|
1472
|
-
(char_code === PERIOD && is_at_relative_path(this.#text, this.#index))) {
|
|
1620
|
+
else if (is_at_relative_path(this.#text, this.#index)) {
|
|
1473
1621
|
break;
|
|
1474
1622
|
}
|
|
1475
1623
|
this.#index++;
|
|
1476
1624
|
}
|
|
1625
|
+
if (mdz_debug_work.enabled)
|
|
1626
|
+
mdz_debug_work.total += this.#index - start;
|
|
1477
1627
|
// Ensure we always consume at least one character
|
|
1478
1628
|
if (this.#index === start && this.#index < this.#text.length) {
|
|
1479
1629
|
this.#index++;
|
|
@@ -1504,7 +1654,7 @@ export class MdzLexer {
|
|
|
1504
1654
|
reference,
|
|
1505
1655
|
link_type: 'external',
|
|
1506
1656
|
start,
|
|
1507
|
-
end: this.#index
|
|
1657
|
+
end: this.#index
|
|
1508
1658
|
});
|
|
1509
1659
|
}
|
|
1510
1660
|
#tokenize_auto_link_internal() {
|
|
@@ -1525,7 +1675,7 @@ export class MdzLexer {
|
|
|
1525
1675
|
reference,
|
|
1526
1676
|
link_type: 'internal',
|
|
1527
1677
|
start,
|
|
1528
|
-
end: this.#index
|
|
1678
|
+
end: this.#index
|
|
1529
1679
|
});
|
|
1530
1680
|
}
|
|
1531
1681
|
// -- Helper methods --
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mdz_opcodes.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_opcodes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"mdz_opcodes.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_opcodes.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mdz_opcodes_to_nodes.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_opcodes_to_nodes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EACX,OAAO,EASP,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"mdz_opcodes_to_nodes.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_opcodes_to_nodes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EACX,OAAO,EASP,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,kBAAkB,CAAC;AAyB7D;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,KAAK,CAAC,SAAS,CAAC,KAAG,KAAK,CAAC,OAAO,CAgM7E,CAAC"}
|