@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.
@@ -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":"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"}
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/Markdown/parser.ts"],"names":[],"mappings":"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"}
@@ -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, opts) {
840
- const lines = input.split('\n');
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, opts);
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, opts);
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, opts);
1577
+ settledBlocks = parseMarkdownImpl(settledText, parseOpts);
1284
1578
  }
1285
- const unsettledBlocks = unsettledText ? parseMarkdownImpl(unsettledText, opts) : [];
1579
+ const unsettledBlocks = unsettledText ? parseMarkdownImpl(unsettledText, parseOpts) : [];
1286
1580
  state.settledText = settledText;
1287
1581
  state.settledBlocks = settledBlocks;
1288
1582
  state.settledUpTo = boundary;