@tanstack/markdown 0.0.11 → 0.0.13
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 +2 -0
- package/dist/html.js +1 -1
- package/dist/inline.js +10 -2
- package/dist/octane.js +1 -1
- package/dist/react.d.ts +6 -1
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +1 -1
- package/package.json +7 -3
- package/skills/custom-extensions/SKILL.md +1 -1
- package/skills/docs-features/SKILL.md +10 -17
- package/skills/octane-rendering/SKILL.md +1 -1
- package/skills/production-pipelines/SKILL.md +9 -17
- package/skills/react-rendering/SKILL.md +20 -17
- package/skills/render-markdown/SKILL.md +1 -1
- package/skills/render-markdown/references/ast-and-options.md +1 -1
package/README.md
CHANGED
|
@@ -42,6 +42,8 @@ export function Article({ source }: { source: string }) @{
|
|
|
42
42
|
|
|
43
43
|
TanStack Markdown targets controlled technical content. It supports the Markdown used by blogs and docs, then spends its remaining complexity budget on deterministic output, renderer parity, malformed-input resilience, and small entry points. It is intentionally not a complete CommonMark, GFM, MDX, or general content-processing implementation.
|
|
44
44
|
|
|
45
|
+
For syntax highlighting, use the tested [TanStack Highlight adapter](./docs/guides/syntax-highlighting.md#tanstack-highlight-adapter). It registers only the languages you choose and returns escaped token markup inside Markdown-owned code containers.
|
|
46
|
+
|
|
45
47
|
## Documentation
|
|
46
48
|
|
|
47
49
|
- [Overview](./docs/overview.md)
|
package/dist/html.js
CHANGED
|
@@ -83,7 +83,7 @@ function renderInlines(nodes, options) {
|
|
|
83
83
|
function renderCodeBlock(node, options) {
|
|
84
84
|
const lang = node.lang ?? 'plaintext';
|
|
85
85
|
const preAttrs = [
|
|
86
|
-
|
|
86
|
+
`class="tm-code${options.codeLineNumbers ? ' tm-code--line-numbers' : ''}"`,
|
|
87
87
|
`data-lang="${escapeAttr(lang)}"`,
|
|
88
88
|
node.title ? `data-code-title="${escapeAttr(node.title)}"` : '',
|
|
89
89
|
node.file ? `data-filename="${escapeAttr(node.file)}"` : '',
|
package/dist/inline.js
CHANGED
|
@@ -319,12 +319,20 @@ function findDelimiter(value, start, delimiter, budget) {
|
|
|
319
319
|
return -1;
|
|
320
320
|
if (value[index - 1] === '\\')
|
|
321
321
|
continue;
|
|
322
|
+
if (!value.startsWith(delimiter, index))
|
|
323
|
+
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;
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
322
331
|
if (delimiter === '~~' && (value[index - 1] === '~' || value[index + 2] === '~'))
|
|
323
332
|
continue;
|
|
324
333
|
if (delimiter[0] === '_' && !canUseUnderscore(value, index, delimiter.length, false))
|
|
325
334
|
continue;
|
|
326
|
-
|
|
327
|
-
return index;
|
|
335
|
+
return index;
|
|
328
336
|
}
|
|
329
337
|
return -1;
|
|
330
338
|
}
|
package/dist/octane.js
CHANGED
|
@@ -88,7 +88,7 @@ function renderCodeBlockOctane(node, options, key) {
|
|
|
88
88
|
}
|
|
89
89
|
: undefined;
|
|
90
90
|
const pre = h(options, 'pre', {
|
|
91
|
-
className: 'tm-code'
|
|
91
|
+
className: `tm-code${options.codeLineNumbers ? ' tm-code--line-numbers' : ''}`,
|
|
92
92
|
'data-lang': lang,
|
|
93
93
|
...(node.title ? { 'data-code-title': node.title } : {}),
|
|
94
94
|
...(node.file ? { 'data-filename': node.file } : {}),
|
package/dist/react.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import type { ComponentType, ReactElement, ReactNode } from 'react';
|
|
1
|
+
import type { ComponentPropsWithoutRef, ComponentType, JSX, ReactElement, ReactNode } from 'react';
|
|
2
2
|
import type { BlockNode, InlineNode, MarkdownInput, RenderOptions } from './types.js';
|
|
3
|
+
type IntrinsicElementName = keyof JSX.IntrinsicElements;
|
|
3
4
|
type ComponentMap = Partial<Record<string, string | ComponentType<any>>>;
|
|
5
|
+
export type MarkdownComponentProps<TagName extends IntrinsicElementName> = ComponentPropsWithoutRef<TagName>;
|
|
6
|
+
export type MarkdownComponents = Partial<{
|
|
7
|
+
[TagName in IntrinsicElementName]: IntrinsicElementName | ComponentType<MarkdownComponentProps<TagName>>;
|
|
8
|
+
}> & Record<string, string | ComponentType<any> | undefined>;
|
|
4
9
|
export interface MarkdownReactOptions extends RenderOptions {
|
|
5
10
|
components?: ComponentMap;
|
|
6
11
|
}
|
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,aAAa,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;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"}
|
package/dist/react.js
CHANGED
|
@@ -91,7 +91,7 @@ function renderCodeBlockReact(node, options, key) {
|
|
|
91
91
|
}
|
|
92
92
|
: undefined;
|
|
93
93
|
const pre = h(options, 'pre', {
|
|
94
|
-
className: 'tm-code'
|
|
94
|
+
className: `tm-code${options.codeLineNumbers ? ' tm-code--line-numbers' : ''}`,
|
|
95
95
|
'data-lang': lang,
|
|
96
96
|
...(node.title ? { 'data-code-title': node.title } : {}),
|
|
97
97
|
...(node.file ? { 'data-filename': node.file } : {}),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/markdown",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A tiny, fast, deterministic Markdown renderer for blogs and documentation.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -86,6 +86,7 @@
|
|
|
86
86
|
}
|
|
87
87
|
},
|
|
88
88
|
"devDependencies": {
|
|
89
|
+
"@tanstack/highlight": "0.0.6",
|
|
89
90
|
"@tanstack/intent": "^0.3.6",
|
|
90
91
|
"@types/node": "^24.0.0",
|
|
91
92
|
"@types/react": "^19.0.0",
|
|
@@ -120,12 +121,15 @@
|
|
|
120
121
|
"corpus:audit:tanstack": "tsx scripts/audit-tanstack-corpus.ts",
|
|
121
122
|
"corpus:audit:external": "tsx scripts/audit-external-corpus.ts",
|
|
122
123
|
"docs:verify": "node scripts/verify-docs.mjs",
|
|
123
|
-
"test:skills": "intent validate skills --check",
|
|
124
|
+
"test:skills": "intent validate skills --check && pnpm run skills:check-version",
|
|
125
|
+
"skills:check-version": "node scripts/sync-skill-version.mjs --check",
|
|
126
|
+
"skills:sync-version": "node scripts/sync-skill-version.mjs",
|
|
124
127
|
"skills:stale": "intent stale",
|
|
125
128
|
"conformance": "tsx scripts/conformance.ts",
|
|
126
129
|
"bench": "tsx scripts/bench.ts",
|
|
127
130
|
"size": "tsx scripts/measure-size.ts",
|
|
128
131
|
"research": "pnpm run conformance && pnpm run size && pnpm run bench",
|
|
129
|
-
"verify": "pnpm test && pnpm run typecheck && pnpm run build && pnpm run docs:verify && pnpm run test:skills && pnpm run research && pnpm pack --dry-run"
|
|
132
|
+
"verify": "pnpm test && pnpm run typecheck && pnpm run build && pnpm run docs:verify && pnpm run test:skills && pnpm run research && pnpm pack --dry-run",
|
|
133
|
+
"version": "pnpm run skills:sync-version"
|
|
130
134
|
}
|
|
131
135
|
}
|
|
@@ -9,7 +9,7 @@ description: >
|
|
|
9
9
|
metadata:
|
|
10
10
|
type: core
|
|
11
11
|
library: '@tanstack/markdown'
|
|
12
|
-
library_version: '0.0.
|
|
12
|
+
library_version: '0.0.13'
|
|
13
13
|
requires:
|
|
14
14
|
- 'render-markdown'
|
|
15
15
|
sources:
|
|
@@ -392,11 +392,10 @@ export const html = renderHtml(source)
|
|
|
392
392
|
Correct:
|
|
393
393
|
|
|
394
394
|
````ts
|
|
395
|
-
import {
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
} from '@tanstack/highlight'
|
|
395
|
+
import { createHighlighter } from '@tanstack/highlight/core'
|
|
396
|
+
import { plaintext } from '@tanstack/highlight/languages/plaintext'
|
|
397
|
+
import { ts } from '@tanstack/highlight/languages/ts'
|
|
398
|
+
import { createTanStackMarkdownHighlighter } from '@tanstack/highlight/markdown'
|
|
400
399
|
import { renderHtml } from '@tanstack/markdown/html'
|
|
401
400
|
|
|
402
401
|
const source = `\`\`\`ts file="app.ts" {2}
|
|
@@ -404,18 +403,12 @@ const one = 1
|
|
|
404
403
|
const two = 2
|
|
405
404
|
\`\`\``
|
|
406
405
|
|
|
406
|
+
const highlighter = createHighlighter({
|
|
407
|
+
languages: [plaintext, ts],
|
|
408
|
+
})
|
|
409
|
+
|
|
407
410
|
export const html = renderHtml(source, {
|
|
408
|
-
highlighter(
|
|
409
|
-
const result = tokenize(code, { lang })
|
|
410
|
-
return renderNodesToHtml(
|
|
411
|
-
renderTokens(result.tokens, {
|
|
412
|
-
decorations: options?.highlightLines?.map((line) => ({
|
|
413
|
-
lines: line,
|
|
414
|
-
className: 'is-highlighted',
|
|
415
|
-
})),
|
|
416
|
-
}),
|
|
417
|
-
)
|
|
418
|
-
},
|
|
411
|
+
highlighter: createTanStackMarkdownHighlighter(highlighter),
|
|
419
412
|
})
|
|
420
413
|
````
|
|
421
414
|
|
|
@@ -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.13'
|
|
12
12
|
requires:
|
|
13
13
|
- 'render-markdown'
|
|
14
14
|
sources:
|
|
@@ -58,23 +58,15 @@ Fix: Separate trusted and untrusted entry points, keep trusted callbacks disable
|
|
|
58
58
|
### Check: Return trusted code contents only
|
|
59
59
|
|
|
60
60
|
Expected:
|
|
61
|
-
|
|
62
61
|
```ts
|
|
63
62
|
import { renderHtml } from '@tanstack/markdown/html'
|
|
64
|
-
import {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
export const html = renderHtml(source, {
|
|
73
|
-
highlighter(code, lang, options) {
|
|
74
|
-
const result = tokenize(code, { lang: lang ?? 'plaintext' })
|
|
75
|
-
const decorations = options?.highlightLines?.map((lines) => ({ lines, className: 'is-highlighted' }))
|
|
76
|
-
return renderNodesToHtml(renderTokens(result.tokens, { lineNumbers: options?.lineNumbers, decorations }))
|
|
77
|
-
},
|
|
63
|
+
import { createHighlighter } from '@tanstack/highlight/core'
|
|
64
|
+
import { plaintext } from '@tanstack/highlight/languages/plaintext'
|
|
65
|
+
import { ts } from '@tanstack/highlight/languages/ts'
|
|
66
|
+
import { createTanStackMarkdownHighlighter } from '@tanstack/highlight/markdown'
|
|
67
|
+
const highlighter = createHighlighter({ languages: [plaintext, ts] })
|
|
68
|
+
export const html = renderHtml('```ts {1}\nconst answer = 42\n```', {
|
|
69
|
+
highlighter: createTanStackMarkdownHighlighter(highlighter),
|
|
78
70
|
})
|
|
79
71
|
```
|
|
80
72
|
|
|
@@ -135,7 +127,7 @@ import { parseMarkdown } from '@tanstack/markdown/parser'
|
|
|
135
127
|
const cache = new Map<string, MarkdownDocument>()
|
|
136
128
|
|
|
137
129
|
export function renderCachedArticle(key: string, source: string): string {
|
|
138
|
-
const cacheKey = `markdown-0.0.
|
|
130
|
+
const cacheKey = `markdown-0.0.13:${key}`
|
|
139
131
|
let document = cache.get(cacheKey)
|
|
140
132
|
if (!document) {
|
|
141
133
|
document = parseMarkdown(source, {
|
|
@@ -9,7 +9,7 @@ metadata:
|
|
|
9
9
|
type: framework
|
|
10
10
|
library: '@tanstack/markdown'
|
|
11
11
|
framework: 'react'
|
|
12
|
-
library_version: '0.0.
|
|
12
|
+
library_version: '0.0.13'
|
|
13
13
|
requires:
|
|
14
14
|
- 'render-markdown'
|
|
15
15
|
sources:
|
|
@@ -48,31 +48,34 @@ export function Article({ source }: { source: string }) {
|
|
|
48
48
|
### Replace emitted links with an application component
|
|
49
49
|
|
|
50
50
|
```tsx
|
|
51
|
-
import
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
51
|
+
import {
|
|
52
|
+
Markdown,
|
|
53
|
+
type MarkdownComponents,
|
|
54
|
+
} from '@tanstack/markdown/react'
|
|
55
|
+
|
|
56
|
+
const components = {
|
|
57
|
+
a(props) {
|
|
58
|
+
const external = props.href?.startsWith('http') ?? false
|
|
59
|
+
return (
|
|
60
|
+
<a
|
|
61
|
+
{...props}
|
|
62
|
+
rel={external ? 'nofollow noopener noreferrer' : props.rel}
|
|
63
|
+
target={external ? '_blank' : props.target}
|
|
64
|
+
/>
|
|
65
|
+
)
|
|
66
|
+
},
|
|
67
|
+
} satisfies MarkdownComponents
|
|
65
68
|
|
|
66
69
|
export function Article({ source }: { source: string }) {
|
|
67
70
|
return (
|
|
68
71
|
<article>
|
|
69
|
-
<Markdown components={
|
|
72
|
+
<Markdown components={components}>{source}</Markdown>
|
|
70
73
|
</article>
|
|
71
74
|
)
|
|
72
75
|
}
|
|
73
76
|
```
|
|
74
77
|
|
|
75
|
-
|
|
78
|
+
Known intrinsic keys infer their matching React props. Arbitrary extension component tag names remain supported.
|
|
76
79
|
|
|
77
80
|
### Parse once before repeated React rendering
|
|
78
81
|
|