@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/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.d.ts.map +1 -1
- package/dist/html.js +33 -34
- package/dist/inline.d.ts.map +1 -1
- package/dist/inline.js +106 -212
- package/dist/octane.d.ts.map +1 -1
- package/dist/octane.js +28 -23
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +166 -194
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +45 -37
- package/dist/types.d.ts +10 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +6 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +37 -25
- package/package.json +11 -1
- package/skills/custom-extensions/SKILL.md +11 -4
- package/skills/docs-features/SKILL.md +1 -1
- package/skills/docs-features/references/docs-metadata.md +1 -1
- package/skills/octane-rendering/SKILL.md +1 -1
- package/skills/production-pipelines/SKILL.md +10 -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 +8 -3
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
|
}
|
|
@@ -10,14 +11,14 @@ export function renderMarkdownReact(input, options = {}) {
|
|
|
10
11
|
export function renderBlockReact(node, options = {}, key) {
|
|
11
12
|
switch (node.type) {
|
|
12
13
|
case 'heading':
|
|
13
|
-
return h(options, `h${node.depth}`, { key, ...(node.id
|
|
14
|
+
return h(options, `h${node.depth}`, { key, ...(node.id && { id: node.id }), ...(node.framework && { 'data-framework': node.framework }) }, renderInlines(node.children, options), renderHeadingAnchorReact(node.id, options));
|
|
14
15
|
case 'paragraph':
|
|
15
16
|
return h(options, 'p', { key }, renderInlines(node.children, options));
|
|
16
17
|
case 'code':
|
|
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
|
|
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,21 +54,23 @@ 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}`,
|
|
60
61
|
}, node.number));
|
|
61
62
|
case 'link':
|
|
62
|
-
return h(options, 'a', { key, href: node.href, ...(node.title
|
|
63
|
+
return h(options, 'a', { key, href: node.href, ...(node.title && { title: node.title }) }, renderInlines(node.children, options));
|
|
63
64
|
case 'image':
|
|
64
|
-
return h(options, 'img', { key, src: node.src, alt: node.alt, ...(node.title
|
|
65
|
+
return h(options, 'img', { key, src: node.src, alt: node.alt, ...(node.title && { title: node.title }) });
|
|
65
66
|
case 'break':
|
|
66
67
|
return h(options, 'br', { key });
|
|
67
68
|
case 'inlineHtml':
|
|
68
69
|
return options.allowHtml
|
|
69
70
|
? h(options, 'span', { key, dangerouslySetInnerHTML: { __html: node.value } })
|
|
70
71
|
: node.value;
|
|
72
|
+
case 'inlineComponent':
|
|
73
|
+
return renderComponentReact(node, options, key);
|
|
71
74
|
}
|
|
72
75
|
}
|
|
73
76
|
function renderInlines(nodes, options) {
|
|
@@ -76,14 +79,17 @@ function renderInlines(nodes, options) {
|
|
|
76
79
|
function renderCodeBlockReact(node, options, key) {
|
|
77
80
|
const lang = node.lang ?? 'plaintext';
|
|
78
81
|
const highlighter = options.highlighter;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const content = highlighter ? undefined :
|
|
82
|
+
// Keep completed groups of lines in stable text nodes as code grows. Grouping
|
|
83
|
+
// bounds React's sibling work without relaying out the entire code block.
|
|
84
|
+
// Static rendering and custom code components retain string children.
|
|
85
|
+
const content = highlighter ? undefined : !options.components?.code && options.extensions?.some(extension => extension.name === 'streaming')
|
|
86
|
+
? node.value.match(/(?:[^\n]*\n){1,4}|[^\n]+$/g)
|
|
87
|
+
: node.value;
|
|
83
88
|
const highlighted = highlighter
|
|
84
89
|
? {
|
|
85
90
|
dangerouslySetInnerHTML: {
|
|
86
91
|
__html: highlighter(node.value, lang, {
|
|
92
|
+
...(node.meta && { meta: node.meta }),
|
|
87
93
|
...(node.highlightLines && { highlightLines: node.highlightLines }),
|
|
88
94
|
...(options.codeLineNumbers !== undefined && { lineNumbers: options.codeLineNumbers }),
|
|
89
95
|
}),
|
|
@@ -91,19 +97,20 @@ function renderCodeBlockReact(node, options, key) {
|
|
|
91
97
|
}
|
|
92
98
|
: undefined;
|
|
93
99
|
const pre = h(options, 'pre', {
|
|
100
|
+
key,
|
|
94
101
|
className: `tm-code${options.codeLineNumbers ? ' tm-code--line-numbers' : ''}`,
|
|
95
102
|
'data-lang': lang,
|
|
96
|
-
...(node.
|
|
97
|
-
...(node.
|
|
98
|
-
...(node.
|
|
99
|
-
|
|
103
|
+
...(node.meta && { 'data-meta': node.meta }),
|
|
104
|
+
...(node.title && { 'data-code-title': node.title }),
|
|
105
|
+
...(node.file && { 'data-filename': node.file }),
|
|
106
|
+
...(node.framework && { 'data-framework': node.framework }),
|
|
107
|
+
}, h(options, 'code', { className: `language-${lang}`, ...highlighted }, content));
|
|
100
108
|
if (!node.title)
|
|
101
|
-
return
|
|
109
|
+
return pre;
|
|
102
110
|
return h(options, 'figure', { key, className: 'tm-code-frame', 'data-lang': lang }, h(options, 'figcaption', null, node.title), pre);
|
|
103
111
|
}
|
|
104
112
|
function renderListItemChildrenReact(children, checked, loose, options, key) {
|
|
105
|
-
|
|
106
|
-
const task = checked === undefined
|
|
113
|
+
let result = checked === undefined
|
|
107
114
|
? []
|
|
108
115
|
: [
|
|
109
116
|
h(options, 'input', {
|
|
@@ -115,20 +122,21 @@ function renderListItemChildrenReact(children, checked, loose, options, key) {
|
|
|
115
122
|
}),
|
|
116
123
|
' ',
|
|
117
124
|
];
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
125
|
+
for (let index = 0; index < children.length; index++) {
|
|
126
|
+
const child = children[index];
|
|
127
|
+
if (child.type === 'paragraph' && (!loose || index === 0)) {
|
|
128
|
+
for (const inline of renderInlines(child.children, options))
|
|
129
|
+
result.push(inline);
|
|
130
|
+
if (loose)
|
|
131
|
+
result = [h(options, 'p', { key: `${key}:paragraph` }, result)];
|
|
132
|
+
}
|
|
133
|
+
else
|
|
134
|
+
result.push(renderBlockReact(child, options, `${key}:${index}`));
|
|
124
135
|
}
|
|
125
|
-
return
|
|
126
|
-
}
|
|
127
|
-
function renderListChildReact(child, loose, options, key) {
|
|
128
|
-
return !loose && child.type === 'paragraph' ? renderInlines(child.children, options) : [renderBlockReact(child, options, key)];
|
|
136
|
+
return result;
|
|
129
137
|
}
|
|
130
138
|
function renderTableCellReact(tag, cell, align, options, key) {
|
|
131
|
-
return h(options, tag, { key, ...(align
|
|
139
|
+
return h(options, tag, { key, ...(align && { style: { textAlign: align } }) }, renderInlines(cell.children, options));
|
|
132
140
|
}
|
|
133
141
|
function renderFootnotesReact(items, options, key) {
|
|
134
142
|
return h(options, 'section', { key, 'data-footnotes': '', className: 'footnotes' }, h(options, 'h2', { id: 'footnote-label', className: 'sr-only' }, 'Footnotes', renderHeadingAnchorReact('footnote-label', options)), h(options, 'ol', null, items.map(item => h(options, 'li', { key: item.id, id: `user-content-fn-${item.id}` }, renderFootnoteItemReact(item, options)))));
|
|
@@ -136,19 +144,20 @@ function renderFootnotesReact(items, options, key) {
|
|
|
136
144
|
function renderFootnoteItemReact(item, options) {
|
|
137
145
|
const lastIndex = item.children.length - 1;
|
|
138
146
|
const backrefs = renderFootnoteBackrefsReact(item, options);
|
|
139
|
-
|
|
140
|
-
return [h(options, 'p', { key: 'backref-wrapper' }, backrefs.slice(1))];
|
|
141
|
-
return item.children.map((child, index) => {
|
|
147
|
+
const result = item.children.map((child, index) => {
|
|
142
148
|
if (index === lastIndex && child.type === 'paragraph') {
|
|
143
149
|
return h(options, 'p', { key: index }, renderInlines(child.children, options), backrefs);
|
|
144
150
|
}
|
|
145
151
|
return renderBlockReact(child, options, `${index}`);
|
|
146
152
|
});
|
|
153
|
+
if (item.children[lastIndex]?.type !== 'paragraph')
|
|
154
|
+
result.push(h(options, 'p', { key: 'backref-wrapper' }, backrefs.slice(1)));
|
|
155
|
+
return result;
|
|
147
156
|
}
|
|
148
157
|
function renderFootnoteBackrefsReact(item, options) {
|
|
149
158
|
const result = [];
|
|
150
159
|
for (let index = 1; index <= (item.referenceCount ?? 1); index++) {
|
|
151
|
-
const referenceId =
|
|
160
|
+
const referenceId = footnoteReferenceId(item.id, index);
|
|
152
161
|
const label = index === 1 ? `${item.number}` : `${item.number}-${index}`;
|
|
153
162
|
result.push(' ', h(options, 'a', {
|
|
154
163
|
key: index,
|
|
@@ -160,24 +169,23 @@ function renderFootnoteBackrefsReact(item, options) {
|
|
|
160
169
|
}
|
|
161
170
|
return result;
|
|
162
171
|
}
|
|
163
|
-
function footnoteReferenceId(node) {
|
|
164
|
-
return node.referenceIndex && node.referenceIndex > 1 ? `${node.id}-${node.referenceIndex}` : node.id;
|
|
165
|
-
}
|
|
166
172
|
function h(options, tag, props, ...children) {
|
|
167
173
|
const component = typeof tag === 'string' ? options.components?.[tag] ?? tag : tag;
|
|
168
174
|
return createElement(component, props, ...children);
|
|
169
175
|
}
|
|
170
176
|
function renderComponentReact(node, options, key) {
|
|
171
|
-
const tag = node.tagName ?? 'md-comment-component';
|
|
177
|
+
const tag = node.tagName ?? (node.type === 'inlineComponent' ? 'span' : 'md-comment-component');
|
|
172
178
|
const props = {
|
|
173
|
-
...
|
|
179
|
+
...node.properties,
|
|
174
180
|
};
|
|
175
181
|
if (!node.tagName) {
|
|
176
182
|
props['data-component'] = node.name;
|
|
177
183
|
if (!props['data-attributes'])
|
|
178
184
|
props['data-attributes'] = JSON.stringify(node.attributes);
|
|
179
185
|
}
|
|
180
|
-
return h(options, tag, { key, ...props }, node.
|
|
186
|
+
return h(options, tag, { key, ...props }, node.type === 'inlineComponent'
|
|
187
|
+
? renderInlines(node.children, options)
|
|
188
|
+
: node.children.map((child, index) => renderBlockReact(child, options, `${key}:${index}`)));
|
|
181
189
|
}
|
|
182
190
|
function renderHeadingAnchorReact(id, options) {
|
|
183
191
|
if (!id || !options.headingAnchors)
|
package/dist/types.d.ts
CHANGED
|
@@ -84,7 +84,11 @@ export interface ComponentNode {
|
|
|
84
84
|
tagName?: string;
|
|
85
85
|
properties?: Record<string, string>;
|
|
86
86
|
}
|
|
87
|
-
export
|
|
87
|
+
export interface InlineComponentNode extends Omit<ComponentNode, 'type' | 'children'> {
|
|
88
|
+
type: 'inlineComponent';
|
|
89
|
+
children: InlineNode[];
|
|
90
|
+
}
|
|
91
|
+
export type InlineNode = TextNode | CodeSpanNode | StrongNode | EmphasisNode | StrikeNode | FootnoteReferenceNode | LinkNode | ImageNode | BreakNode | HtmlInlineNode | InlineComponentNode;
|
|
88
92
|
export interface TextNode {
|
|
89
93
|
type: 'text';
|
|
90
94
|
value: string;
|
|
@@ -158,6 +162,7 @@ export interface HtmlRenderContext {
|
|
|
158
162
|
}
|
|
159
163
|
export interface ParseOptions {
|
|
160
164
|
allowHtml?: boolean;
|
|
165
|
+
urlTransform?: UrlTransform;
|
|
161
166
|
frontmatter?: boolean;
|
|
162
167
|
headingIds?: boolean | ((text: string, index: number) => string);
|
|
163
168
|
extensions?: MarkdownExtension[];
|
|
@@ -166,6 +171,9 @@ export interface ParseOptions {
|
|
|
166
171
|
footnoteOrder?: string[];
|
|
167
172
|
footnoteCounts?: Record<string, number>;
|
|
168
173
|
}
|
|
174
|
+
export interface UrlTransform {
|
|
175
|
+
(url: string, kind: 'link' | 'image', defaultUrl: string): string | null;
|
|
176
|
+
}
|
|
169
177
|
export interface RenderOptions extends ParseOptions {
|
|
170
178
|
highlighter?: CodeHighlighter;
|
|
171
179
|
codeLineNumbers?: boolean;
|
|
@@ -175,6 +183,7 @@ export interface CodeHighlighter {
|
|
|
175
183
|
(code: string, lang?: string, options?: CodeHighlightOptions): string;
|
|
176
184
|
}
|
|
177
185
|
export interface CodeHighlightOptions {
|
|
186
|
+
meta?: string;
|
|
178
187
|
highlightLines?: number[];
|
|
179
188
|
lineNumbers?: boolean;
|
|
180
189
|
}
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
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;AAarE,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
|
@@ -4,15 +4,17 @@ const htmlEscapes = {
|
|
|
4
4
|
'>': '>',
|
|
5
5
|
'"': '"',
|
|
6
6
|
"'": ''',
|
|
7
|
+
'`': '`',
|
|
7
8
|
};
|
|
9
|
+
const escapeCharacter = (char) => htmlEscapes[char];
|
|
8
10
|
export function normalizeInput(value) {
|
|
9
11
|
return value.replace(/^\uFEFF/, '').replace(/\r\n?/g, '\n');
|
|
10
12
|
}
|
|
11
13
|
export function escapeHtml(value) {
|
|
12
|
-
return value.replace(/[&<>"']/g,
|
|
14
|
+
return value.replace(/[&<>"']/g, escapeCharacter);
|
|
13
15
|
}
|
|
14
16
|
export function escapeAttr(value) {
|
|
15
|
-
return
|
|
17
|
+
return value.replace(/[&<>"'`]/g, escapeCharacter);
|
|
16
18
|
}
|
|
17
19
|
export function isBlank(value) {
|
|
18
20
|
return /^\s*$/.test(value);
|
|
@@ -35,40 +37,50 @@ export function plainText(nodes) {
|
|
|
35
37
|
}
|
|
36
38
|
return value;
|
|
37
39
|
}
|
|
38
|
-
export function createSlugger() {
|
|
40
|
+
export function createSlugger(normalize = headingSlug) {
|
|
39
41
|
const seen = new Map();
|
|
40
42
|
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}`;
|
|
43
|
+
const base = normalize(value);
|
|
44
|
+
let count = seen.get(base) ?? 1;
|
|
45
|
+
let id = base;
|
|
46
|
+
while (seen.has(id))
|
|
47
|
+
id = `${base}-${++count}`;
|
|
48
|
+
seen.set(base, count);
|
|
49
|
+
seen.set(id, 1);
|
|
50
|
+
return id;
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
|
+
function headingSlug(value) {
|
|
54
|
+
return value
|
|
55
|
+
.toLowerCase()
|
|
56
|
+
.normalize('NFKD')
|
|
57
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
58
|
+
.replace(/&[a-z0-9#]+;/gi, '')
|
|
59
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
60
|
+
.replace(/^-+|-+$/g, '') || 'section';
|
|
61
|
+
}
|
|
53
62
|
export function sanitizeUrl(value) {
|
|
54
|
-
const trimmed = value.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
return
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return
|
|
63
|
+
const trimmed = value.replace(/[\u0000-\u001F\u007F\s]+/g, '');
|
|
64
|
+
return /^(?!https?:|mailto:|tel:)[a-z][a-z0-9+.-]*:/i.test(trimmed) ? '' : trimmed;
|
|
65
|
+
}
|
|
66
|
+
export function parseDestination(value) {
|
|
67
|
+
const match = value.match(/^(?:<([^<>\n]*)>|([^<>\s]*?))(?:\s+(?:"([^"]*)"|'([^']*)'|\(([^)]*)\)))?$/);
|
|
68
|
+
if (!match)
|
|
69
|
+
return undefined;
|
|
70
|
+
const href = match[1] ?? match[2];
|
|
71
|
+
const title = match[3] ?? match[4] ?? match[5];
|
|
72
|
+
return { href: href.replace(/\\([!-/:-@\[-`{-~])/g, '$1'), ...(title ? { title } : {}) };
|
|
64
73
|
}
|
|
65
74
|
export function normalizeReferenceLabel(value) {
|
|
66
|
-
return value.trim().toLowerCase();
|
|
75
|
+
return value.trim().replace(/\s+/g, ' ').toLowerCase();
|
|
67
76
|
}
|
|
68
77
|
export function footnoteId(label) {
|
|
69
78
|
return normalizeReferenceLabel(label)
|
|
70
79
|
.replace(/[^a-z0-9_-]+/g, '-')
|
|
71
|
-
.replace(/^-+|-+$/g, '');
|
|
80
|
+
.replace(/^-+|-+$/g, '') || 'footnote';
|
|
81
|
+
}
|
|
82
|
+
export function footnoteReferenceId(id, index = 1) {
|
|
83
|
+
return index > 1 ? `${id}-${index}` : id;
|
|
72
84
|
}
|
|
73
85
|
export function splitLines(value) {
|
|
74
86
|
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.15",
|
|
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
|
+
"@changesets/cli": "3.0.2",
|
|
89
90
|
"@tanstack/highlight": "0.0.6",
|
|
90
91
|
"@tanstack/intent": "^0.3.6",
|
|
91
92
|
"@types/node": "^24.0.0",
|
|
@@ -99,11 +100,14 @@
|
|
|
99
100
|
"marked": "^18.0.5",
|
|
100
101
|
"micromark": "^4.0.2",
|
|
101
102
|
"octane": "0.1.12",
|
|
103
|
+
"playwright": "1.63.0",
|
|
102
104
|
"react": "^19.0.0",
|
|
103
105
|
"react-dom": "^19.0.0",
|
|
104
106
|
"rehype-stringify": "^10.0.1",
|
|
105
107
|
"remark-parse": "^11.0.0",
|
|
106
108
|
"remark-rehype": "^11.1.2",
|
|
109
|
+
"streamdown": "2.6.0",
|
|
110
|
+
"streaming-markdown": "0.2.15",
|
|
107
111
|
"tsx": "^4.20.0",
|
|
108
112
|
"typescript": "^5.8.0",
|
|
109
113
|
"unified": "^11.0.5",
|
|
@@ -116,6 +120,7 @@
|
|
|
116
120
|
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
117
121
|
"typecheck": "tsc --noEmit",
|
|
118
122
|
"test": "vitest run",
|
|
123
|
+
"test:streaming-browser": "tsx scripts/verify-streaming-browser.ts",
|
|
119
124
|
"test:corpus": "vitest run tests/corpus.test.tsx",
|
|
120
125
|
"corpus:audit": "tsx scripts/audit-corpus.ts",
|
|
121
126
|
"corpus:audit:tanstack": "tsx scripts/audit-tanstack-corpus.ts",
|
|
@@ -125,8 +130,13 @@
|
|
|
125
130
|
"skills:check-version": "node scripts/sync-skill-version.mjs --check",
|
|
126
131
|
"skills:sync-version": "node scripts/sync-skill-version.mjs",
|
|
127
132
|
"skills:stale": "intent stale",
|
|
133
|
+
"changeset": "changeset",
|
|
134
|
+
"changeset:status": "changeset status",
|
|
135
|
+
"release:version": "changeset version && pnpm run skills:sync-version",
|
|
136
|
+
"release:publish": "pnpm run verify && changeset publish",
|
|
128
137
|
"conformance": "tsx scripts/conformance.ts",
|
|
129
138
|
"bench": "tsx scripts/bench.ts",
|
|
139
|
+
"bench:streaming": "tsx scripts/bench-streaming-browser.ts",
|
|
130
140
|
"size": "tsx scripts/measure-size.ts",
|
|
131
141
|
"research": "pnpm run conformance && pnpm run size && pnpm run bench",
|
|
132
142
|
"verify": "pnpm test && pnpm run typecheck && pnpm run build && pnpm run docs:verify && pnpm run test:skills && pnpm run research && pnpm pack --dry-run",
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
name: 'custom-extensions'
|
|
3
3
|
description: >
|
|
4
4
|
Implement MarkdownExtension block parsers, inline and document transforms,
|
|
5
|
-
HTML hooks, and portable
|
|
5
|
+
HTML hooks, and portable block and inline component output. Load when adding deterministic
|
|
6
6
|
custom syntax or rendering behavior across HTML, React, and Octane.
|
|
7
7
|
metadata:
|
|
8
8
|
type: core
|
|
9
9
|
library: '@tanstack/markdown'
|
|
10
|
-
library_version: '0.0.
|
|
10
|
+
library_version: '0.0.15'
|
|
11
11
|
requires:
|
|
12
12
|
- 'render-markdown'
|
|
13
13
|
sources:
|
|
@@ -111,6 +111,13 @@ console.log(html)
|
|
|
111
111
|
|
|
112
112
|
Transforms receive built-in inline nodes and must return a deterministic replacement array.
|
|
113
113
|
|
|
114
|
+
For custom inline UI, return an `InlineComponentNode` with `type: 'inlineComponent'`,
|
|
115
|
+
`name`, `attributes`, inline `children`, and optional `tagName` and string `properties`.
|
|
116
|
+
It uses the same emitted-tag component maps as a block `ComponentNode`, but defaults
|
|
117
|
+
to `<span>` instead of `<md-comment-component>`. Keep tag and property names under
|
|
118
|
+
extension control, use phrasing content, and recurse through inline `children` when
|
|
119
|
+
transforming text nested inside links or emphasis. This does not require `allowHtml`.
|
|
120
|
+
|
|
114
121
|
### Derive document metadata after parsing
|
|
115
122
|
|
|
116
123
|
```ts
|
|
@@ -370,7 +377,7 @@ export function Article() {
|
|
|
370
377
|
}
|
|
371
378
|
```
|
|
372
379
|
|
|
373
|
-
`renderHtml` hooks do not run in React or Octane; a `ComponentNode` and emitted-tag component mapping is the portable path.
|
|
380
|
+
`renderHtml` hooks do not run in React or Octane; a `ComponentNode` or `InlineComponentNode` and emitted-tag component mapping is the portable path.
|
|
374
381
|
|
|
375
382
|
Source: `docs/guides/extensions.md`
|
|
376
383
|
|
|
@@ -425,7 +432,7 @@ Source: `docs/guides/extensions.md`
|
|
|
425
432
|
|
|
426
433
|
### HIGH Rich output versus untrusted-content safety
|
|
427
434
|
|
|
428
|
-
Prefer
|
|
435
|
+
Prefer block or inline component nodes plus application components for rich output. Treat `allowHtml`, extension HTML strings, and highlighter markup as explicit trusted boundaries; see `production-pipelines`.
|
|
429
436
|
|
|
430
437
|
### MEDIUM Parse-ahead performance versus option timing
|
|
431
438
|
|
|
@@ -317,4 +317,4 @@ interface CodeBlockNode {
|
|
|
317
317
|
- `{2,4-6}` and `lines=2,4-6` produce sorted, unique positive line numbers.
|
|
318
318
|
- Invalid ranges are ignored, and each individual range expands to at most 1,000 lines.
|
|
319
319
|
|
|
320
|
-
HTML code blocks expose `data-lang`, `data-code-title`, `data-filename`, and `data-framework` when the corresponding values exist.
|
|
320
|
+
HTML, React, and Octane code blocks expose `data-lang`, `data-meta`, `data-code-title`, `data-filename`, and `data-framework` on `<pre>` when the corresponding values exist. Framework `pre` replacements can read `props['data-meta']`; highlighters receive the raw metadata in `options.meta` alongside highlight lines. Metadata alone does not create token or line markup.
|
|
@@ -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.15'
|
|
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.15:${key}`
|
|
131
131
|
let document = cache.get(cacheKey)
|
|
132
132
|
if (!document) {
|
|
133
133
|
document = parseMarkdown(source, {
|
|
@@ -331,6 +331,14 @@ export function renderWithPolicy(
|
|
|
331
331
|
|
|
332
332
|
Core escaping and protocol filtering do not enforce application-specific outbound-link, image, or final-HTML policy.
|
|
333
333
|
|
|
334
|
+
For parsed Markdown destinations, `urlTransform(url, kind, defaultUrl)` can return
|
|
335
|
+
the screened default, a trusted replacement, or `null` to remove the link or image
|
|
336
|
+
while retaining its label content. Only allow data images after application validation
|
|
337
|
+
of their content, MIME type, and size. Keep the default policy for other destinations.
|
|
338
|
+
Callback results are not screened again, and the callback does not apply to raw HTML,
|
|
339
|
+
extension-created URLs, or ASTs supplied directly to renderers. Do not enable raw HTML
|
|
340
|
+
to allow images. Include URL-policy changes in persisted AST cache invalidation.
|
|
341
|
+
|
|
334
342
|
Source: `docs/core-concepts/security.md`
|
|
335
343
|
|
|
336
344
|
### HIGH Assuming complete CommonMark behavior
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AST and Options Reference
|
|
2
2
|
|
|
3
|
-
This reference targets `@tanstack/markdown@0.0.
|
|
3
|
+
This reference targets `@tanstack/markdown@0.0.15`. Shared public types are
|
|
4
4
|
exported from `@tanstack/markdown`.
|
|
5
5
|
|
|
6
6
|
## Entry Points
|
|
@@ -190,10 +190,11 @@ Important rendering invariants:
|
|
|
190
190
|
| `emphasis` | `EmphasisNode` | inline `children` |
|
|
191
191
|
| `strike` | `StrikeNode` | inline `children` |
|
|
192
192
|
| `footnoteReference` | `FootnoteReferenceNode` | normalized `id`, display `number`, optional `referenceIndex` |
|
|
193
|
-
| `link` | `LinkNode` |
|
|
194
|
-
| `image` | `ImageNode` |
|
|
193
|
+
| `link` | `LinkNode` | policy-processed `href`, optional `title`, inline `children` |
|
|
194
|
+
| `image` | `ImageNode` | policy-processed `src`, text `alt`, optional `title` |
|
|
195
195
|
| `break` | `BreakNode` | no additional fields |
|
|
196
196
|
| `inlineHtml` | `HtmlInlineNode` | raw `value` |
|
|
197
|
+
| `inlineComponent` | `InlineComponentNode` | `name`, `attributes`, inline `children`, optional `tagName`, optional string `properties` |
|
|
197
198
|
|
|
198
199
|
`HtmlInlineNode` is created only when parsing with `allowHtml: true`.
|
|
199
200
|
Repeated footnote references use `referenceIndex` to produce unique source
|
|
@@ -206,10 +207,12 @@ import type {
|
|
|
206
207
|
FootnoteDefinition,
|
|
207
208
|
LinkReferenceDefinition,
|
|
208
209
|
MarkdownExtension,
|
|
210
|
+
UrlTransform,
|
|
209
211
|
} from '@tanstack/markdown'
|
|
210
212
|
|
|
211
213
|
interface ParseOptions {
|
|
212
214
|
allowHtml?: boolean
|
|
215
|
+
urlTransform?: UrlTransform
|
|
213
216
|
frontmatter?: boolean
|
|
214
217
|
headingIds?: boolean | ((text: string, index: number) => string)
|
|
215
218
|
extensions?: MarkdownExtension[]
|
|
@@ -223,6 +226,7 @@ interface ParseOptions {
|
|
|
223
226
|
| Option | Default | Contract |
|
|
224
227
|
| --- | --- | --- |
|
|
225
228
|
| `allowHtml` | `false` | Recognize raw block and inline HTML nodes. Rendering also requires this option to emit their raw values. |
|
|
229
|
+
| `urlTransform` | built-in policy | Synchronous `(url, kind, defaultUrl) => string \| null`; return the screened default, a trusted replacement, or `null` to keep only label content. Applies to Markdown destinations, not raw HTML or supplied ASTs. |
|
|
226
230
|
| `frontmatter` | `true` | Extract one leading `---` block into `document.frontmatter`. The library does not parse YAML. |
|
|
227
231
|
| `headingIds` | `true` | Generate duplicate-safe IDs, disable IDs with `false`, or return an ID from `(text, normalizedLineIndex)`. |
|
|
228
232
|
| `extensions` | `[]` | Run block parsers and inline/document transforms in array order. |
|
|
@@ -259,6 +263,7 @@ interface HeadingAnchorOptions {
|
|
|
259
263
|
}
|
|
260
264
|
|
|
261
265
|
interface CodeHighlightOptions {
|
|
266
|
+
meta?: string
|
|
262
267
|
highlightLines?: number[]
|
|
263
268
|
lineNumbers?: boolean
|
|
264
269
|
}
|