@tanstack/markdown 0.0.12 → 0.0.14

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/inline.js CHANGED
@@ -1,7 +1,6 @@
1
- import { footnoteId, normalizeReferenceLabel, sanitizeUrl } from './utils.js';
1
+ import { footnoteId, normalizeReferenceLabel, parseDestination, plainText, sanitizeUrl } from './utils.js';
2
2
  export function parseInline(value, options = {}) {
3
- const nodes = parseInlineRaw(value, options);
4
- let result = mergeText(nodes);
3
+ let result = parseInlineRaw(value, options);
5
4
  for (const extension of options.extensions ?? []) {
6
5
  result = extension.transformInline?.(result, { options }) ?? result;
7
6
  }
@@ -9,7 +8,8 @@ export function parseInline(value, options = {}) {
9
8
  }
10
9
  const maxInlineDepth = 32;
11
10
  const scansPerCharacter = 16;
12
- function parseInlineRaw(value, options, budget = { scans: Math.max(value.length * scansPerCharacter, 1024), depth: 0 }) {
11
+ const inlineMarker = /[\\`!\[_*~<]/g;
12
+ function parseInlineRaw(value, options, budget = { scans: Math.max(value.length * scansPerCharacter, 1024), depth: 0, links: 0 }) {
13
13
  if (budget.depth >= maxInlineDepth)
14
14
  return value ? [{ type: 'text', value }] : [];
15
15
  budget.depth++;
@@ -32,7 +32,7 @@ function parseInlineRaw(value, options, budget = { scans: Math.max(value.length
32
32
  index += 2;
33
33
  continue;
34
34
  }
35
- if (next && /[\\`*_[\]{}()#+\-.!|~<>]/.test(next)) {
35
+ if (next && /[!-/:-@\[-`{-~]/.test(next)) {
36
36
  text += next;
37
37
  index += 2;
38
38
  continue;
@@ -45,7 +45,8 @@ function parseInlineRaw(value, options, budget = { scans: Math.max(value.length
45
45
  pushText();
46
46
  nodes.push({
47
47
  type: 'inlineCode',
48
- value: value.slice(index + tickCount, close).replace(/\s+/g, ' ').trim(),
48
+ // Strip one padding space at each end, except in an all-space span.
49
+ value: value.slice(index + tickCount, close).replace(/\n/g, ' ').replace(/^ (?! *$)(.*) $/s, '$1'),
49
50
  });
50
51
  index = close + tickCount;
51
52
  continue;
@@ -54,44 +55,44 @@ function parseInlineRaw(value, options, budget = { scans: Math.max(value.length
54
55
  index += tickCount;
55
56
  continue;
56
57
  }
57
- if (char === '!' && next === '[') {
58
- const parsed = parseLinkish(value, index + 1, options, budget);
59
- if (parsed) {
60
- pushText();
61
- nodes.push({
62
- type: 'image',
63
- src: sanitizeUrl(parsed.href),
64
- alt: textFromMarkdown(parsed.label, budget),
65
- ...(parsed.title ? { title: parsed.title } : {}),
66
- });
67
- index = parsed.end;
68
- continue;
69
- }
70
- }
71
- if (char === '[') {
72
- const footnote = parseFootnoteReference(value, index, options, budget);
58
+ if (char === '[' || (char === '!' && next === '[')) {
59
+ const image = char === '!';
60
+ const footnote = next === '^' && parseFootnoteReference(value, index, options, budget);
73
61
  if (footnote) {
74
62
  pushText();
75
63
  nodes.push(footnote.node);
64
+ budget.links++;
76
65
  index = footnote.end;
77
66
  continue;
78
67
  }
79
- const parsed = parseLinkish(value, index, options, budget);
68
+ const parsed = parseLinkish(value, index + Number(image), options, budget);
80
69
  if (parsed) {
70
+ const links = budget.links;
71
+ const children = parseInlineRaw(parsed.label, image ? (options.references ? { references: options.references } : {}) : options, budget);
72
+ const nested = !image && budget.links !== links;
81
73
  const href = sanitizeUrl(parsed.href);
82
- pushText();
83
- if (href) {
74
+ if (image || (!nested && (href || !parsed.href))) {
75
+ pushText();
84
76
  nodes.push({
85
- type: 'link',
86
- href,
77
+ ...(image ? { type: 'image', src: href, alt: plainText(children) } : { type: 'link', href, children }),
87
78
  ...(parsed.title ? { title: parsed.title } : {}),
88
- children: parseInlineRaw(parsed.label, options, budget),
89
79
  });
90
80
  }
91
81
  else {
92
- nodes.push(...parseInlineRaw(parsed.label, options, budget));
82
+ if (nested)
83
+ text += '[';
84
+ for (const child of children) {
85
+ if (child.type === 'text')
86
+ text += child.value;
87
+ else {
88
+ pushText();
89
+ nodes.push(child);
90
+ }
91
+ }
93
92
  }
94
- index = parsed.end;
93
+ // An inner link disables its outer opener, but not the trailing markup.
94
+ budget.links = image ? links : budget.links + 1;
95
+ index = nested ? index + parsed.label.length + 1 : parsed.end;
95
96
  continue;
96
97
  }
97
98
  }
@@ -107,65 +108,28 @@ function parseInlineRaw(value, options, budget = { scans: Math.max(value.length
107
108
  continue;
108
109
  }
109
110
  }
110
- if ((char === '*' && next === '*') || (char === '_' && next === '_')) {
111
- const close = char === '_' && !canUseUnderscore(value, index, 2, true) ? -1 : findDelimiter(value, index + 2, char + char, budget);
112
- if (close !== -1) {
113
- pushText();
114
- nodes.push({
115
- type: 'strong',
116
- children: parseInlineRaw(value.slice(index + 2, close), options, budget),
117
- });
118
- index = close + 2;
119
- continue;
120
- }
121
- text += char + next;
122
- index += 2;
123
- continue;
124
- }
125
111
  if (char === '~' && next === '~' && value[index + 2] === '~') {
126
112
  const run = countRun(value, index, '~');
127
113
  text += value.slice(index, index + run);
128
114
  index += run;
129
115
  continue;
130
116
  }
131
- if (char === '~' && next === '~') {
132
- const close = findDelimiter(value, index + 2, '~~', budget);
117
+ if (char === '*' || char === '_' || char === '~') {
118
+ const size = next === char ? 2 : 1;
119
+ const close = char === '_' && !canUseUnderscore(value, index, size, true) ? -1 : findDelimiter(value, index + size, char.repeat(size), budget);
133
120
  if (close !== -1) {
134
121
  pushText();
135
122
  nodes.push({
136
- type: 'strike',
137
- children: parseInlineRaw(value.slice(index + 2, close), options, budget),
123
+ type: char === '~' ? 'strike' : size === 2 ? 'strong' : 'emphasis',
124
+ children: parseInlineRaw(value.slice(index + size, close), options, budget),
138
125
  });
139
- index = close + 2;
140
- continue;
126
+ index = close + size;
141
127
  }
142
- text += '~~';
143
- index += 2;
144
- continue;
145
- }
146
- if (char === '~') {
147
- const close = findSingleTildeDelimiter(value, index + 1, budget);
148
- if (close !== -1) {
149
- pushText();
150
- nodes.push({
151
- type: 'strike',
152
- children: parseInlineRaw(value.slice(index + 1, close), options, budget),
153
- });
154
- index = close + 1;
155
- continue;
156
- }
157
- }
158
- if (char === '*' || char === '_') {
159
- const close = char === '_' && !canUseUnderscore(value, index, 1, true) ? -1 : findDelimiter(value, index + 1, char, budget);
160
- if (close !== -1) {
161
- pushText();
162
- nodes.push({
163
- type: 'emphasis',
164
- children: parseInlineRaw(value.slice(index + 1, close), options, budget),
165
- });
166
- index = close + 1;
167
- continue;
128
+ else {
129
+ text += char.repeat(size);
130
+ index += size;
168
131
  }
132
+ continue;
169
133
  }
170
134
  if (char === '<' && options.allowHtml) {
171
135
  const close = findCharacter(value, index + 1, '>', budget);
@@ -176,16 +140,17 @@ function parseInlineRaw(value, options, budget = { scans: Math.max(value.length
176
140
  continue;
177
141
  }
178
142
  }
179
- text += char;
180
- index++;
143
+ // Reset after recursive parsing; exhausted budgets still allow escapes.
144
+ inlineMarker.lastIndex = index + 1;
145
+ const end = budget.scans > 0 ? inlineMarker.exec(value)?.index ?? value.length : value.indexOf('\\', index + 1);
146
+ text += value.slice(index, end < 0 ? value.length : end);
147
+ index = end < 0 ? value.length : end;
181
148
  }
182
149
  pushText();
183
150
  budget.depth--;
184
151
  return nodes;
185
152
  }
186
153
  function parseFootnoteReference(value, open, options, budget) {
187
- if (value[open + 1] !== '^')
188
- return undefined;
189
154
  const close = findCharacter(value, open + 2, ']', budget);
190
155
  if (close === -1)
191
156
  return undefined;
@@ -194,19 +159,17 @@ function parseFootnoteReference(value, open, options, budget) {
194
159
  const definition = options.footnotes?.[key];
195
160
  if (!definition || !options.footnoteOrder)
196
161
  return undefined;
197
- let orderIndex = options.footnoteOrder.indexOf(key);
198
- if (orderIndex === -1) {
199
- options.footnoteOrder.push(key);
200
- orderIndex = options.footnoteOrder.length - 1;
201
- }
162
+ let number = options.footnoteOrder.indexOf(key) + 1;
163
+ if (!number)
164
+ number = options.footnoteOrder.push(key);
202
165
  const referenceIndex = (options.footnoteCounts?.[key] ?? 0) + 1;
203
166
  if (options.footnoteCounts)
204
167
  options.footnoteCounts[key] = referenceIndex;
205
168
  return {
206
169
  node: {
207
170
  type: 'footnoteReference',
208
- id: definition.id ?? (footnoteId(definition.label) || 'footnote'),
209
- number: orderIndex + 1,
171
+ id: definition.id ?? footnoteId(definition.label),
172
+ number,
210
173
  ...(referenceIndex > 1 ? { referenceIndex } : {}),
211
174
  },
212
175
  end: close + 1,
@@ -217,72 +180,49 @@ function parseLinkish(value, open, options, budget) {
217
180
  if (closeBracket === -1)
218
181
  return undefined;
219
182
  const label = value.slice(open + 1, closeBracket);
220
- if (value[closeBracket + 1] === '(') {
221
- const closeParen = findBalanced(value, closeBracket + 1, '(', ')', budget);
183
+ let end = closeBracket + 1;
184
+ let definition;
185
+ if (value[end] === '(') {
186
+ const closeParen = findBalanced(value, end, '(', ')', budget);
222
187
  if (closeParen === -1)
223
188
  return undefined;
224
- const destination = value.slice(closeBracket + 2, closeParen).trim();
225
- const parsed = parseDestination(destination);
226
- if (!parsed)
227
- return undefined;
228
- return {
229
- label,
230
- href: parsed.href,
231
- ...(parsed.title ? { title: parsed.title } : {}),
232
- end: closeParen + 1,
233
- };
189
+ definition = parseDestination(value.slice(end + 1, closeParen).trim());
190
+ end = closeParen + 1;
234
191
  }
235
- if (value[closeBracket + 1] === '[') {
236
- const closeReference = findBalanced(value, closeBracket + 1, '[', ']', budget);
192
+ else if (value[end] === '[') {
193
+ const closeReference = findBalanced(value, end, '[', ']', budget);
237
194
  if (closeReference === -1)
238
195
  return undefined;
239
- const referenceLabel = value.slice(closeBracket + 2, closeReference);
240
- const definition = options.references?.[normalizeReferenceLabel(referenceLabel || label)];
241
- if (!definition)
242
- return undefined;
243
- return {
244
- label,
245
- href: definition.href,
246
- ...(definition.title ? { title: definition.title } : {}),
247
- end: closeReference + 1,
248
- };
196
+ definition = options.references?.[normalizeReferenceLabel(value.slice(end + 1, closeReference) || label)];
197
+ end = closeReference + 1;
198
+ }
199
+ else {
200
+ definition = options.references?.[normalizeReferenceLabel(label)];
249
201
  }
250
- const definition = options.references?.[normalizeReferenceLabel(label)];
251
202
  if (definition) {
252
203
  return {
204
+ ...definition,
253
205
  label,
254
- href: definition.href,
255
- ...(definition.title ? { title: definition.title } : {}),
256
- end: closeBracket + 1,
206
+ end,
257
207
  };
258
208
  }
259
209
  return undefined;
260
210
  }
261
211
  function isInlineHtml(value) {
262
- if (/^<!--(?:[\s\S]*--|-?)>$/.test(value))
263
- return true;
264
- if (/^<\?[\s\S]*\?>$/.test(value) || /^<![A-Z][\s\S]*>$/.test(value) || /^<!\[CDATA\[[\s\S]*\]\]>$/.test(value))
265
- return true;
266
- if (/^<\/[A-Za-z][\w:-]*\s*>$/.test(value))
267
- return true;
268
- return /^<[A-Za-z][\w:-]*(?:\s+[A-Za-z_:][\w:.-]*(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+))?)*\s*\/?>$/.test(value);
269
- }
270
- function parseDestination(value) {
271
- const match = value.match(/^(\S+)(?:\s+["']([^"']*)["'])?$/);
272
- if (!match)
273
- return { href: value };
274
- return {
275
- href: match[1].replace(/^<|>$/g, ''),
276
- ...(match[2] ? { title: match[2] } : {}),
277
- };
212
+ return /^<(?:!--(?:[\s\S]*--|-?)|\?[\s\S]*\?|![A-Z][\s\S]*|!\[CDATA\[[\s\S]*\]\]|\/[A-Za-z][\w:-]*\s*|[A-Za-z][\w:-]*(?:\s+[A-Za-z_:][\w:.-]*(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+))?)*\s*\/?)>$/.test(value);
278
213
  }
279
214
  function findBalanced(value, openIndex, open, close, budget) {
215
+ // Reject missing closers with a native search, charging the same shared budget.
216
+ if (findCharacter(value, openIndex + 1, close, budget) === -1)
217
+ return -1;
280
218
  let depth = 0;
281
219
  for (let index = openIndex; index < value.length; index++) {
282
- if (!takeScan(budget))
220
+ if (--budget.scans < 0)
283
221
  return -1;
284
- if (value[index - 1] === '\\')
222
+ if (value[index] === '\\') {
223
+ index++;
285
224
  continue;
225
+ }
286
226
  if (value[index] === open)
287
227
  depth++;
288
228
  if (value[index] === close) {
@@ -296,102 +236,63 @@ function findBalanced(value, openIndex, open, close, budget) {
296
236
  function countRun(value, index, char, budget) {
297
237
  let count = 0;
298
238
  while (value[index + count] === char) {
299
- if (budget && !takeScan(budget))
239
+ if (budget && --budget.scans < 0)
300
240
  break;
301
241
  count++;
302
242
  }
303
243
  return count;
304
244
  }
305
245
  function findClosingRun(value, start, char, count, budget) {
306
- for (let index = start; index < value.length; index++) {
307
- if (!takeScan(budget))
308
- return -1;
309
- if (value[index] !== char)
310
- continue;
311
- if (countRun(value, index, char, budget) >= count)
246
+ let index = start;
247
+ while ((index = findCharacter(value, index, char, budget)) !== -1) {
248
+ const size = countRun(value, index, char, budget);
249
+ if (size === count)
312
250
  return index;
251
+ index += size;
313
252
  }
314
253
  return -1;
315
254
  }
316
255
  function findDelimiter(value, start, delimiter, budget) {
317
- for (let index = start; index < value.length; index++) {
318
- if (!takeScan(budget))
319
- return -1;
320
- if (value[index - 1] === '\\')
321
- continue;
322
- if (delimiter === '~~' && (value[index - 1] === '~' || value[index + 2] === '~'))
323
- continue;
324
- if (delimiter[0] === '_' && !canUseUnderscore(value, index, delimiter.length, false))
325
- continue;
326
- if (value.startsWith(delimiter, index))
327
- return index;
328
- }
329
- return -1;
330
- }
331
- function findSingleTildeDelimiter(value, start, budget) {
332
- if (isWhitespace(value[start] ?? '') || /\d/.test(value[start] ?? ''))
256
+ if (delimiter === '~' && /[\s\d]/.test(value[start] ?? ''))
333
257
  return -1;
334
258
  for (let index = start; index < value.length; index++) {
335
- if (!takeScan(budget))
259
+ if (--budget.scans < 0)
336
260
  return -1;
337
- if (value[index - 1] === '\\')
338
- continue;
339
- if (value[index] !== '~')
261
+ if (value[index] === '\\') {
262
+ index++;
340
263
  continue;
341
- if (value[index - 1] === '~')
264
+ }
265
+ if (!value.startsWith(delimiter, index))
342
266
  continue;
343
- if (value[index + 1] === '~')
267
+ if (delimiter[0] === '~') {
268
+ if (value[index - 1] === '~' || value[index + delimiter.length] === '~')
269
+ continue;
270
+ if (delimiter === '~' && /\s/.test(value[index - 1] ?? ''))
271
+ return -1;
272
+ }
273
+ else if (value[index + 1] === delimiter) {
274
+ const size = countRun(value, index, delimiter, budget);
275
+ const close = findDelimiter(value, index + size, delimiter.repeat(size), budget);
276
+ if (close > index + size) {
277
+ index = close + size - 1;
278
+ continue;
279
+ }
280
+ }
281
+ if (delimiter[0] === '_' && !canUseUnderscore(value, index, delimiter.length, false))
344
282
  continue;
345
- if (isWhitespace(value[index - 1] ?? ''))
346
- return -1;
347
283
  return index;
348
284
  }
349
285
  return -1;
350
286
  }
351
287
  function findCharacter(value, start, character, budget) {
352
- for (let index = start; index < value.length; index++) {
353
- if (!takeScan(budget))
354
- return -1;
355
- if (value[index] === character)
356
- return index;
357
- }
358
- return -1;
359
- }
360
- function takeScan(budget) {
361
288
  if (budget.scans <= 0)
362
- return false;
363
- budget.scans--;
364
- return true;
289
+ return -1;
290
+ const index = value.indexOf(character, start);
291
+ budget.scans -= (index < 0 ? value.length : index + 1) - start;
292
+ return budget.scans < 0 ? -1 : index;
365
293
  }
366
294
  function canUseUnderscore(value, index, size, opening) {
367
- const before = value[index - 1];
368
- const after = value[index + size];
369
- const leftFlanking = !isDelimiterWhitespace(after) && (!isPunctuation(after) || isDelimiterWhitespace(before) || isPunctuation(before));
370
- const rightFlanking = !isDelimiterWhitespace(before) && (!isPunctuation(before) || isDelimiterWhitespace(after) || isPunctuation(after));
371
- return opening ? leftFlanking && (!rightFlanking || isPunctuation(before)) : rightFlanking && (!leftFlanking || isPunctuation(after));
372
- }
373
- function isDelimiterWhitespace(value) {
374
- return value === undefined || /\s/.test(value);
375
- }
376
- function isPunctuation(value) {
377
- return value !== undefined && /[^\p{L}\p{N}\s]/u.test(value);
378
- }
379
- function isWhitespace(value) {
380
- return /\s/.test(value);
381
- }
382
- function textFromMarkdown(value, budget) {
383
- return parseInlineRaw(value, {}, budget).map(node => (node.type === 'text' || node.type === 'inlineCode' ? node.value : '')).join('');
384
- }
385
- function mergeText(nodes) {
386
- const result = [];
387
- for (const node of nodes) {
388
- const previous = result.at(-1);
389
- if (previous?.type === 'text' && node.type === 'text') {
390
- previous.value += node.value;
391
- }
392
- else {
393
- result.push(node);
394
- }
395
- }
396
- return result;
295
+ const inside = value[opening ? index + size : index - 1];
296
+ const outside = value[opening ? index - 1 : index + size];
297
+ return inside !== undefined && !/\s/.test(inside) && (outside === undefined || /[^\p{L}\p{N}]/u.test(outside));
397
298
  }
@@ -1 +1 @@
1
- {"version":3,"file":"octane.d.ts","sourceRoot":"","sources":["../src/octane.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAE1E,OAAO,KAAK,EAAE,SAAS,EAAmC,UAAU,EAAE,aAAa,EAAE,aAAa,EAAiB,MAAM,YAAY,CAAA;AAErI,KAAK,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;AAExE,MAAM,WAAW,qBAAsB,SAAQ,aAAa;IAC1D,UAAU,CAAC,EAAE,YAAY,CAAA;CAC1B;AAED,MAAM,WAAW,aAAc,SAAQ,qBAAqB;IAC1D,QAAQ,EAAE,aAAa,CAAA;CACxB;AAED,wBAAgB,QAAQ,CAAC,EAAE,QAAQ,EAAE,GAAG,OAAO,EAAE,EAAE,aAAa,GAAG,iBAAiB,CAEnF;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,GAAE,qBAA0B,GAAG,UAAU,EAAE,CAG5G;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,GAAE,qBAA0B,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,iBAAiB,CA+DvH;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,GAAE,qBAA0B,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,UAAU,CAwClH"}
1
+ {"version":3,"file":"octane.d.ts","sourceRoot":"","sources":["../src/octane.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAG1E,OAAO,KAAK,EAAE,SAAS,EAAmC,UAAU,EAAE,aAAa,EAAE,aAAa,EAAiB,MAAM,YAAY,CAAA;AAErI,KAAK,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;AAExE,MAAM,WAAW,qBAAsB,SAAQ,aAAa;IAC1D,UAAU,CAAC,EAAE,YAAY,CAAA;CAC1B;AAED,MAAM,WAAW,aAAc,SAAQ,qBAAqB;IAC1D,QAAQ,EAAE,aAAa,CAAA;CACxB;AAED,wBAAgB,QAAQ,CAAC,EAAE,QAAQ,EAAE,GAAG,OAAO,EAAE,EAAE,aAAa,GAAG,iBAAiB,CAEnF;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,GAAE,qBAA0B,GAAG,UAAU,EAAE,CAG5G;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,GAAE,qBAA0B,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,iBAAiB,CA+DvH;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,GAAE,qBAA0B,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,UAAU,CAwClH"}
package/dist/octane.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Fragment, createElement } from 'octane';
2
2
  import { parseMarkdown } from './parser.js';
3
+ import { footnoteReferenceId } from './utils.js';
3
4
  export function Markdown({ children, ...options }) {
4
5
  return createElement(Fragment, null, ...renderMarkdownOctane(children, options));
5
6
  }
@@ -17,7 +18,7 @@ export function renderBlockOctane(node, options = {}, key) {
17
18
  return renderCodeBlockOctane(node, options, key);
18
19
  case 'list': {
19
20
  const tag = node.ordered ? 'ol' : 'ul';
20
- return h(options, tag, { key, ...(node.ordered && node.start && node.start !== 1 ? { start: node.start } : {}) }, node.items.map((item, index) => h(options, 'li', { key: index }, renderListItemChildrenOctane(item.children, item.checked, node.loose, options, `${index}`))));
21
+ return h(options, tag, { key, ...(node.ordered && node.start !== undefined && node.start !== 1 ? { start: node.start } : {}) }, node.items.map((item, index) => h(options, 'li', { key: index }, renderListItemChildrenOctane(item.children, item.checked, node.loose, options, `${index}`))));
21
22
  }
22
23
  case 'blockquote':
23
24
  return h(options, 'blockquote', { key }, node.children.map((child, index) => renderBlockOctane(child, options, `${key}:${index}`)));
@@ -53,7 +54,7 @@ export function renderInlineOctane(node, options = {}, key) {
53
54
  return h(options, 'del', { key }, renderInlines(node.children, options));
54
55
  case 'footnoteReference':
55
56
  return h(options, 'sup', { key }, h(options, 'a', {
56
- id: `user-content-fnref-${footnoteReferenceId(node)}`,
57
+ id: `user-content-fnref-${footnoteReferenceId(node.id, node.referenceIndex)}`,
57
58
  'data-footnote-ref': '',
58
59
  'aria-describedby': 'footnote-label',
59
60
  href: `#user-content-fn-${node.id}`,
@@ -99,8 +100,7 @@ function renderCodeBlockOctane(node, options, key) {
99
100
  return h(options, 'figure', { key, className: 'tm-code-frame', 'data-lang': lang }, h(options, 'figcaption', null, node.title), pre);
100
101
  }
101
102
  function renderListItemChildrenOctane(children, checked, loose, options, key) {
102
- const [first, ...rest] = children;
103
- const task = checked === undefined
103
+ let result = checked === undefined
104
104
  ? []
105
105
  : [
106
106
  h(options, 'input', {
@@ -112,17 +112,18 @@ function renderListItemChildrenOctane(children, checked, loose, options, key) {
112
112
  }),
113
113
  ' ',
114
114
  ];
115
- if (first?.type === 'paragraph') {
116
- const content = [...task, ...renderInlines(first.children, options)];
117
- return [
118
- ...(loose ? [h(options, 'p', { key: `${key}:paragraph` }, content)] : content),
119
- ...rest.flatMap((child, childIndex) => renderListChildOctane(child, loose, options, `${key}:${childIndex + 1}`)),
120
- ];
115
+ for (let index = 0; index < children.length; index++) {
116
+ const child = children[index];
117
+ if (child.type === 'paragraph' && (!loose || index === 0)) {
118
+ for (const inline of renderInlines(child.children, options))
119
+ result.push(inline);
120
+ if (loose)
121
+ result = [h(options, 'p', { key: `${key}:paragraph` }, result)];
122
+ }
123
+ else
124
+ result.push(renderBlockOctane(child, options, `${key}:${index}`));
121
125
  }
122
- return [...task, ...children.flatMap((child, childIndex) => renderListChildOctane(child, loose, options, `${key}:${childIndex}`))];
123
- }
124
- function renderListChildOctane(child, loose, options, key) {
125
- return !loose && child.type === 'paragraph' ? renderInlines(child.children, options) : [renderBlockOctane(child, options, key)];
126
+ return result;
126
127
  }
127
128
  function renderTableCellOctane(tag, cell, align, options, key) {
128
129
  return h(options, tag, { key, ...(align ? { style: { textAlign: align } } : {}) }, renderInlines(cell.children, options));
@@ -133,19 +134,20 @@ function renderFootnotesOctane(items, options, key) {
133
134
  function renderFootnoteItemOctane(item, options) {
134
135
  const lastIndex = item.children.length - 1;
135
136
  const backrefs = renderFootnoteBackrefsOctane(item, options);
136
- if (lastIndex < 0)
137
- return [h(options, 'p', { key: 'backref-wrapper' }, backrefs.slice(1))];
138
- return item.children.map((child, index) => {
137
+ const result = item.children.map((child, index) => {
139
138
  if (index === lastIndex && child.type === 'paragraph') {
140
139
  return h(options, 'p', { key: index }, renderInlines(child.children, options), backrefs);
141
140
  }
142
141
  return renderBlockOctane(child, options, `${index}`);
143
142
  });
143
+ if (item.children[lastIndex]?.type !== 'paragraph')
144
+ result.push(h(options, 'p', { key: 'backref-wrapper' }, backrefs.slice(1)));
145
+ return result;
144
146
  }
145
147
  function renderFootnoteBackrefsOctane(item, options) {
146
148
  const result = [];
147
149
  for (let index = 1; index <= (item.referenceCount ?? 1); index++) {
148
- const referenceId = index === 1 ? item.id : `${item.id}-${index}`;
150
+ const referenceId = footnoteReferenceId(item.id, index);
149
151
  const label = index === 1 ? `${item.number}` : `${item.number}-${index}`;
150
152
  result.push(' ', h(options, 'a', {
151
153
  key: index,
@@ -157,9 +159,6 @@ function renderFootnoteBackrefsOctane(item, options) {
157
159
  }
158
160
  return result;
159
161
  }
160
- function footnoteReferenceId(node) {
161
- return node.referenceIndex && node.referenceIndex > 1 ? `${node.id}-${node.referenceIndex}` : node.id;
162
- }
163
162
  function h(options, tag, props, ...children) {
164
163
  const component = typeof tag === 'string' ? options.components?.[tag] ?? tag : tag;
165
164
  return createElement(component, props ?? undefined, ...children);
@@ -1 +1 @@
1
- {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAQV,gBAAgB,EAChB,YAAY,EAGb,MAAM,YAAY,CAAA;AAWnB,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,gBAAgB,CAyC5F"}
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAQV,gBAAgB,EAChB,YAAY,EAGb,MAAM,YAAY,CAAA;AAWnB,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,gBAAgB,CA0C5F"}