@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/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,45 @@ 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) {
81
- const href = sanitizeUrl(parsed.href);
82
- pushText();
83
- if (href) {
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;
73
+ const defaultUrl = sanitizeUrl(parsed.href);
74
+ const href = options.urlTransform ? options.urlTransform(parsed.href, image ? 'image' : 'link', defaultUrl) : defaultUrl;
75
+ if (href !== null && (image || (!nested && (href || !parsed.href)))) {
76
+ pushText();
84
77
  nodes.push({
85
- type: 'link',
86
- href,
78
+ ...(image ? { type: 'image', src: href, alt: plainText(children) } : { type: 'link', href, children }),
87
79
  ...(parsed.title ? { title: parsed.title } : {}),
88
- children: parseInlineRaw(parsed.label, options, budget),
89
80
  });
90
81
  }
91
82
  else {
92
- nodes.push(...parseInlineRaw(parsed.label, options, budget));
83
+ if (nested)
84
+ text += '[';
85
+ for (const child of children) {
86
+ if (child.type === 'text')
87
+ text += child.value;
88
+ else {
89
+ pushText();
90
+ nodes.push(child);
91
+ }
92
+ }
93
93
  }
94
- index = parsed.end;
94
+ // An inner link disables its outer opener, but not the trailing markup.
95
+ budget.links = image ? links : budget.links + 1;
96
+ index = nested ? index + parsed.label.length + 1 : parsed.end;
95
97
  continue;
96
98
  }
97
99
  }
@@ -107,65 +109,28 @@ function parseInlineRaw(value, options, budget = { scans: Math.max(value.length
107
109
  continue;
108
110
  }
109
111
  }
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
112
  if (char === '~' && next === '~' && value[index + 2] === '~') {
126
113
  const run = countRun(value, index, '~');
127
114
  text += value.slice(index, index + run);
128
115
  index += run;
129
116
  continue;
130
117
  }
131
- if (char === '~' && next === '~') {
132
- const close = findDelimiter(value, index + 2, '~~', budget);
133
- if (close !== -1) {
134
- pushText();
135
- nodes.push({
136
- type: 'strike',
137
- children: parseInlineRaw(value.slice(index + 2, close), options, budget),
138
- });
139
- index = close + 2;
140
- continue;
141
- }
142
- text += '~~';
143
- index += 2;
144
- continue;
145
- }
146
- if (char === '~') {
147
- const close = findSingleTildeDelimiter(value, index + 1, budget);
118
+ if (char === '*' || char === '_' || char === '~') {
119
+ const size = next === char ? 2 : 1;
120
+ const close = char === '_' && !canUseUnderscore(value, index, size, true) ? -1 : findDelimiter(value, index + size, char.repeat(size), budget);
148
121
  if (close !== -1) {
149
122
  pushText();
150
123
  nodes.push({
151
- type: 'strike',
152
- children: parseInlineRaw(value.slice(index + 1, close), options, budget),
124
+ type: char === '~' ? 'strike' : size === 2 ? 'strong' : 'emphasis',
125
+ children: parseInlineRaw(value.slice(index + size, close), options, budget),
153
126
  });
154
- index = close + 1;
155
- continue;
127
+ index = close + size;
156
128
  }
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;
129
+ else {
130
+ text += char.repeat(size);
131
+ index += size;
168
132
  }
133
+ continue;
169
134
  }
170
135
  if (char === '<' && options.allowHtml) {
171
136
  const close = findCharacter(value, index + 1, '>', budget);
@@ -176,16 +141,17 @@ function parseInlineRaw(value, options, budget = { scans: Math.max(value.length
176
141
  continue;
177
142
  }
178
143
  }
179
- text += char;
180
- index++;
144
+ // Reset after recursive parsing; exhausted budgets still allow escapes.
145
+ inlineMarker.lastIndex = index + 1;
146
+ const end = budget.scans > 0 ? inlineMarker.exec(value)?.index ?? value.length : value.indexOf('\\', index + 1);
147
+ text += value.slice(index, end < 0 ? value.length : end);
148
+ index = end < 0 ? value.length : end;
181
149
  }
182
150
  pushText();
183
151
  budget.depth--;
184
152
  return nodes;
185
153
  }
186
154
  function parseFootnoteReference(value, open, options, budget) {
187
- if (value[open + 1] !== '^')
188
- return undefined;
189
155
  const close = findCharacter(value, open + 2, ']', budget);
190
156
  if (close === -1)
191
157
  return undefined;
@@ -194,19 +160,17 @@ function parseFootnoteReference(value, open, options, budget) {
194
160
  const definition = options.footnotes?.[key];
195
161
  if (!definition || !options.footnoteOrder)
196
162
  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
- }
163
+ let number = options.footnoteOrder.indexOf(key) + 1;
164
+ if (!number)
165
+ number = options.footnoteOrder.push(key);
202
166
  const referenceIndex = (options.footnoteCounts?.[key] ?? 0) + 1;
203
167
  if (options.footnoteCounts)
204
168
  options.footnoteCounts[key] = referenceIndex;
205
169
  return {
206
170
  node: {
207
171
  type: 'footnoteReference',
208
- id: definition.id ?? (footnoteId(definition.label) || 'footnote'),
209
- number: orderIndex + 1,
172
+ id: definition.id ?? footnoteId(definition.label),
173
+ number,
210
174
  ...(referenceIndex > 1 ? { referenceIndex } : {}),
211
175
  },
212
176
  end: close + 1,
@@ -217,72 +181,49 @@ function parseLinkish(value, open, options, budget) {
217
181
  if (closeBracket === -1)
218
182
  return undefined;
219
183
  const label = value.slice(open + 1, closeBracket);
220
- if (value[closeBracket + 1] === '(') {
221
- const closeParen = findBalanced(value, closeBracket + 1, '(', ')', budget);
184
+ let end = closeBracket + 1;
185
+ let definition;
186
+ if (value[end] === '(') {
187
+ const closeParen = findBalanced(value, end, '(', ')', budget);
222
188
  if (closeParen === -1)
223
189
  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
- };
190
+ definition = parseDestination(value.slice(end + 1, closeParen).trim());
191
+ end = closeParen + 1;
234
192
  }
235
- if (value[closeBracket + 1] === '[') {
236
- const closeReference = findBalanced(value, closeBracket + 1, '[', ']', budget);
193
+ else if (value[end] === '[') {
194
+ const closeReference = findBalanced(value, end, '[', ']', budget);
237
195
  if (closeReference === -1)
238
196
  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
- };
197
+ definition = options.references?.[normalizeReferenceLabel(value.slice(end + 1, closeReference) || label)];
198
+ end = closeReference + 1;
199
+ }
200
+ else {
201
+ definition = options.references?.[normalizeReferenceLabel(label)];
249
202
  }
250
- const definition = options.references?.[normalizeReferenceLabel(label)];
251
203
  if (definition) {
252
204
  return {
205
+ ...definition,
253
206
  label,
254
- href: definition.href,
255
- ...(definition.title ? { title: definition.title } : {}),
256
- end: closeBracket + 1,
207
+ end,
257
208
  };
258
209
  }
259
210
  return undefined;
260
211
  }
261
212
  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
- };
213
+ 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
214
  }
279
215
  function findBalanced(value, openIndex, open, close, budget) {
216
+ // Reject missing closers with a native search, charging the same shared budget.
217
+ if (findCharacter(value, openIndex + 1, close, budget) === -1)
218
+ return -1;
280
219
  let depth = 0;
281
220
  for (let index = openIndex; index < value.length; index++) {
282
- if (!takeScan(budget))
221
+ if (--budget.scans < 0)
283
222
  return -1;
284
- if (value[index - 1] === '\\')
223
+ if (value[index] === '\\') {
224
+ index++;
285
225
  continue;
226
+ }
286
227
  if (value[index] === open)
287
228
  depth++;
288
229
  if (value[index] === close) {
@@ -296,110 +237,63 @@ function findBalanced(value, openIndex, open, close, budget) {
296
237
  function countRun(value, index, char, budget) {
297
238
  let count = 0;
298
239
  while (value[index + count] === char) {
299
- if (budget && !takeScan(budget))
240
+ if (budget && --budget.scans < 0)
300
241
  break;
301
242
  count++;
302
243
  }
303
244
  return count;
304
245
  }
305
246
  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)
247
+ let index = start;
248
+ while ((index = findCharacter(value, index, char, budget)) !== -1) {
249
+ const size = countRun(value, index, char, budget);
250
+ if (size === count)
312
251
  return index;
252
+ index += size;
313
253
  }
314
254
  return -1;
315
255
  }
316
256
  function findDelimiter(value, start, delimiter, budget) {
257
+ if (delimiter === '~' && /[\s\d]/.test(value[start] ?? ''))
258
+ return -1;
317
259
  for (let index = start; index < value.length; index++) {
318
- if (!takeScan(budget))
260
+ if (--budget.scans < 0)
319
261
  return -1;
320
- if (value[index - 1] === '\\')
262
+ if (value[index] === '\\') {
263
+ index++;
321
264
  continue;
265
+ }
322
266
  if (!value.startsWith(delimiter, index))
323
267
  continue;
324
- if (value[index + 1] === delimiter) {
325
- const close = findDelimiter(value, index + 2, delimiter + delimiter, budget);
326
- if (close > index + 2) {
327
- index = close + 1;
268
+ if (delimiter[0] === '~') {
269
+ if (value[index - 1] === '~' || value[index + delimiter.length] === '~')
270
+ continue;
271
+ if (delimiter === '~' && /\s/.test(value[index - 1] ?? ''))
272
+ return -1;
273
+ }
274
+ else if (value[index + 1] === delimiter) {
275
+ const size = countRun(value, index, delimiter, budget);
276
+ const close = findDelimiter(value, index + size, delimiter.repeat(size), budget);
277
+ if (close > index + size) {
278
+ index = close + size - 1;
328
279
  continue;
329
280
  }
330
281
  }
331
- if (delimiter === '~~' && (value[index - 1] === '~' || value[index + 2] === '~'))
332
- continue;
333
282
  if (delimiter[0] === '_' && !canUseUnderscore(value, index, delimiter.length, false))
334
283
  continue;
335
284
  return index;
336
285
  }
337
286
  return -1;
338
287
  }
339
- function findSingleTildeDelimiter(value, start, budget) {
340
- if (isWhitespace(value[start] ?? '') || /\d/.test(value[start] ?? ''))
341
- return -1;
342
- for (let index = start; index < value.length; index++) {
343
- if (!takeScan(budget))
344
- return -1;
345
- if (value[index - 1] === '\\')
346
- continue;
347
- if (value[index] !== '~')
348
- continue;
349
- if (value[index - 1] === '~')
350
- continue;
351
- if (value[index + 1] === '~')
352
- continue;
353
- if (isWhitespace(value[index - 1] ?? ''))
354
- return -1;
355
- return index;
356
- }
357
- return -1;
358
- }
359
288
  function findCharacter(value, start, character, budget) {
360
- for (let index = start; index < value.length; index++) {
361
- if (!takeScan(budget))
362
- return -1;
363
- if (value[index] === character)
364
- return index;
365
- }
366
- return -1;
367
- }
368
- function takeScan(budget) {
369
289
  if (budget.scans <= 0)
370
- return false;
371
- budget.scans--;
372
- return true;
290
+ return -1;
291
+ const index = value.indexOf(character, start);
292
+ budget.scans -= (index < 0 ? value.length : index + 1) - start;
293
+ return budget.scans < 0 ? -1 : index;
373
294
  }
374
295
  function canUseUnderscore(value, index, size, opening) {
375
- const before = value[index - 1];
376
- const after = value[index + size];
377
- const leftFlanking = !isDelimiterWhitespace(after) && (!isPunctuation(after) || isDelimiterWhitespace(before) || isPunctuation(before));
378
- const rightFlanking = !isDelimiterWhitespace(before) && (!isPunctuation(before) || isDelimiterWhitespace(after) || isPunctuation(after));
379
- return opening ? leftFlanking && (!rightFlanking || isPunctuation(before)) : rightFlanking && (!leftFlanking || isPunctuation(after));
380
- }
381
- function isDelimiterWhitespace(value) {
382
- return value === undefined || /\s/.test(value);
383
- }
384
- function isPunctuation(value) {
385
- return value !== undefined && /[^\p{L}\p{N}\s]/u.test(value);
386
- }
387
- function isWhitespace(value) {
388
- return /\s/.test(value);
389
- }
390
- function textFromMarkdown(value, budget) {
391
- return parseInlineRaw(value, {}, budget).map(node => (node.type === 'text' || node.type === 'inlineCode' ? node.value : '')).join('');
392
- }
393
- function mergeText(nodes) {
394
- const result = [];
395
- for (const node of nodes) {
396
- const previous = result.at(-1);
397
- if (previous?.type === 'text' && node.type === 'text') {
398
- previous.value += node.value;
399
- }
400
- else {
401
- result.push(node);
402
- }
403
- }
404
- return result;
296
+ const inside = value[opening ? index + size : index - 1];
297
+ const outside = value[opening ? index - 1 : index + size];
298
+ return inside !== undefined && !/\s/.test(inside) && (outside === undefined || /[^\p{L}\p{N}]/u.test(outside));
405
299
  }
@@ -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,EAAwD,UAAU,EAAE,aAAa,EAAE,aAAa,EAAiB,MAAM,YAAY,CAAA;AAE1J,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,CA0ClH"}
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}`,
@@ -68,6 +69,8 @@ export function renderInlineOctane(node, options = {}, key) {
68
69
  return options.allowHtml
69
70
  ? h(options, 'span', { key, dangerouslySetInnerHTML: { __html: node.value } })
70
71
  : node.value;
72
+ case 'inlineComponent':
73
+ return renderComponentOctane(node, options, key);
71
74
  }
72
75
  }
73
76
  function renderInlines(nodes, options) {
@@ -81,6 +84,7 @@ function renderCodeBlockOctane(node, options, key) {
81
84
  ? {
82
85
  dangerouslySetInnerHTML: {
83
86
  __html: highlighter(node.value, lang, {
87
+ ...(node.meta && { meta: node.meta }),
84
88
  ...(node.highlightLines && { highlightLines: node.highlightLines }),
85
89
  ...(options.codeLineNumbers !== undefined && { lineNumbers: options.codeLineNumbers }),
86
90
  }),
@@ -90,6 +94,7 @@ function renderCodeBlockOctane(node, options, key) {
90
94
  const pre = h(options, 'pre', {
91
95
  className: `tm-code${options.codeLineNumbers ? ' tm-code--line-numbers' : ''}`,
92
96
  'data-lang': lang,
97
+ ...(node.meta ? { 'data-meta': node.meta } : {}),
93
98
  ...(node.title ? { 'data-code-title': node.title } : {}),
94
99
  ...(node.file ? { 'data-filename': node.file } : {}),
95
100
  ...(node.framework ? { 'data-framework': node.framework } : {}),
@@ -99,8 +104,7 @@ function renderCodeBlockOctane(node, options, key) {
99
104
  return h(options, 'figure', { key, className: 'tm-code-frame', 'data-lang': lang }, h(options, 'figcaption', null, node.title), pre);
100
105
  }
101
106
  function renderListItemChildrenOctane(children, checked, loose, options, key) {
102
- const [first, ...rest] = children;
103
- const task = checked === undefined
107
+ let result = checked === undefined
104
108
  ? []
105
109
  : [
106
110
  h(options, 'input', {
@@ -112,17 +116,18 @@ function renderListItemChildrenOctane(children, checked, loose, options, key) {
112
116
  }),
113
117
  ' ',
114
118
  ];
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
- ];
119
+ for (let index = 0; index < children.length; index++) {
120
+ const child = children[index];
121
+ if (child.type === 'paragraph' && (!loose || index === 0)) {
122
+ for (const inline of renderInlines(child.children, options))
123
+ result.push(inline);
124
+ if (loose)
125
+ result = [h(options, 'p', { key: `${key}:paragraph` }, result)];
126
+ }
127
+ else
128
+ result.push(renderBlockOctane(child, options, `${key}:${index}`));
121
129
  }
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)];
130
+ return result;
126
131
  }
127
132
  function renderTableCellOctane(tag, cell, align, options, key) {
128
133
  return h(options, tag, { key, ...(align ? { style: { textAlign: align } } : {}) }, renderInlines(cell.children, options));
@@ -133,19 +138,20 @@ function renderFootnotesOctane(items, options, key) {
133
138
  function renderFootnoteItemOctane(item, options) {
134
139
  const lastIndex = item.children.length - 1;
135
140
  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) => {
141
+ const result = item.children.map((child, index) => {
139
142
  if (index === lastIndex && child.type === 'paragraph') {
140
143
  return h(options, 'p', { key: index }, renderInlines(child.children, options), backrefs);
141
144
  }
142
145
  return renderBlockOctane(child, options, `${index}`);
143
146
  });
147
+ if (item.children[lastIndex]?.type !== 'paragraph')
148
+ result.push(h(options, 'p', { key: 'backref-wrapper' }, backrefs.slice(1)));
149
+ return result;
144
150
  }
145
151
  function renderFootnoteBackrefsOctane(item, options) {
146
152
  const result = [];
147
153
  for (let index = 1; index <= (item.referenceCount ?? 1); index++) {
148
- const referenceId = index === 1 ? item.id : `${item.id}-${index}`;
154
+ const referenceId = footnoteReferenceId(item.id, index);
149
155
  const label = index === 1 ? `${item.number}` : `${item.number}-${index}`;
150
156
  result.push(' ', h(options, 'a', {
151
157
  key: index,
@@ -157,15 +163,12 @@ function renderFootnoteBackrefsOctane(item, options) {
157
163
  }
158
164
  return result;
159
165
  }
160
- function footnoteReferenceId(node) {
161
- return node.referenceIndex && node.referenceIndex > 1 ? `${node.id}-${node.referenceIndex}` : node.id;
162
- }
163
166
  function h(options, tag, props, ...children) {
164
167
  const component = typeof tag === 'string' ? options.components?.[tag] ?? tag : tag;
165
168
  return createElement(component, props ?? undefined, ...children);
166
169
  }
167
170
  function renderComponentOctane(node, options, key) {
168
- const tag = node.tagName ?? 'md-comment-component';
171
+ const tag = node.tagName ?? (node.type === 'inlineComponent' ? 'span' : 'md-comment-component');
169
172
  const props = {
170
173
  ...(node.properties ?? {}),
171
174
  };
@@ -174,7 +177,9 @@ function renderComponentOctane(node, options, key) {
174
177
  if (!props['data-attributes'])
175
178
  props['data-attributes'] = JSON.stringify(node.attributes);
176
179
  }
177
- return h(options, tag, { key, ...props }, node.children.map((child, index) => renderBlockOctane(child, options, `${key}:${index}`)));
180
+ return h(options, tag, { key, ...props }, node.type === 'inlineComponent'
181
+ ? renderInlines(node.children, options)
182
+ : node.children.map((child, index) => renderBlockOctane(child, options, `${key}:${index}`)));
178
183
  }
179
184
  function renderHeadingAnchorOctane(id, options) {
180
185
  if (!id || !options.headingAnchors)
@@ -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"}