@astryxdesign/core 0.1.3-canary.adbd0e5 → 0.1.3-canary.cbfd87b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Markdown/parser.d.ts +7 -0
- package/dist/Markdown/parser.d.ts.map +1 -1
- package/dist/Markdown/parser.js +300 -6
- package/dist/astryx.umd.js +46 -44
- package/dist/astryx.umd.js.map +3 -3
- package/package.json +1 -1
- package/src/Avatar/Avatar.doc.mjs +1 -1
- package/src/DateInput/DateInput.doc.mjs +5 -0
- package/src/FileInput/FileInput.doc.mjs +1 -1
- package/src/Markdown/Markdown.test.tsx +23 -0
- package/src/Markdown/incremental.test.ts +36 -0
- package/src/Markdown/parser.test.ts +210 -0
- package/src/Markdown/parser.ts +301 -6
- package/src/MultiSelector/MultiSelector.doc.mjs +5 -0
- package/src/Selector/Selector.doc.mjs +5 -0
- package/src/TabList/TabList.doc.mjs +3 -3
- package/src/TimeInput/TimeInput.doc.mjs +2 -2
- package/src/Typeahead/Typeahead.doc.mjs +5 -0
|
@@ -115,6 +115,13 @@ export interface IncrementalState {
|
|
|
115
115
|
* with newly-arriving content.
|
|
116
116
|
*/
|
|
117
117
|
autolink?: 'gfm';
|
|
118
|
+
/**
|
|
119
|
+
* Signature of the link reference definitions the cached `settledBlocks`
|
|
120
|
+
* were parsed with. Definitions are document-global and typically arrive
|
|
121
|
+
* (in a footer) after the references that use them, so when the set changes
|
|
122
|
+
* the settled cache is invalidated to let earlier references resolve.
|
|
123
|
+
*/
|
|
124
|
+
linkDefsKey?: string;
|
|
118
125
|
}
|
|
119
126
|
export declare function createIncrementalState(): IncrementalState;
|
|
120
127
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/Markdown/parser.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAMH,MAAM,MAAM,UAAU,GAClB;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,GAC/B;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,UAAU,EAAE,CAAA;CAAC,GACtC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,UAAU,EAAE,CAAA;CAAC,GACxC;IAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,UAAU,EAAE,CAAA;CAAC,GAC/C;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,GAC/B;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,UAAU,EAAE,CAAA;CAAC,GACpD;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAC,GACzC;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAC,GACpC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAC,CAAC;AAEpB,MAAM,MAAM,SAAS,GACjB;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAAC,QAAQ,EAAE,UAAU,EAAE,CAAA;CAAC,GACvE;IAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,UAAU,EAAE,CAAA;CAAC,GAC3C;IAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,GACtD;IAAC,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,EAAE,SAAS,EAAE,CAAA;CAAC,GAC3C;IACE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,SAAS,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB,GACD;IACE,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,IAAI,EAAE,aAAa,EAAE,EAAE,CAAC;CACzB,GACD;IAAC,IAAI,EAAE,IAAI,CAAA;CAAC,GACZ;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAC,CAAC;AAE9C,MAAM,MAAM,YAAY,GAAG;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,SAAS,EAAE,CAAA;CAAC,CAAC;AACtE,MAAM,MAAM,aAAa,GAAG;IAAC,QAAQ,EAAE,UAAU,EAAE,CAAA;CAAC,CAAC;AACrD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,IAAI,CAAC;AAMhE;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB;+DAC2D;IAC3D,SAAS,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAChC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC;CAClB,CAAC;
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/Markdown/parser.ts"],"names":[],"mappings":"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"}
|
package/dist/Markdown/parser.js
CHANGED
|
@@ -49,6 +49,235 @@ function resolveOptions(arg) {
|
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Link reference definitions
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
// A CommonMark link reference definition line: up to 3 leading spaces, a
|
|
57
|
+
// bracketed label, `:`, a destination (bare or `<...>`), and an optional
|
|
58
|
+
// same-line title (captured as group 4 so a title-less definition can absorb a
|
|
59
|
+
// title on the following line). `^`-leading labels (`[^1]:`) are footnote
|
|
60
|
+
// definitions — a separate, unsupported feature — and are excluded so they
|
|
61
|
+
// pass through verbatim.
|
|
62
|
+
const LINK_DEFINITION_RE = /^ {0,3}\[([^\]^](?:\\.|[^\]\\])*)\]:[ \t]*(?:<([^<>\n]*)>|(\S+))([ \t]+(?:"[^"\n]*"|'[^'\n]*'|\([^()\n]*\)))?[ \t]*$/;
|
|
63
|
+
|
|
64
|
+
// A line that is nothing but a title — the continuation form allowed when a
|
|
65
|
+
// definition's destination is followed by its title on the next line.
|
|
66
|
+
const LINK_TITLE_ONLY_RE = /^ {0,3}(?:"[^"\n]*"|'[^'\n]*'|\([^()\n]*\))[ \t]*$/;
|
|
67
|
+
|
|
68
|
+
// CommonMark matches reference labels case-insensitively with leading/trailing
|
|
69
|
+
// whitespace stripped and internal whitespace runs collapsed to one space.
|
|
70
|
+
function normalizeLinkLabel(label) {
|
|
71
|
+
return label.trim().replace(/\s+/g, ' ').toLowerCase();
|
|
72
|
+
}
|
|
73
|
+
function matchLinkDefinition(line) {
|
|
74
|
+
const match = LINK_DEFINITION_RE.exec(line);
|
|
75
|
+
if (match == null) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const label = normalizeLinkLabel(match[1]);
|
|
79
|
+
// `match[2]` is the `<...>` destination (present but possibly empty, e.g.
|
|
80
|
+
// `<>` → empty href, valid per CommonMark); `match[3]` is the bare
|
|
81
|
+
// destination (always non-empty). One of the two always matches.
|
|
82
|
+
const destination = match[2] != null ? match[2] : match[3];
|
|
83
|
+
if (label === '' || destination == null) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
label,
|
|
88
|
+
destination,
|
|
89
|
+
hasTitle: match[4] != null
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Collect link reference definitions from the whole document and return the
|
|
95
|
+
* input with the definition lines removed. A definition is recognized at a
|
|
96
|
+
* block boundary — document start, after a blank line, after another
|
|
97
|
+
* definition, or after a self-contained block (heading / thematic break /
|
|
98
|
+
* closed fenced code) — but never inside a fenced code block or as a lazy
|
|
99
|
+
* continuation of a paragraph, honoring CommonMark's rule that a definition
|
|
100
|
+
* cannot interrupt a paragraph. First definition wins, and definitions produce
|
|
101
|
+
* no output so stripping unreferenced ones is correct.
|
|
102
|
+
*
|
|
103
|
+
* Scope limit: definitions are collected at the top level only, and a
|
|
104
|
+
* definition directly following a list, blockquote, or table (with no blank
|
|
105
|
+
* line between) is not recognized. A definition nested inside a blockquote or
|
|
106
|
+
* list item resolves within that container (via the recursive parse) but is
|
|
107
|
+
* not exposed to references elsewhere in the document, unlike full CommonMark
|
|
108
|
+
* where every definition is global. Separating a footer definition block with
|
|
109
|
+
* a blank line — the usual form — always works.
|
|
110
|
+
*/
|
|
111
|
+
function extractLinkDefinitions(input) {
|
|
112
|
+
const lines = input.split('\n');
|
|
113
|
+
const defs = new Map();
|
|
114
|
+
const keep = new Array(lines.length).fill(true);
|
|
115
|
+
let atBoundary = true;
|
|
116
|
+
let inFence = false;
|
|
117
|
+
let fenceMarker = '';
|
|
118
|
+
for (let index = 0; index < lines.length; index++) {
|
|
119
|
+
const line = lines[index];
|
|
120
|
+
if (inFence) {
|
|
121
|
+
if (line.startsWith(fenceMarker)) {
|
|
122
|
+
inFence = false;
|
|
123
|
+
fenceMarker = '';
|
|
124
|
+
// The line after a closed fence begins a new block.
|
|
125
|
+
atBoundary = true;
|
|
126
|
+
} else {
|
|
127
|
+
atBoundary = false;
|
|
128
|
+
}
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const fenceMatch = line.match(/^(`{3,}|~{3,})/);
|
|
132
|
+
if (fenceMatch) {
|
|
133
|
+
inFence = true;
|
|
134
|
+
fenceMarker = fenceMatch[1];
|
|
135
|
+
atBoundary = false;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (line.trim() === '') {
|
|
139
|
+
atBoundary = true;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (atBoundary) {
|
|
143
|
+
const def = matchLinkDefinition(line);
|
|
144
|
+
if (def != null) {
|
|
145
|
+
if (!defs.has(def.label)) {
|
|
146
|
+
defs.set(def.label, def.destination);
|
|
147
|
+
}
|
|
148
|
+
keep[index] = false;
|
|
149
|
+
// A title-less definition absorbs a title on the following line
|
|
150
|
+
// (CommonMark), which then also produces no output.
|
|
151
|
+
if (!def.hasTitle && index + 1 < lines.length && LINK_TITLE_ONLY_RE.test(lines[index + 1])) {
|
|
152
|
+
keep[index + 1] = false;
|
|
153
|
+
index++;
|
|
154
|
+
}
|
|
155
|
+
// Consecutive definitions stay at a block boundary.
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// A heading or thematic break is a self-contained single-line block, so
|
|
160
|
+
// the next line begins a new block where a definition may appear.
|
|
161
|
+
atBoundary = /^ {0,3}#{1,6}(?: |\t|$)/.test(line) || isHorizontalRule(line);
|
|
162
|
+
}
|
|
163
|
+
if (defs.size === 0) {
|
|
164
|
+
return {
|
|
165
|
+
defs,
|
|
166
|
+
cleaned: input
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
const cleaned = lines.filter((_, index) => keep[index]).join('\n');
|
|
170
|
+
return {
|
|
171
|
+
defs,
|
|
172
|
+
cleaned
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Order-independent signature of a link-definition set, for cache checks. */
|
|
177
|
+
function linkDefsSignature(defs) {
|
|
178
|
+
if (defs.size === 0) {
|
|
179
|
+
return '';
|
|
180
|
+
}
|
|
181
|
+
return [...defs].sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0).map(([label, dest]) => `${label}\u0000${dest}`).join('\u0001');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Resolve a full (`[text][label]`), collapsed (`[text][]`), or shortcut
|
|
186
|
+
* (`[text]`) reference at `start` (which points at `[`) against `linkDefs`.
|
|
187
|
+
* Returns the node plus the index just past the reference, or null when it is
|
|
188
|
+
* not a resolvable reference (caller falls through to literal handling).
|
|
189
|
+
*/
|
|
190
|
+
function matchReferenceLink(text, start, linkDefs, opts) {
|
|
191
|
+
const textClose = text.indexOf(']', start + 1);
|
|
192
|
+
if (textClose === -1) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
const linkText = text.slice(start + 1, textClose);
|
|
196
|
+
// Full `[text][label]` / collapsed `[text][]` — a matching definition wins.
|
|
197
|
+
if (text[textClose + 1] === '[') {
|
|
198
|
+
const labelClose = text.indexOf(']', textClose + 2);
|
|
199
|
+
if (labelClose !== -1) {
|
|
200
|
+
const rawLabel = text.slice(textClose + 2, labelClose);
|
|
201
|
+
// Only truly-empty brackets are the collapsed form; a whitespace-only
|
|
202
|
+
// label (`[ ]`) is a full reference whose normalized label is empty and
|
|
203
|
+
// matches nothing.
|
|
204
|
+
const label = rawLabel === '' ? linkText : rawLabel;
|
|
205
|
+
const href = linkDefs.get(normalizeLinkLabel(label));
|
|
206
|
+
if (href != null) {
|
|
207
|
+
return {
|
|
208
|
+
node: {
|
|
209
|
+
type: 'link',
|
|
210
|
+
href,
|
|
211
|
+
children: parseInlineImpl(linkText, opts)
|
|
212
|
+
},
|
|
213
|
+
end: labelClose + 1
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
// No match — fall back to a shortcut `[text]` (CommonMark back-off),
|
|
217
|
+
// leaving the trailing `[label]` to be parsed separately.
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// Shortcut: `[text]`.
|
|
221
|
+
if (linkText.trim() === '') {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
const href = linkDefs.get(normalizeLinkLabel(linkText));
|
|
225
|
+
if (href == null) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
node: {
|
|
230
|
+
type: 'link',
|
|
231
|
+
href,
|
|
232
|
+
children: parseInlineImpl(linkText, opts)
|
|
233
|
+
},
|
|
234
|
+
end: textClose + 1
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** Reference-image equivalent of {@link matchReferenceLink} (`![alt][label]`). */
|
|
239
|
+
function matchReferenceImage(text, start, linkDefs) {
|
|
240
|
+
const altClose = text.indexOf(']', start + 2);
|
|
241
|
+
if (altClose === -1) {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
const alt = text.slice(start + 2, altClose);
|
|
245
|
+
if (text[altClose + 1] === '[') {
|
|
246
|
+
const labelClose = text.indexOf(']', altClose + 2);
|
|
247
|
+
if (labelClose !== -1) {
|
|
248
|
+
const rawLabel = text.slice(altClose + 2, labelClose);
|
|
249
|
+
const label = rawLabel === '' ? alt : rawLabel;
|
|
250
|
+
const src = linkDefs.get(normalizeLinkLabel(label));
|
|
251
|
+
if (src != null) {
|
|
252
|
+
return {
|
|
253
|
+
node: {
|
|
254
|
+
type: 'image',
|
|
255
|
+
src,
|
|
256
|
+
alt
|
|
257
|
+
},
|
|
258
|
+
end: labelClose + 1
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
// No match — fall back to a shortcut `![alt]`.
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (alt.trim() === '') {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
const src = linkDefs.get(normalizeLinkLabel(alt));
|
|
268
|
+
if (src == null) {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
node: {
|
|
273
|
+
type: 'image',
|
|
274
|
+
src,
|
|
275
|
+
alt
|
|
276
|
+
},
|
|
277
|
+
end: altClose + 1
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
52
281
|
// ---------------------------------------------------------------------------
|
|
53
282
|
// Inline parser helpers
|
|
54
283
|
// ---------------------------------------------------------------------------
|
|
@@ -201,6 +430,16 @@ function parseInlineImpl(text, opts) {
|
|
|
201
430
|
}
|
|
202
431
|
}
|
|
203
432
|
|
|
433
|
+
// --- Reference image ![alt][label] / ![alt][] / ![alt] ---
|
|
434
|
+
if (opts.linkDefs != null && text[i] === '!' && text[i + 1] === '[') {
|
|
435
|
+
const ref = matchReferenceImage(text, i, opts.linkDefs);
|
|
436
|
+
if (ref) {
|
|
437
|
+
nodes.push(ref.node);
|
|
438
|
+
i = ref.end;
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
204
443
|
// --- Citation: bracket [id] (before link — link requires `(` after `]`) ---
|
|
205
444
|
{
|
|
206
445
|
const citation = matchBracketCitation(text, i, opts);
|
|
@@ -231,6 +470,16 @@ function parseInlineImpl(text, opts) {
|
|
|
231
470
|
}
|
|
232
471
|
}
|
|
233
472
|
|
|
473
|
+
// --- Reference link [text][label] / [text][] / [text] ---
|
|
474
|
+
if (opts.linkDefs != null && text[i] === '[') {
|
|
475
|
+
const ref = matchReferenceLink(text, i, opts.linkDefs, opts);
|
|
476
|
+
if (ref) {
|
|
477
|
+
nodes.push(ref.node);
|
|
478
|
+
i = ref.end;
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
234
483
|
// --- Bold-italic: *** or ___ ---
|
|
235
484
|
if (text[i] === '*' && text[i + 1] === '*' && text[i + 2] === '*' || text[i] === '_' && text[i + 1] === '_' && text[i + 2] === '_') {
|
|
236
485
|
const marker = text.slice(i, i + 3);
|
|
@@ -836,8 +1085,32 @@ function parseList(lines, startIndex, ordered, opts) {
|
|
|
836
1085
|
export function parseMarkdown(input, arg) {
|
|
837
1086
|
return parseMarkdownImpl(input, resolveOptions(arg));
|
|
838
1087
|
}
|
|
839
|
-
function parseMarkdownImpl(input,
|
|
840
|
-
|
|
1088
|
+
function parseMarkdownImpl(input, baseOpts) {
|
|
1089
|
+
// Collect this input's link reference definitions and strip their lines,
|
|
1090
|
+
// then merge them with any definitions inherited from an enclosing parse
|
|
1091
|
+
// (the incremental parser passes the whole document's definitions in; a
|
|
1092
|
+
// recursive blockquote/list parse inherits the outer definitions). Inherited
|
|
1093
|
+
// definitions win on conflict, matching CommonMark's first-definition-wins
|
|
1094
|
+
// in document order; locally-nested definitions still resolve within this
|
|
1095
|
+
// parse.
|
|
1096
|
+
const {
|
|
1097
|
+
defs,
|
|
1098
|
+
cleaned
|
|
1099
|
+
} = extractLinkDefinitions(input);
|
|
1100
|
+
const inherited = baseOpts.linkDefs;
|
|
1101
|
+
let linkDefs;
|
|
1102
|
+
if (defs.size === 0) {
|
|
1103
|
+
linkDefs = inherited;
|
|
1104
|
+
} else if (inherited == null) {
|
|
1105
|
+
linkDefs = defs;
|
|
1106
|
+
} else {
|
|
1107
|
+
linkDefs = new Map([...defs, ...inherited]);
|
|
1108
|
+
}
|
|
1109
|
+
const opts = linkDefs != null ? {
|
|
1110
|
+
...baseOpts,
|
|
1111
|
+
linkDefs
|
|
1112
|
+
} : baseOpts;
|
|
1113
|
+
const lines = cleaned.split('\n');
|
|
841
1114
|
const blocks = [];
|
|
842
1115
|
let index = 0;
|
|
843
1116
|
while (index < lines.length) {
|
|
@@ -1257,14 +1530,35 @@ export function parseMarkdownIncremental(input, state, arg) {
|
|
|
1257
1530
|
state.settledText = '';
|
|
1258
1531
|
state.settledBlocks = [];
|
|
1259
1532
|
state.settledUpTo = 0;
|
|
1533
|
+
state.linkDefsKey = undefined;
|
|
1260
1534
|
return [];
|
|
1261
1535
|
}
|
|
1536
|
+
|
|
1537
|
+
// Link reference definitions are document-global and usually stream in
|
|
1538
|
+
// (as a footer) after the references that use them, so collect them from the
|
|
1539
|
+
// whole input and pass them into every slice parse. When the set changes,
|
|
1540
|
+
// invalidate the settled cache so already-settled references re-resolve.
|
|
1541
|
+
const {
|
|
1542
|
+
defs: linkDefs
|
|
1543
|
+
} = extractLinkDefinitions(input);
|
|
1544
|
+
const linkDefsKey = linkDefsSignature(linkDefs);
|
|
1545
|
+
if (state.linkDefsKey !== linkDefsKey) {
|
|
1546
|
+
state.prevInput = '';
|
|
1547
|
+
state.settledText = '';
|
|
1548
|
+
state.settledBlocks = [];
|
|
1549
|
+
state.settledUpTo = 0;
|
|
1550
|
+
state.linkDefsKey = linkDefsKey;
|
|
1551
|
+
}
|
|
1552
|
+
const parseOpts = linkDefs.size > 0 ? {
|
|
1553
|
+
...opts,
|
|
1554
|
+
linkDefs
|
|
1555
|
+
} : opts;
|
|
1262
1556
|
const lines = input.split('\n');
|
|
1263
1557
|
const boundary = findSettledBoundary(lines);
|
|
1264
1558
|
if (boundary < 0) {
|
|
1265
1559
|
// Inside an unclosed fence or no blank-line boundary — full re-parse
|
|
1266
1560
|
state.prevInput = input;
|
|
1267
|
-
return parseMarkdownImpl(input,
|
|
1561
|
+
return parseMarkdownImpl(input, parseOpts);
|
|
1268
1562
|
}
|
|
1269
1563
|
const settledText = lines.slice(0, boundary).join('\n');
|
|
1270
1564
|
const unsettledRaw = lines.slice(boundary).join('\n').trim();
|
|
@@ -1276,13 +1570,13 @@ export function parseMarkdownIncremental(input, state, arg) {
|
|
|
1276
1570
|
} else if (state.settledText.length > 0 && settledText.startsWith(state.settledText)) {
|
|
1277
1571
|
// Settled portion grew — parse only the new delta
|
|
1278
1572
|
const delta = settledText.slice(state.settledText.length);
|
|
1279
|
-
const deltaBlocks = parseMarkdownImpl(delta,
|
|
1573
|
+
const deltaBlocks = parseMarkdownImpl(delta, parseOpts);
|
|
1280
1574
|
settledBlocks = mergeSettledBlocks(state.settledBlocks, deltaBlocks);
|
|
1281
1575
|
} else {
|
|
1282
1576
|
// Content before the boundary changed — full re-parse of settled portion
|
|
1283
|
-
settledBlocks = parseMarkdownImpl(settledText,
|
|
1577
|
+
settledBlocks = parseMarkdownImpl(settledText, parseOpts);
|
|
1284
1578
|
}
|
|
1285
|
-
const unsettledBlocks = unsettledText ? parseMarkdownImpl(unsettledText,
|
|
1579
|
+
const unsettledBlocks = unsettledText ? parseMarkdownImpl(unsettledText, parseOpts) : [];
|
|
1286
1580
|
state.settledText = settledText;
|
|
1287
1581
|
state.settledBlocks = settledBlocks;
|
|
1288
1582
|
state.settledUpTo = boundary;
|