@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/README.md +22 -2
- package/dist/extensions/callouts.d.ts.map +1 -1
- package/dist/extensions/callouts.js +0 -5
- package/dist/extensions/comment-components.d.ts.map +1 -1
- package/dist/extensions/comment-components.js +16 -34
- package/dist/extensions/headings.js +8 -7
- package/dist/extensions/shared.d.ts.map +1 -1
- package/dist/extensions/shared.js +2 -9
- package/dist/extensions/tabs.d.ts.map +1 -1
- package/dist/extensions/tabs.js +11 -18
- package/dist/html.js +25 -32
- package/dist/inline.d.ts.map +1 -1
- package/dist/inline.js +108 -207
- package/dist/octane.d.ts.map +1 -1
- package/dist/octane.js +20 -21
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +51 -72
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +20 -21
- package/dist/utils.d.ts +6 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +33 -23
- package/package.json +28 -25
- package/skills/custom-extensions/SKILL.md +1 -1
- package/skills/docs-features/SKILL.md +1 -1
- package/skills/octane-rendering/SKILL.md +1 -1
- package/skills/production-pipelines/SKILL.md +2 -2
- package/skills/react-rendering/SKILL.md +1 -1
- package/skills/render-markdown/SKILL.md +1 -1
- package/skills/render-markdown/references/ast-and-options.md +1 -1
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);
|
|
@@ -26,10 +26,11 @@ export function parseMarkdown(markdown, options = {}) {
|
|
|
26
26
|
...(hasFootnotes ? { footnotes: definitions.footnotes, footnoteOrder, footnoteCounts } : {}),
|
|
27
27
|
}
|
|
28
28
|
: options;
|
|
29
|
-
const
|
|
29
|
+
const slugger = createSlugger();
|
|
30
|
+
const parser = new BlockParser(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 ?? []) {
|
|
@@ -43,6 +44,7 @@ class BlockParser {
|
|
|
43
44
|
slugger;
|
|
44
45
|
budget;
|
|
45
46
|
index = 0;
|
|
47
|
+
loose = false;
|
|
46
48
|
constructor(lines, options, slugger, budget = { depth: 0 }) {
|
|
47
49
|
this.lines = lines;
|
|
48
50
|
this.options = options;
|
|
@@ -59,6 +61,8 @@ class BlockParser {
|
|
|
59
61
|
const nodes = [];
|
|
60
62
|
while (this.index < this.lines.length) {
|
|
61
63
|
if (isBlank(this.current())) {
|
|
64
|
+
if (nodes.length)
|
|
65
|
+
this.loose = true;
|
|
62
66
|
this.index++;
|
|
63
67
|
continue;
|
|
64
68
|
}
|
|
@@ -101,23 +105,21 @@ class BlockParser {
|
|
|
101
105
|
return undefined;
|
|
102
106
|
}
|
|
103
107
|
parseFence() {
|
|
104
|
-
const match = this.current().match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
|
|
108
|
+
const match = this.current().match(/^( {0,3})(`{3,}|~{3,})(.*)$/);
|
|
105
109
|
if (!match)
|
|
106
110
|
return undefined;
|
|
107
|
-
const fence = match[
|
|
108
|
-
const
|
|
109
|
-
const fenceSize = fence.length;
|
|
110
|
-
const info = match[2].trim();
|
|
111
|
+
const fence = match[2];
|
|
112
|
+
const info = match[3].trim();
|
|
111
113
|
const code = [];
|
|
112
114
|
this.index++;
|
|
113
115
|
while (this.index < this.lines.length) {
|
|
114
116
|
const line = this.current();
|
|
115
117
|
const close = line.match(/^ {0,3}(`{3,}|~{3,})\s*$/);
|
|
116
|
-
if (close
|
|
118
|
+
if (close?.[1].startsWith(fence)) {
|
|
117
119
|
this.index++;
|
|
118
120
|
break;
|
|
119
121
|
}
|
|
120
|
-
code.push(line);
|
|
122
|
+
code.push(stripIndent(line, match[1].length));
|
|
121
123
|
this.index++;
|
|
122
124
|
}
|
|
123
125
|
return {
|
|
@@ -150,15 +152,13 @@ class BlockParser {
|
|
|
150
152
|
const quoted = [];
|
|
151
153
|
while (this.index < this.lines.length) {
|
|
152
154
|
const line = this.current();
|
|
153
|
-
if (isBlank(line)) {
|
|
154
|
-
quoted.push('');
|
|
155
|
-
this.index++;
|
|
156
|
-
continue;
|
|
157
|
-
}
|
|
158
155
|
const match = line.match(/^ {0,3}>\s?(.*)$/);
|
|
159
|
-
if (!match)
|
|
160
|
-
|
|
161
|
-
|
|
156
|
+
if (!match) {
|
|
157
|
+
if (!isBlank(line))
|
|
158
|
+
break;
|
|
159
|
+
this.loose = true;
|
|
160
|
+
}
|
|
161
|
+
quoted.push(match?.[1] ?? '');
|
|
162
162
|
this.index++;
|
|
163
163
|
}
|
|
164
164
|
return {
|
|
@@ -173,11 +173,10 @@ class BlockParser {
|
|
|
173
173
|
const items = [];
|
|
174
174
|
const ordered = first.ordered;
|
|
175
175
|
const baseIndent = first.indent;
|
|
176
|
-
const start = ordered ? first.number : undefined;
|
|
177
176
|
let loose = false;
|
|
178
177
|
while (this.index < this.lines.length) {
|
|
179
178
|
const marker = listMarker(this.current());
|
|
180
|
-
if (!marker ||
|
|
179
|
+
if (!marker || marker.marker !== first.marker || marker.indent !== baseIndent)
|
|
181
180
|
break;
|
|
182
181
|
let firstLine = marker.content;
|
|
183
182
|
const task = firstLine.match(/^\[([ xX])\]\s+(.*)$/);
|
|
@@ -200,7 +199,7 @@ class BlockParser {
|
|
|
200
199
|
break;
|
|
201
200
|
const followingMarker = listMarker(following);
|
|
202
201
|
if (followingMarker?.indent === baseIndent) {
|
|
203
|
-
if (
|
|
202
|
+
if (followingMarker.marker !== first.marker)
|
|
204
203
|
break;
|
|
205
204
|
loose = true;
|
|
206
205
|
this.index = nextIndex;
|
|
@@ -208,9 +207,8 @@ class BlockParser {
|
|
|
208
207
|
}
|
|
209
208
|
if (leadingSpaces(following) < marker.contentIndent)
|
|
210
209
|
break;
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
this.index = nextIndex;
|
|
210
|
+
while (this.index < nextIndex)
|
|
211
|
+
itemLines.push(stripIndent(this.lines[this.index++], marker.contentIndent));
|
|
214
212
|
continue;
|
|
215
213
|
}
|
|
216
214
|
if (leadingSpaces(line) >= marker.contentIndent) {
|
|
@@ -223,7 +221,9 @@ class BlockParser {
|
|
|
223
221
|
itemLines.push(line.trimStart());
|
|
224
222
|
this.index++;
|
|
225
223
|
}
|
|
226
|
-
const
|
|
224
|
+
const parser = new BlockParser(itemLines, this.options, this.slugger, this.budget);
|
|
225
|
+
const children = parser.parse();
|
|
226
|
+
loose ||= parser.loose;
|
|
227
227
|
const item = { type: 'listItem', children };
|
|
228
228
|
if (checked !== undefined)
|
|
229
229
|
item.checked = checked;
|
|
@@ -232,7 +232,7 @@ class BlockParser {
|
|
|
232
232
|
return {
|
|
233
233
|
type: 'list',
|
|
234
234
|
ordered,
|
|
235
|
-
...(ordered &&
|
|
235
|
+
...(ordered && { start: first.number }),
|
|
236
236
|
...(loose && { loose: true }),
|
|
237
237
|
items,
|
|
238
238
|
};
|
|
@@ -322,7 +322,7 @@ function parseCodeInfo(info) {
|
|
|
322
322
|
const titleMatch = meta.match(/(?:^|\s)(?:title|file)=(?:"([^"]+)"|'([^']+)'|([^\s}]+))/);
|
|
323
323
|
const frameworkMatch = meta.match(/(?:^|\s)framework=(?:"([^"]+)"|'([^']+)'|([^\s}]+))/);
|
|
324
324
|
const rangeMatch = meta.match(/\{([^}]+)\}|(?:^|\s)lines=([^\s]+)/);
|
|
325
|
-
const highlightLines = parseLineRanges(rangeMatch
|
|
325
|
+
const highlightLines = rangeMatch ? parseLineRanges(rangeMatch[1] ?? rangeMatch[2]) : [];
|
|
326
326
|
const title = titleMatch ? titleMatch[1] ?? titleMatch[2] ?? titleMatch[3] : undefined;
|
|
327
327
|
const framework = frameworkMatch ? frameworkMatch[1] ?? frameworkMatch[2] ?? frameworkMatch[3] : undefined;
|
|
328
328
|
return {
|
|
@@ -336,29 +336,20 @@ function parseCodeInfo(info) {
|
|
|
336
336
|
function extractDefinitions(lines) {
|
|
337
337
|
const references = Object.create(null);
|
|
338
338
|
const footnotes = Object.create(null);
|
|
339
|
-
const footnoteIds =
|
|
339
|
+
const footnoteIds = createSlugger(footnoteId);
|
|
340
340
|
const remaining = [];
|
|
341
|
-
let
|
|
342
|
-
let fenceSize = 0;
|
|
341
|
+
let activeFence = '';
|
|
343
342
|
let index = 0;
|
|
344
343
|
while (index < lines.length) {
|
|
345
344
|
const line = lines[index];
|
|
346
|
-
const fence = line.match(/^ {0,3}(`{3,}|~{3,})
|
|
345
|
+
const fence = line.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
|
|
347
346
|
if (fence) {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
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;
|
|
347
|
+
if (!activeFence)
|
|
348
|
+
activeFence = fence[1];
|
|
349
|
+
else if (fence[1].startsWith(activeFence) && isBlank(fence[2]))
|
|
350
|
+
activeFence = '';
|
|
360
351
|
}
|
|
361
|
-
if (!
|
|
352
|
+
else if (!activeFence) {
|
|
362
353
|
const footnote = line.match(/^ {0,3}\[\^([^\]\n]+)\]:[ \t]*(.*)$/);
|
|
363
354
|
if (footnote) {
|
|
364
355
|
const label = footnote[1];
|
|
@@ -373,20 +364,14 @@ function extractDefinitions(lines) {
|
|
|
373
364
|
}
|
|
374
365
|
const key = normalizeReferenceLabel(label);
|
|
375
366
|
if (!footnotes[key]) {
|
|
376
|
-
|
|
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}` };
|
|
367
|
+
footnotes[key] = { label, content: content.join('\n'), id: footnoteIds(label) };
|
|
380
368
|
}
|
|
381
369
|
continue;
|
|
382
370
|
}
|
|
383
|
-
const definition = line.match(/^ {0,3}\[([^\]\n]+)\]:[ \t]*(\S
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
references[normalizeReferenceLabel(definition[1])]
|
|
387
|
-
href: definition[2].replace(/^<|>$/g, ''),
|
|
388
|
-
...(title !== undefined ? { title } : {}),
|
|
389
|
-
};
|
|
371
|
+
const definition = line.match(/^ {0,3}\[([^\]\n]+)\]:[ \t]*(\S.*)$/);
|
|
372
|
+
const destination = definition && parseDestination(definition[2].trimEnd());
|
|
373
|
+
if (destination) {
|
|
374
|
+
references[normalizeReferenceLabel(definition[1])] ??= destination;
|
|
390
375
|
index++;
|
|
391
376
|
continue;
|
|
392
377
|
}
|
|
@@ -396,7 +381,7 @@ function extractDefinitions(lines) {
|
|
|
396
381
|
}
|
|
397
382
|
return { lines: remaining, references, footnotes };
|
|
398
383
|
}
|
|
399
|
-
function createFootnotesBlock(footnotes, footnoteOrder, options) {
|
|
384
|
+
function createFootnotesBlock(footnotes, footnoteOrder, options, slugger) {
|
|
400
385
|
const items = [];
|
|
401
386
|
for (let index = 0; index < footnoteOrder.length; index++) {
|
|
402
387
|
const key = footnoteOrder[index];
|
|
@@ -404,9 +389,9 @@ function createFootnotesBlock(footnotes, footnoteOrder, options) {
|
|
|
404
389
|
if (!definition)
|
|
405
390
|
continue;
|
|
406
391
|
items.push({
|
|
407
|
-
id: definition.id ??
|
|
392
|
+
id: definition.id ?? footnoteId(definition.label),
|
|
408
393
|
number: index + 1,
|
|
409
|
-
children: new BlockParser(normalizeInput(definition.content).split('\n'), options,
|
|
394
|
+
children: new BlockParser(normalizeInput(definition.content).split('\n'), options, slugger).parse(),
|
|
410
395
|
});
|
|
411
396
|
}
|
|
412
397
|
for (const item of items) {
|
|
@@ -424,6 +409,9 @@ function parseLineRanges(value) {
|
|
|
424
409
|
continue;
|
|
425
410
|
const start = Number(match[1]);
|
|
426
411
|
const end = Number(match[2] ?? match[1]);
|
|
412
|
+
// Larger integers can stop line++ from making progress.
|
|
413
|
+
if (end > Number.MAX_SAFE_INTEGER)
|
|
414
|
+
continue;
|
|
427
415
|
for (let line = start; line <= end && line < start + 1000; line++)
|
|
428
416
|
lines.add(line);
|
|
429
417
|
}
|
|
@@ -444,18 +432,12 @@ function listMarker(line) {
|
|
|
444
432
|
content: match[4] ?? '',
|
|
445
433
|
};
|
|
446
434
|
}
|
|
447
|
-
function sameListType(left, right) {
|
|
448
|
-
return left.ordered === right.ordered && left.marker === right.marker;
|
|
449
|
-
}
|
|
450
435
|
function leadingSpaces(line) {
|
|
451
436
|
return line.match(/^ */)?.[0].length ?? 0;
|
|
452
437
|
}
|
|
453
438
|
function isBlockStart(line, next) {
|
|
454
439
|
const marker = listMarker(line);
|
|
455
|
-
return (/^ {0,3}(
|
|
456
|
-
/^ {0,3}#{1,6}(?:\s+|$)/.test(line) ||
|
|
457
|
-
/^ {0,3}([-*_])(?:\s*\1){2,}\s*$/.test(line) ||
|
|
458
|
-
/^ {0,3}>\s?/.test(line) ||
|
|
440
|
+
return (/^ {0,3}(?:`{3,}|~{3,}|#{1,6}(?:\s|$)|([-*_])(?:\s*\1){2,}\s*$|>)/.test(line) ||
|
|
459
441
|
(marker !== undefined && (!marker.ordered || marker.number === 1)) ||
|
|
460
442
|
(!!next && looksLikeTableHeader(line, next)));
|
|
461
443
|
}
|
|
@@ -466,14 +448,10 @@ function looksLikeTableHeader(header, delimiter) {
|
|
|
466
448
|
return cells.length === splitTableRow(header).length && cells.every(cell => /^:?-+:?$/.test(cell.trim()));
|
|
467
449
|
}
|
|
468
450
|
function splitTableRow(value) {
|
|
469
|
-
|
|
470
|
-
if (row.startsWith('|'))
|
|
471
|
-
row = row.slice(1);
|
|
472
|
-
if (row.endsWith('|'))
|
|
473
|
-
row = row.slice(0, -1);
|
|
451
|
+
const row = value.trim();
|
|
474
452
|
const cells = [];
|
|
475
453
|
let current = '';
|
|
476
|
-
for (let index = 0; index < row.length; index++) {
|
|
454
|
+
for (let index = row.startsWith('|') ? 1 : 0; index < row.length; index++) {
|
|
477
455
|
const char = row[index];
|
|
478
456
|
if (char === '\\' && row[index + 1] === '|') {
|
|
479
457
|
current += '|';
|
|
@@ -487,7 +465,8 @@ function splitTableRow(value) {
|
|
|
487
465
|
}
|
|
488
466
|
current += char;
|
|
489
467
|
}
|
|
490
|
-
|
|
468
|
+
if (current || !row.endsWith('|') || !cells.length)
|
|
469
|
+
cells.push(current.trim());
|
|
491
470
|
return cells;
|
|
492
471
|
}
|
|
493
472
|
function parseAlign(value) {
|
package/dist/react.d.ts.map
CHANGED
|
@@ -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;
|
|
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,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"}
|
package/dist/react.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Fragment, createElement } from 'react';
|
|
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, renderMarkdownReact(children, options));
|
|
5
6
|
}
|
|
@@ -17,7 +18,7 @@ export function renderBlockReact(node, options = {}, key) {
|
|
|
17
18
|
return renderCodeBlockReact(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 }, renderListItemChildrenReact(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 }, renderListItemChildrenReact(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) => renderBlockReact(child, options, `${key}:${index}`)));
|
|
@@ -53,7 +54,7 @@ export function renderInlineReact(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}`,
|
|
@@ -102,8 +103,7 @@ function renderCodeBlockReact(node, options, key) {
|
|
|
102
103
|
return h(options, 'figure', { key, className: 'tm-code-frame', 'data-lang': lang }, h(options, 'figcaption', null, node.title), pre);
|
|
103
104
|
}
|
|
104
105
|
function renderListItemChildrenReact(children, checked, loose, options, key) {
|
|
105
|
-
|
|
106
|
-
const task = checked === undefined
|
|
106
|
+
let result = checked === undefined
|
|
107
107
|
? []
|
|
108
108
|
: [
|
|
109
109
|
h(options, 'input', {
|
|
@@ -115,17 +115,18 @@ function renderListItemChildrenReact(children, checked, loose, options, key) {
|
|
|
115
115
|
}),
|
|
116
116
|
' ',
|
|
117
117
|
];
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
118
|
+
for (let index = 0; index < children.length; index++) {
|
|
119
|
+
const child = children[index];
|
|
120
|
+
if (child.type === 'paragraph' && (!loose || index === 0)) {
|
|
121
|
+
for (const inline of renderInlines(child.children, options))
|
|
122
|
+
result.push(inline);
|
|
123
|
+
if (loose)
|
|
124
|
+
result = [h(options, 'p', { key: `${key}:paragraph` }, result)];
|
|
125
|
+
}
|
|
126
|
+
else
|
|
127
|
+
result.push(renderBlockReact(child, options, `${key}:${index}`));
|
|
124
128
|
}
|
|
125
|
-
return
|
|
126
|
-
}
|
|
127
|
-
function renderListChildReact(child, loose, options, key) {
|
|
128
|
-
return !loose && child.type === 'paragraph' ? renderInlines(child.children, options) : [renderBlockReact(child, options, key)];
|
|
129
|
+
return result;
|
|
129
130
|
}
|
|
130
131
|
function renderTableCellReact(tag, cell, align, options, key) {
|
|
131
132
|
return h(options, tag, { key, ...(align ? { style: { textAlign: align } } : {}) }, renderInlines(cell.children, options));
|
|
@@ -136,19 +137,20 @@ function renderFootnotesReact(items, options, key) {
|
|
|
136
137
|
function renderFootnoteItemReact(item, options) {
|
|
137
138
|
const lastIndex = item.children.length - 1;
|
|
138
139
|
const backrefs = renderFootnoteBackrefsReact(item, options);
|
|
139
|
-
|
|
140
|
-
return [h(options, 'p', { key: 'backref-wrapper' }, backrefs.slice(1))];
|
|
141
|
-
return item.children.map((child, index) => {
|
|
140
|
+
const result = item.children.map((child, index) => {
|
|
142
141
|
if (index === lastIndex && child.type === 'paragraph') {
|
|
143
142
|
return h(options, 'p', { key: index }, renderInlines(child.children, options), backrefs);
|
|
144
143
|
}
|
|
145
144
|
return renderBlockReact(child, options, `${index}`);
|
|
146
145
|
});
|
|
146
|
+
if (item.children[lastIndex]?.type !== 'paragraph')
|
|
147
|
+
result.push(h(options, 'p', { key: 'backref-wrapper' }, backrefs.slice(1)));
|
|
148
|
+
return result;
|
|
147
149
|
}
|
|
148
150
|
function renderFootnoteBackrefsReact(item, options) {
|
|
149
151
|
const result = [];
|
|
150
152
|
for (let index = 1; index <= (item.referenceCount ?? 1); index++) {
|
|
151
|
-
const referenceId =
|
|
153
|
+
const referenceId = footnoteReferenceId(item.id, index);
|
|
152
154
|
const label = index === 1 ? `${item.number}` : `${item.number}-${index}`;
|
|
153
155
|
result.push(' ', h(options, 'a', {
|
|
154
156
|
key: index,
|
|
@@ -160,9 +162,6 @@ function renderFootnoteBackrefsReact(item, options) {
|
|
|
160
162
|
}
|
|
161
163
|
return result;
|
|
162
164
|
}
|
|
163
|
-
function footnoteReferenceId(node) {
|
|
164
|
-
return node.referenceIndex && node.referenceIndex > 1 ? `${node.id}-${node.referenceIndex}` : node.id;
|
|
165
|
-
}
|
|
166
165
|
function h(options, tag, props, ...children) {
|
|
167
166
|
const component = typeof tag === 'string' ? options.components?.[tag] ?? tag : tag;
|
|
168
167
|
return createElement(component, props, ...children);
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import type { InlineNode } from './types.js';
|
|
1
|
+
import type { InlineNode, LinkReferenceDefinition } from './types.js';
|
|
2
2
|
export declare function normalizeInput(value: string): string;
|
|
3
3
|
export declare function escapeHtml(value: string): string;
|
|
4
4
|
export declare function escapeAttr(value: string): string;
|
|
5
5
|
export declare function isBlank(value: string): boolean;
|
|
6
6
|
export declare function stripIndent(value: string, size: number): string;
|
|
7
7
|
export declare function plainText(nodes: InlineNode[]): string;
|
|
8
|
-
export declare function createSlugger(): (value: string) => string;
|
|
8
|
+
export declare function createSlugger(normalize?: typeof headingSlug): (value: string) => string;
|
|
9
|
+
declare function headingSlug(value: string): string;
|
|
9
10
|
export declare function sanitizeUrl(value: string): string;
|
|
11
|
+
export declare function parseDestination(value: string): LinkReferenceDefinition | undefined;
|
|
10
12
|
export declare function normalizeReferenceLabel(value: string): string;
|
|
11
13
|
export declare function footnoteId(label: string): string;
|
|
14
|
+
export declare function footnoteReferenceId(id: string, index?: number): string;
|
|
12
15
|
export declare function splitLines(value: string): string[];
|
|
16
|
+
export {};
|
|
13
17
|
//# sourceMappingURL=utils.d.ts.map
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AAUrE,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAI/D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,CAQrD;AAED,wBAAgB,aAAa,CAAC,SAAS,qBAAc,IAG3C,OAAO,MAAM,YAStB;AAED,iBAAS,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ1C;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGjD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,uBAAuB,GAAG,SAAS,CAMnF;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIhD;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,MAAM,CAEjE;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAIlD"}
|
package/dist/utils.js
CHANGED
|
@@ -35,40 +35,50 @@ export function plainText(nodes) {
|
|
|
35
35
|
}
|
|
36
36
|
return value;
|
|
37
37
|
}
|
|
38
|
-
export function createSlugger() {
|
|
38
|
+
export function createSlugger(normalize = headingSlug) {
|
|
39
39
|
const seen = new Map();
|
|
40
40
|
return (value) => {
|
|
41
|
-
const base = value
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
seen.set(base, count + 1);
|
|
50
|
-
return count === 0 ? base : `${base}-${count + 1}`;
|
|
41
|
+
const base = normalize(value);
|
|
42
|
+
let count = seen.get(base) ?? 1;
|
|
43
|
+
let id = base;
|
|
44
|
+
while (seen.has(id))
|
|
45
|
+
id = `${base}-${++count}`;
|
|
46
|
+
seen.set(base, count);
|
|
47
|
+
seen.set(id, 1);
|
|
48
|
+
return id;
|
|
51
49
|
};
|
|
52
50
|
}
|
|
51
|
+
function headingSlug(value) {
|
|
52
|
+
return value
|
|
53
|
+
.toLowerCase()
|
|
54
|
+
.normalize('NFKD')
|
|
55
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
56
|
+
.replace(/&[a-z0-9#]+;/gi, '')
|
|
57
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
58
|
+
.replace(/^-+|-+$/g, '') || 'section';
|
|
59
|
+
}
|
|
53
60
|
export function sanitizeUrl(value) {
|
|
54
|
-
const trimmed = value.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
return
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return
|
|
61
|
+
const trimmed = value.replace(/[\u0000-\u001F\u007F\s]+/g, '');
|
|
62
|
+
return /^(?!https?:|mailto:|tel:)[a-z][a-z0-9+.-]*:/i.test(trimmed) ? '' : trimmed;
|
|
63
|
+
}
|
|
64
|
+
export function parseDestination(value) {
|
|
65
|
+
const match = value.match(/^(?:<([^<>\n]*)>|([^<>\s]*?))(?:\s+(?:"([^"]*)"|'([^']*)'|\(([^)]*)\)))?$/);
|
|
66
|
+
if (!match)
|
|
67
|
+
return undefined;
|
|
68
|
+
const href = match[1] ?? match[2];
|
|
69
|
+
const title = match[3] ?? match[4] ?? match[5];
|
|
70
|
+
return { href: href.replace(/\\([!-/:-@\[-`{-~])/g, '$1'), ...(title ? { title } : {}) };
|
|
64
71
|
}
|
|
65
72
|
export function normalizeReferenceLabel(value) {
|
|
66
|
-
return value.trim().toLowerCase();
|
|
73
|
+
return value.trim().replace(/\s+/g, ' ').toLowerCase();
|
|
67
74
|
}
|
|
68
75
|
export function footnoteId(label) {
|
|
69
76
|
return normalizeReferenceLabel(label)
|
|
70
77
|
.replace(/[^a-z0-9_-]+/g, '-')
|
|
71
|
-
.replace(/^-+|-+$/g, '');
|
|
78
|
+
.replace(/^-+|-+$/g, '') || 'footnote';
|
|
79
|
+
}
|
|
80
|
+
export function footnoteReferenceId(id, index = 1) {
|
|
81
|
+
return index > 1 ? `${id}-${index}` : id;
|
|
72
82
|
}
|
|
73
83
|
export function splitLines(value) {
|
|
74
84
|
const lines = value.split('\n');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/markdown",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A tiny, fast, deterministic Markdown renderer for blogs and documentation.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
"bugs": {
|
|
13
13
|
"url": "https://github.com/TanStack/markdown/issues"
|
|
14
14
|
},
|
|
15
|
-
"packageManager": "pnpm@11.4.0",
|
|
16
15
|
"sideEffects": false,
|
|
17
16
|
"files": [
|
|
18
17
|
"dist",
|
|
@@ -71,27 +70,6 @@
|
|
|
71
70
|
"import": "./dist/extensions/tabs.js"
|
|
72
71
|
}
|
|
73
72
|
},
|
|
74
|
-
"scripts": {
|
|
75
|
-
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
76
|
-
"typecheck": "tsc --noEmit",
|
|
77
|
-
"test": "vitest run",
|
|
78
|
-
"test:corpus": "vitest run tests/corpus.test.tsx",
|
|
79
|
-
"corpus:audit": "tsx scripts/audit-corpus.ts",
|
|
80
|
-
"corpus:audit:tanstack": "tsx scripts/audit-tanstack-corpus.ts",
|
|
81
|
-
"corpus:audit:external": "tsx scripts/audit-external-corpus.ts",
|
|
82
|
-
"docs:verify": "node scripts/verify-docs.mjs",
|
|
83
|
-
"test:skills": "intent validate skills --check && pnpm run skills:check-version",
|
|
84
|
-
"skills:check-version": "node scripts/sync-skill-version.mjs --check",
|
|
85
|
-
"skills:sync-version": "node scripts/sync-skill-version.mjs",
|
|
86
|
-
"skills:stale": "intent stale",
|
|
87
|
-
"conformance": "tsx scripts/conformance.ts",
|
|
88
|
-
"bench": "tsx scripts/bench.ts",
|
|
89
|
-
"size": "tsx scripts/measure-size.ts",
|
|
90
|
-
"research": "pnpm run conformance && pnpm run size && pnpm run bench",
|
|
91
|
-
"verify": "pnpm test && pnpm run typecheck && pnpm run build && pnpm run docs:verify && pnpm run test:skills && pnpm run research && pnpm pack --dry-run",
|
|
92
|
-
"prepublishOnly": "pnpm run verify",
|
|
93
|
-
"version": "pnpm run skills:sync-version"
|
|
94
|
-
},
|
|
95
73
|
"publishConfig": {
|
|
96
74
|
"access": "public"
|
|
97
75
|
},
|
|
@@ -108,6 +86,7 @@
|
|
|
108
86
|
}
|
|
109
87
|
},
|
|
110
88
|
"devDependencies": {
|
|
89
|
+
"@changesets/cli": "3.0.2",
|
|
111
90
|
"@tanstack/highlight": "0.0.6",
|
|
112
91
|
"@tanstack/intent": "^0.3.6",
|
|
113
92
|
"@types/node": "^24.0.0",
|
|
@@ -133,5 +112,29 @@
|
|
|
133
112
|
},
|
|
134
113
|
"keywords": [
|
|
135
114
|
"tanstack-intent"
|
|
136
|
-
]
|
|
137
|
-
|
|
115
|
+
],
|
|
116
|
+
"scripts": {
|
|
117
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
118
|
+
"typecheck": "tsc --noEmit",
|
|
119
|
+
"test": "vitest run",
|
|
120
|
+
"test:corpus": "vitest run tests/corpus.test.tsx",
|
|
121
|
+
"corpus:audit": "tsx scripts/audit-corpus.ts",
|
|
122
|
+
"corpus:audit:tanstack": "tsx scripts/audit-tanstack-corpus.ts",
|
|
123
|
+
"corpus:audit:external": "tsx scripts/audit-external-corpus.ts",
|
|
124
|
+
"docs:verify": "node scripts/verify-docs.mjs",
|
|
125
|
+
"test:skills": "intent validate skills --check && pnpm run skills:check-version",
|
|
126
|
+
"skills:check-version": "node scripts/sync-skill-version.mjs --check",
|
|
127
|
+
"skills:sync-version": "node scripts/sync-skill-version.mjs",
|
|
128
|
+
"skills:stale": "intent stale",
|
|
129
|
+
"changeset": "changeset",
|
|
130
|
+
"changeset:status": "changeset status",
|
|
131
|
+
"release:version": "changeset version && pnpm run skills:sync-version",
|
|
132
|
+
"release:publish": "pnpm run verify && changeset publish",
|
|
133
|
+
"conformance": "tsx scripts/conformance.ts",
|
|
134
|
+
"bench": "tsx scripts/bench.ts",
|
|
135
|
+
"size": "tsx scripts/measure-size.ts",
|
|
136
|
+
"research": "pnpm run conformance && pnpm run size && pnpm run bench",
|
|
137
|
+
"verify": "pnpm test && pnpm run typecheck && pnpm run build && pnpm run docs:verify && pnpm run test:skills && pnpm run research && pnpm pack --dry-run",
|
|
138
|
+
"version": "pnpm run skills:sync-version"
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -8,7 +8,7 @@ description: >
|
|
|
8
8
|
metadata:
|
|
9
9
|
type: lifecycle
|
|
10
10
|
library: '@tanstack/markdown'
|
|
11
|
-
library_version: '0.0.
|
|
11
|
+
library_version: '0.0.14'
|
|
12
12
|
requires:
|
|
13
13
|
- 'render-markdown'
|
|
14
14
|
sources:
|
|
@@ -127,7 +127,7 @@ import { parseMarkdown } from '@tanstack/markdown/parser'
|
|
|
127
127
|
const cache = new Map<string, MarkdownDocument>()
|
|
128
128
|
|
|
129
129
|
export function renderCachedArticle(key: string, source: string): string {
|
|
130
|
-
const cacheKey = `markdown-0.0.
|
|
130
|
+
const cacheKey = `markdown-0.0.14:${key}`
|
|
131
131
|
let document = cache.get(cacheKey)
|
|
132
132
|
if (!document) {
|
|
133
133
|
document = parseMarkdown(source, {
|