@tanstack/markdown 0.0.13 → 0.0.15

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/parser.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { parseInline } from './inline.js';
2
- import { createSlugger, footnoteId, isBlank, normalizeInput, normalizeReferenceLabel, plainText, stripIndent } from './utils.js';
2
+ import { createSlugger, footnoteId, isBlank, normalizeInput, normalizeReferenceLabel, parseDestination, plainText, stripIndent } from './utils.js';
3
3
  const maxBlockDepth = 64;
4
4
  export function parseMarkdown(markdown, options = {}) {
5
5
  const normalized = normalizeInput(markdown);
@@ -22,14 +22,15 @@ export function parseMarkdown(markdown, options = {}) {
22
22
  const parseOptions = hasReferences || hasFootnotes
23
23
  ? {
24
24
  ...options,
25
- ...(hasReferences ? { references: definitions.references } : {}),
26
- ...(hasFootnotes ? { footnotes: definitions.footnotes, footnoteOrder, footnoteCounts } : {}),
25
+ ...(hasReferences && { references: definitions.references }),
26
+ ...(hasFootnotes && { footnotes: definitions.footnotes, footnoteOrder, footnoteCounts }),
27
27
  }
28
28
  : options;
29
- const parser = new BlockParser(lines, parseOptions, createSlugger());
29
+ const slugger = createSlugger();
30
+ const parser = createBlockParser(lines, parseOptions, slugger);
30
31
  const children = parser.parse();
31
32
  if (hasFootnotes && footnoteOrder.length > 0) {
32
- children.push(createFootnotesBlock(definitions.footnotes, footnoteOrder, parseOptions));
33
+ children.push(createFootnotesBlock(definitions.footnotes, footnoteOrder, parseOptions, slugger));
33
34
  }
34
35
  let document = frontmatter === undefined ? { type: 'root', children } : { type: 'root', frontmatter, children };
35
36
  for (const extension of parseOptions.extensions ?? []) {
@@ -37,88 +38,86 @@ export function parseMarkdown(markdown, options = {}) {
37
38
  }
38
39
  return document;
39
40
  }
40
- class BlockParser {
41
- lines;
42
- options;
43
- slugger;
44
- budget;
45
- index = 0;
46
- constructor(lines, options, slugger, budget = { depth: 0 }) {
47
- this.lines = lines;
48
- this.options = options;
49
- this.slugger = slugger;
50
- this.budget = budget;
51
- }
52
- parse() {
53
- if (this.budget.depth >= maxBlockDepth) {
54
- const value = this.lines.slice(this.index).join('\n');
55
- this.index = this.lines.length;
56
- return value ? [{ type: 'paragraph', children: parseInline(value, this.options) }] : [];
41
+ function createBlockParser(inputLines, options, slugger, budget = { depth: 0 }) {
42
+ let cursor = 0;
43
+ let looseBlocks = false;
44
+ return {
45
+ parse,
46
+ get loose() {
47
+ return looseBlocks;
48
+ },
49
+ };
50
+ function parse() {
51
+ if (budget.depth >= maxBlockDepth) {
52
+ const value = inputLines.slice(cursor).join('\n');
53
+ cursor = inputLines.length;
54
+ return value ? [{ type: 'paragraph', children: parseInline(value, options) }] : [];
57
55
  }
58
- this.budget.depth++;
56
+ budget.depth++;
59
57
  const nodes = [];
60
- while (this.index < this.lines.length) {
61
- if (isBlank(this.current())) {
62
- this.index++;
58
+ while (cursor < inputLines.length) {
59
+ if (isBlank(current())) {
60
+ if (nodes.length)
61
+ looseBlocks = true;
62
+ cursor++;
63
63
  continue;
64
64
  }
65
- const extensionNode = this.parseExtensionBlock();
65
+ const extensionNode = parseExtensionBlock();
66
66
  if (extensionNode) {
67
67
  nodes.push(extensionNode);
68
68
  continue;
69
69
  }
70
- const node = this.parseFence() ??
71
- this.parseHeading() ??
72
- this.parseThematicBreak() ??
73
- this.parseBlockquote() ??
74
- this.parseList() ??
75
- this.parseTable() ??
76
- this.parseHtmlBlock() ??
77
- this.parseParagraph();
70
+ // Letter-led text cannot open a fence, heading, rule, quote, or list.
71
+ // It can still be a table header, so table detection remains below.
72
+ const node = (/^[a-z]/i.test(current()) ? undefined : (parseFence() ??
73
+ parseHeading() ??
74
+ parseThematicBreak() ??
75
+ parseBlockquote() ??
76
+ parseList())) ??
77
+ parseTable() ??
78
+ parseHtmlBlock() ??
79
+ parseParagraph();
78
80
  nodes.push(node);
79
81
  }
80
- this.budget.depth--;
82
+ budget.depth--;
81
83
  return nodes;
82
84
  }
83
- parseExtensionBlock() {
84
- for (const extension of this.options.extensions ?? []) {
85
+ function parseExtensionBlock() {
86
+ for (const extension of options.extensions ?? []) {
85
87
  let consumed = 0;
86
88
  const node = extension.parseBlock?.({
87
- lines: this.lines,
88
- index: this.index,
89
- options: this.options,
90
- parseInline: value => parseInline(value, this.options),
91
- parseBlocks: value => new BlockParser(normalizeInput(value).split('\n'), this.options, this.slugger, this.budget).parse(),
89
+ lines: inputLines,
90
+ index: cursor,
91
+ options: options,
92
+ parseInline: value => parseInline(value, options),
93
+ parseBlocks: value => createBlockParser(normalizeInput(value).split('\n'), options, slugger, budget).parse(),
92
94
  consume: lines => {
93
95
  consumed = lines;
94
96
  },
95
97
  });
96
98
  if (node) {
97
- this.index += Math.max(consumed, 1);
99
+ cursor += Math.max(consumed, 1);
98
100
  return node;
99
101
  }
100
102
  }
101
103
  return undefined;
102
104
  }
103
- parseFence() {
104
- const match = this.current().match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
105
+ function parseFence() {
106
+ const match = current().match(/^( {0,3})(`{3,}|~{3,})(.*)$/);
105
107
  if (!match)
106
108
  return undefined;
107
- const fence = match[1];
108
- const marker = fence[0];
109
- const fenceSize = fence.length;
110
- const info = match[2].trim();
109
+ const fence = match[2];
110
+ const info = match[3].trim();
111
111
  const code = [];
112
- this.index++;
113
- while (this.index < this.lines.length) {
114
- const line = this.current();
115
- const close = line.match(/^ {0,3}(`{3,}|~{3,})\s*$/);
116
- if (close && close[1][0] === marker && close[1].length >= fenceSize) {
117
- this.index++;
112
+ cursor++;
113
+ while (cursor < inputLines.length) {
114
+ const line = current();
115
+ if (line.includes(fence) && /^ {0,3}(?:`+|~+)\s*$/.test(line)) {
116
+ cursor++;
118
117
  break;
119
118
  }
120
- code.push(line);
121
- this.index++;
119
+ code.push(stripIndent(line, match[1].length));
120
+ cursor++;
122
121
  }
123
122
  return {
124
123
  type: 'code',
@@ -126,58 +125,55 @@ class BlockParser {
126
125
  ...parseCodeInfo(info),
127
126
  };
128
127
  }
129
- parseHeading() {
130
- const match = this.current().match(/^ {0,3}(#{1,6})(?:[ \t]+(.*)|[ \t]*)$/);
128
+ function parseHeading() {
129
+ const match = current().match(/^ {0,3}(#{1,6})(?:[ \t]+(.*)|[ \t]*)$/);
131
130
  if (!match)
132
131
  return undefined;
133
132
  const depth = match[1].length;
134
133
  const rawValue = match[2] ?? '';
135
134
  const value = (/^#+[ \t]*$/.test(rawValue) ? '' : rawValue.replace(/[ \t]+#+[ \t]*$/, '')).trim();
136
- const children = parseInline(value, this.options);
137
- const id = this.createHeadingId(children);
138
- this.index++;
135
+ const children = parseInline(value, options);
136
+ const id = createHeadingId(children);
137
+ cursor++;
139
138
  return id ? { type: 'heading', depth, id, children } : { type: 'heading', depth, children };
140
139
  }
141
- parseThematicBreak() {
142
- if (!/^ {0,3}([-*_])(?:\s*\1){2,}\s*$/.test(this.current()))
140
+ function parseThematicBreak() {
141
+ if (!/^ {0,3}([-*_])(?:\s*\1){2,}\s*$/.test(current()))
143
142
  return undefined;
144
- this.index++;
143
+ cursor++;
145
144
  return { type: 'thematicBreak' };
146
145
  }
147
- parseBlockquote() {
148
- if (!/^ {0,3}>\s?/.test(this.current()))
146
+ function parseBlockquote() {
147
+ if (!/^ {0,3}>\s?/.test(current()))
149
148
  return undefined;
150
149
  const quoted = [];
151
- while (this.index < this.lines.length) {
152
- const line = this.current();
153
- if (isBlank(line)) {
154
- quoted.push('');
155
- this.index++;
156
- continue;
157
- }
150
+ while (cursor < inputLines.length) {
151
+ const line = current();
158
152
  const match = line.match(/^ {0,3}>\s?(.*)$/);
159
- if (!match)
160
- break;
161
- quoted.push(match[1]);
162
- this.index++;
153
+ if (!match) {
154
+ if (!isBlank(line))
155
+ break;
156
+ looseBlocks = true;
157
+ }
158
+ quoted.push(match?.[1] ?? '');
159
+ cursor++;
163
160
  }
164
161
  return {
165
162
  type: 'blockquote',
166
- children: new BlockParser(quoted, this.options, this.slugger, this.budget).parse(),
163
+ children: createBlockParser(quoted, options, slugger, budget).parse(),
167
164
  };
168
165
  }
169
- parseList() {
170
- const first = listMarker(this.current());
166
+ function parseList() {
167
+ const first = listMarker(current());
171
168
  if (!first)
172
169
  return undefined;
173
170
  const items = [];
174
171
  const ordered = first.ordered;
175
172
  const baseIndent = first.indent;
176
- const start = ordered ? first.number : undefined;
177
173
  let loose = false;
178
- while (this.index < this.lines.length) {
179
- const marker = listMarker(this.current());
180
- if (!marker || !sameListType(marker, first) || marker.indent !== baseIndent)
174
+ while (cursor < inputLines.length) {
175
+ const marker = listMarker(current());
176
+ if (!marker || marker.marker !== first.marker || marker.indent !== baseIndent)
181
177
  break;
182
178
  let firstLine = marker.content;
183
179
  const task = firstLine.match(/^\[([ xX])\]\s+(.*)$/);
@@ -185,45 +181,46 @@ class BlockParser {
185
181
  if (task)
186
182
  firstLine = task[2];
187
183
  const itemLines = [firstLine];
188
- this.index++;
189
- while (this.index < this.lines.length) {
190
- const line = this.current();
184
+ cursor++;
185
+ while (cursor < inputLines.length) {
186
+ const line = current();
191
187
  const nextMarker = listMarker(line);
192
188
  if (nextMarker && nextMarker.indent === baseIndent)
193
189
  break;
194
190
  if (isBlank(line)) {
195
- let nextIndex = this.index;
196
- while (nextIndex < this.lines.length && isBlank(this.lines[nextIndex]))
191
+ let nextIndex = cursor;
192
+ while (nextIndex < inputLines.length && isBlank(inputLines[nextIndex]))
197
193
  nextIndex++;
198
- const following = this.lines[nextIndex];
194
+ const following = inputLines[nextIndex];
199
195
  if (following === undefined)
200
196
  break;
201
197
  const followingMarker = listMarker(following);
202
198
  if (followingMarker?.indent === baseIndent) {
203
- if (!sameListType(followingMarker, first))
199
+ if (followingMarker.marker !== first.marker)
204
200
  break;
205
201
  loose = true;
206
- this.index = nextIndex;
202
+ cursor = nextIndex;
207
203
  break;
208
204
  }
209
205
  if (leadingSpaces(following) < marker.contentIndent)
210
206
  break;
211
- loose = true;
212
- itemLines.push('');
213
- this.index = nextIndex;
207
+ while (cursor < nextIndex)
208
+ itemLines.push(stripIndent(inputLines[cursor++], marker.contentIndent));
214
209
  continue;
215
210
  }
216
211
  if (leadingSpaces(line) >= marker.contentIndent) {
217
212
  itemLines.push(stripIndent(line, marker.contentIndent));
218
- this.index++;
213
+ cursor++;
219
214
  continue;
220
215
  }
221
- if (isBlockStart(line, this.next()))
216
+ if (isBlockStart(line, next()))
222
217
  break;
223
218
  itemLines.push(line.trimStart());
224
- this.index++;
219
+ cursor++;
225
220
  }
226
- const children = new BlockParser(itemLines, this.options, this.slugger, this.budget).parse();
221
+ const parser = createBlockParser(itemLines, options, slugger, budget);
222
+ const children = parser.parse();
223
+ loose ||= parser.loose;
227
224
  const item = { type: 'listItem', children };
228
225
  if (checked !== undefined)
229
226
  item.checked = checked;
@@ -232,133 +229,120 @@ class BlockParser {
232
229
  return {
233
230
  type: 'list',
234
231
  ordered,
235
- ...(ordered && start !== undefined ? { start } : {}),
232
+ ...(ordered && { start: first.number }),
236
233
  ...(loose && { loose: true }),
237
234
  items,
238
235
  };
239
236
  }
240
- parseTable() {
241
- const header = this.current();
242
- const delimiter = this.next();
237
+ function parseTable() {
238
+ const header = current();
239
+ const delimiter = next();
243
240
  if (!delimiter || !looksLikeTableHeader(header, delimiter))
244
241
  return undefined;
245
242
  const headerCells = splitTableRow(header);
246
243
  const align = splitTableRow(delimiter).map(parseAlign);
247
244
  const columns = headerCells.length;
248
245
  const rows = [];
249
- this.index += 2;
250
- while (this.index < this.lines.length) {
251
- const line = this.current();
252
- if (isBlank(line) || isBlockStart(line, this.next()))
246
+ cursor += 2;
247
+ while (cursor < inputLines.length) {
248
+ const line = current();
249
+ if (isBlank(line) || isBlockStart(line, next()))
253
250
  break;
254
251
  const values = splitTableRow(line);
255
- rows.push(Array.from({ length: columns }, (_, index) => cell(values[index] ?? '', this.options)));
256
- this.index++;
252
+ rows.push(Array.from({ length: columns }, (_, index) => cell(values[index] ?? '', options)));
253
+ cursor++;
257
254
  }
258
255
  return {
259
256
  type: 'table',
260
257
  align,
261
- header: headerCells.map(value => cell(value, this.options)),
258
+ header: headerCells.map(value => cell(value, options)),
262
259
  rows,
263
260
  };
264
261
  }
265
- parseHtmlBlock() {
266
- if (!this.options.allowHtml || !/^ {0,3}<([A-Za-z][\w:-]*|!--|\/[A-Za-z])/.test(this.current()))
262
+ function parseHtmlBlock() {
263
+ if (!options.allowHtml || !/^ {0,3}<([A-Za-z][\w:-]*|!--|\/[A-Za-z])/.test(current()))
267
264
  return undefined;
268
265
  const html = [];
269
- if (/^ {0,3}<!--/.test(this.current())) {
270
- while (this.index < this.lines.length) {
271
- const line = this.current();
266
+ if (/^ {0,3}<!--/.test(current())) {
267
+ while (cursor < inputLines.length) {
268
+ const line = current();
272
269
  html.push(line);
273
- this.index++;
270
+ cursor++;
274
271
  if (line.includes('-->'))
275
272
  break;
276
273
  }
277
274
  return { type: 'html', value: html.join('\n') };
278
275
  }
279
- while (this.index < this.lines.length && !isBlank(this.current())) {
280
- html.push(this.current());
281
- this.index++;
276
+ while (cursor < inputLines.length && !isBlank(current())) {
277
+ html.push(current());
278
+ cursor++;
282
279
  }
283
280
  return { type: 'html', value: html.join('\n') };
284
281
  }
285
- parseParagraph() {
282
+ function parseParagraph() {
286
283
  const lines = [];
287
- while (this.index < this.lines.length) {
288
- const line = this.current();
284
+ while (cursor < inputLines.length) {
285
+ const line = current();
289
286
  if (isBlank(line))
290
287
  break;
291
- if (lines.length > 0 && isBlockStart(line, this.next()))
288
+ if (lines.length > 0 && isBlockStart(line, next()))
292
289
  break;
293
290
  lines.push(line.trim());
294
- this.index++;
291
+ cursor++;
295
292
  }
296
293
  return {
297
294
  type: 'paragraph',
298
- children: parseInline(lines.join('\n'), this.options),
295
+ children: parseInline(lines.join('\n'), options),
299
296
  };
300
297
  }
301
- createHeadingId(children) {
302
- if (this.options.headingIds === false)
298
+ function createHeadingId(children) {
299
+ if (options.headingIds === false)
303
300
  return undefined;
304
301
  const text = plainText(children);
305
- if (typeof this.options.headingIds === 'function')
306
- return this.options.headingIds(text, this.index);
307
- return this.slugger(text);
302
+ if (typeof options.headingIds === 'function')
303
+ return options.headingIds(text, cursor);
304
+ return slugger(text);
308
305
  }
309
- current() {
310
- return this.lines[this.index] ?? '';
306
+ function current() {
307
+ return inputLines[cursor] ?? '';
311
308
  }
312
- next() {
313
- return this.lines[this.index + 1];
309
+ function next() {
310
+ return inputLines[cursor + 1];
314
311
  }
315
312
  }
316
313
  function parseCodeInfo(info) {
317
- if (!info)
318
- return {};
319
314
  const langMatch = info.match(/^([A-Za-z0-9_+.#-]+)/);
320
315
  const lang = langMatch?.[1];
321
316
  const meta = lang ? info.slice(lang.length).trim() : info;
322
- const titleMatch = meta.match(/(?:^|\s)(?:title|file)=(?:"([^"]+)"|'([^']+)'|([^\s}]+))/);
323
- const frameworkMatch = meta.match(/(?:^|\s)framework=(?:"([^"]+)"|'([^']+)'|([^\s}]+))/);
317
+ const title = meta.match(/(?:^|\s)(?:title|file)=(?:"([^"]+)"|'([^']+)'|([^\s}]+))/)?.slice(1).find(Boolean);
318
+ const framework = meta.match(/(?:^|\s)framework=(?:"([^"]+)"|'([^']+)'|([^\s}]+))/)?.slice(1).find(Boolean);
324
319
  const rangeMatch = meta.match(/\{([^}]+)\}|(?:^|\s)lines=([^\s]+)/);
325
- const highlightLines = parseLineRanges(rangeMatch?.[1] ?? rangeMatch?.[2] ?? '');
326
- const title = titleMatch ? titleMatch[1] ?? titleMatch[2] ?? titleMatch[3] : undefined;
327
- const framework = frameworkMatch ? frameworkMatch[1] ?? frameworkMatch[2] ?? frameworkMatch[3] : undefined;
320
+ const highlightLines = rangeMatch ? parseLineRanges(rangeMatch[1] ?? rangeMatch[2]) : [];
328
321
  return {
329
- ...(lang ? { lang } : {}),
330
- ...(meta ? { meta } : {}),
331
- ...(title ? { title, file: title } : {}),
332
- ...(framework ? { framework: framework.toLowerCase() } : {}),
333
- ...(highlightLines.length ? { highlightLines } : {}),
322
+ ...(lang && { lang }),
323
+ ...(meta && { meta }),
324
+ ...(title && { title, file: title }),
325
+ ...(framework && { framework: framework.toLowerCase() }),
326
+ ...(highlightLines.length && { highlightLines }),
334
327
  };
335
328
  }
336
329
  function extractDefinitions(lines) {
337
330
  const references = Object.create(null);
338
331
  const footnotes = Object.create(null);
339
- const footnoteIds = new Map();
332
+ const footnoteIds = createSlugger(footnoteId);
340
333
  const remaining = [];
341
- let fenceMarker;
342
- let fenceSize = 0;
334
+ let activeFence = '';
343
335
  let index = 0;
344
336
  while (index < lines.length) {
345
337
  const line = lines[index];
346
- const fence = line.match(/^ {0,3}(`{3,}|~{3,})/);
338
+ const fence = (!activeFence || line.includes(activeFence)) && line.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
347
339
  if (fence) {
348
- const marker = fence[1][0];
349
- if (!fenceMarker) {
350
- fenceMarker = marker;
351
- fenceSize = fence[1].length;
352
- }
353
- else if (marker === fenceMarker && fence[1].length >= fenceSize) {
354
- fenceMarker = undefined;
355
- fenceSize = 0;
356
- }
357
- remaining.push(line);
358
- index++;
359
- continue;
340
+ if (!activeFence)
341
+ activeFence = fence[1];
342
+ else if (fence[1].startsWith(activeFence) && isBlank(fence[2]))
343
+ activeFence = '';
360
344
  }
361
- if (!fenceMarker) {
345
+ else if (!activeFence) {
362
346
  const footnote = line.match(/^ {0,3}\[\^([^\]\n]+)\]:[ \t]*(.*)$/);
363
347
  if (footnote) {
364
348
  const label = footnote[1];
@@ -373,20 +357,14 @@ function extractDefinitions(lines) {
373
357
  }
374
358
  const key = normalizeReferenceLabel(label);
375
359
  if (!footnotes[key]) {
376
- const baseId = footnoteId(label) || 'footnote';
377
- const count = (footnoteIds.get(baseId) ?? 0) + 1;
378
- footnoteIds.set(baseId, count);
379
- footnotes[key] = { label, content: content.join('\n'), id: count === 1 ? baseId : `${baseId}-${count}` };
360
+ footnotes[key] = { label, content: content.join('\n'), id: footnoteIds(label) };
380
361
  }
381
362
  continue;
382
363
  }
383
- const definition = line.match(/^ {0,3}\[([^\]\n]+)\]:[ \t]*(\S+)(?:[ \t]+(?:"([^"]*)"|'([^']*)'|\(([^)]*)\)))?[ \t]*$/);
384
- if (definition) {
385
- const title = definition[3] ?? definition[4] ?? definition[5];
386
- references[normalizeReferenceLabel(definition[1])] = {
387
- href: definition[2].replace(/^<|>$/g, ''),
388
- ...(title !== undefined ? { title } : {}),
389
- };
364
+ const definition = line.match(/^ {0,3}\[([^\]\n]+)\]:[ \t]*(\S.*)$/);
365
+ const destination = definition && parseDestination(definition[2].trimEnd());
366
+ if (destination) {
367
+ references[normalizeReferenceLabel(definition[1])] ??= destination;
390
368
  index++;
391
369
  continue;
392
370
  }
@@ -396,7 +374,7 @@ function extractDefinitions(lines) {
396
374
  }
397
375
  return { lines: remaining, references, footnotes };
398
376
  }
399
- function createFootnotesBlock(footnotes, footnoteOrder, options) {
377
+ function createFootnotesBlock(footnotes, footnoteOrder, options, slugger) {
400
378
  const items = [];
401
379
  for (let index = 0; index < footnoteOrder.length; index++) {
402
380
  const key = footnoteOrder[index];
@@ -404,9 +382,9 @@ function createFootnotesBlock(footnotes, footnoteOrder, options) {
404
382
  if (!definition)
405
383
  continue;
406
384
  items.push({
407
- id: definition.id ?? (footnoteId(definition.label) || 'footnote'),
385
+ id: definition.id ?? footnoteId(definition.label),
408
386
  number: index + 1,
409
- children: new BlockParser(normalizeInput(definition.content).split('\n'), options, createSlugger()).parse(),
387
+ children: createBlockParser(normalizeInput(definition.content).split('\n'), options, slugger).parse(),
410
388
  });
411
389
  }
412
390
  for (const item of items) {
@@ -424,6 +402,9 @@ function parseLineRanges(value) {
424
402
  continue;
425
403
  const start = Number(match[1]);
426
404
  const end = Number(match[2] ?? match[1]);
405
+ // Larger integers can stop line++ from making progress.
406
+ if (end > Number.MAX_SAFE_INTEGER)
407
+ continue;
427
408
  for (let line = start; line <= end && line < start + 1000; line++)
428
409
  lines.add(line);
429
410
  }
@@ -437,25 +418,19 @@ function listMarker(line) {
437
418
  const ordered = /\d/.test(marker[0]);
438
419
  return {
439
420
  ordered,
440
- ...(ordered ? { number: Number.parseInt(marker, 10) } : {}),
421
+ ...(ordered && { number: Number.parseInt(marker, 10) }),
441
422
  indent: match[1].length,
442
423
  marker: ordered ? marker.at(-1) : marker,
443
424
  contentIndent: match[1].length + marker.length + (match[3]?.length ?? 1),
444
425
  content: match[4] ?? '',
445
426
  };
446
427
  }
447
- function sameListType(left, right) {
448
- return left.ordered === right.ordered && left.marker === right.marker;
449
- }
450
428
  function leadingSpaces(line) {
451
429
  return line.match(/^ */)?.[0].length ?? 0;
452
430
  }
453
431
  function isBlockStart(line, next) {
454
432
  const marker = listMarker(line);
455
- return (/^ {0,3}(`{3,}|~{3,})/.test(line) ||
456
- /^ {0,3}#{1,6}(?:\s+|$)/.test(line) ||
457
- /^ {0,3}([-*_])(?:\s*\1){2,}\s*$/.test(line) ||
458
- /^ {0,3}>\s?/.test(line) ||
433
+ return (/^ {0,3}(?:`{3,}|~{3,}|#{1,6}(?:\s|$)|([-*_])(?:\s*\1){2,}\s*$|>)/.test(line) ||
459
434
  (marker !== undefined && (!marker.ordered || marker.number === 1)) ||
460
435
  (!!next && looksLikeTableHeader(line, next)));
461
436
  }
@@ -466,14 +441,10 @@ function looksLikeTableHeader(header, delimiter) {
466
441
  return cells.length === splitTableRow(header).length && cells.every(cell => /^:?-+:?$/.test(cell.trim()));
467
442
  }
468
443
  function splitTableRow(value) {
469
- let row = value.trim();
470
- if (row.startsWith('|'))
471
- row = row.slice(1);
472
- if (row.endsWith('|'))
473
- row = row.slice(0, -1);
444
+ const row = value.trim();
474
445
  const cells = [];
475
446
  let current = '';
476
- for (let index = 0; index < row.length; index++) {
447
+ for (let index = row.startsWith('|') ? 1 : 0; index < row.length; index++) {
477
448
  const char = row[index];
478
449
  if (char === '\\' && row[index + 1] === '|') {
479
450
  current += '|';
@@ -487,7 +458,8 @@ function splitTableRow(value) {
487
458
  }
488
459
  current += char;
489
460
  }
490
- cells.push(current.trim());
461
+ if (current || !row.endsWith('|') || !cells.length)
462
+ cells.push(current.trim());
491
463
  return cells;
492
464
  }
493
465
  function parseAlign(value) {
@@ -1 +1 @@
1
- {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,aAAa,EAAE,GAAG,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAElG,OAAO,KAAK,EAAE,SAAS,EAAmC,UAAU,EAAE,aAAa,EAAE,aAAa,EAAiB,MAAM,YAAY,CAAA;AAErI,KAAK,oBAAoB,GAAG,MAAM,GAAG,CAAC,iBAAiB,CAAA;AACvD,KAAK,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;AAExE,MAAM,MAAM,sBAAsB,CAAC,OAAO,SAAS,oBAAoB,IAAI,wBAAwB,CAAC,OAAO,CAAC,CAAA;AAE5G,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC;KACtC,OAAO,IAAI,oBAAoB,GAAG,oBAAoB,GAAG,aAAa,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;CACzG,CAAC,GACA,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,CAAA;AAEzD,MAAM,WAAW,oBAAqB,SAAQ,aAAa;IACzD,UAAU,CAAC,EAAE,YAAY,CAAA;CAC1B;AAED,MAAM,WAAW,aAAc,SAAQ,oBAAoB;IACzD,QAAQ,EAAE,aAAa,CAAA;CACxB;AAED,wBAAgB,QAAQ,CAAC,EAAE,QAAQ,EAAE,GAAG,OAAO,EAAE,EAAE,aAAa,GAAG,YAAY,CAE9E;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,GAAE,oBAAyB,GAAG,SAAS,CAGvG;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,GAAE,oBAAyB,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,YAAY,CA+DhH;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,GAAE,oBAAyB,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAwC/G"}
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,aAAa,EAAE,GAAG,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGlG,OAAO,KAAK,EAAE,SAAS,EAAwD,UAAU,EAAE,aAAa,EAAE,aAAa,EAAiB,MAAM,YAAY,CAAA;AAE1J,KAAK,oBAAoB,GAAG,MAAM,GAAG,CAAC,iBAAiB,CAAA;AACvD,KAAK,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;AAExE,MAAM,MAAM,sBAAsB,CAAC,OAAO,SAAS,oBAAoB,IAAI,wBAAwB,CAAC,OAAO,CAAC,CAAA;AAE5G,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC;KACtC,OAAO,IAAI,oBAAoB,GAAG,oBAAoB,GAAG,aAAa,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;CACzG,CAAC,GACA,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,CAAA;AAEzD,MAAM,WAAW,oBAAqB,SAAQ,aAAa;IACzD,UAAU,CAAC,EAAE,YAAY,CAAA;CAC1B;AAED,MAAM,WAAW,aAAc,SAAQ,oBAAoB;IACzD,QAAQ,EAAE,aAAa,CAAA;CACxB;AAED,wBAAgB,QAAQ,CAAC,EAAE,QAAQ,EAAE,GAAG,OAAO,EAAE,EAAE,aAAa,GAAG,YAAY,CAE9E;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,GAAE,oBAAyB,GAAG,SAAS,CAGvG;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,GAAE,oBAAyB,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,YAAY,CA+DhH;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,GAAE,oBAAyB,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CA0C/G"}