@tanstack/markdown 0.0.14 → 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 +4 -4
- package/dist/html.d.ts.map +1 -1
- package/dist/html.js +8 -2
- package/dist/inline.js +3 -2
- package/dist/octane.d.ts.map +1 -1
- package/dist/octane.js +8 -2
- package/dist/parser.js +126 -133
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +26 -17
- package/dist/types.d.ts +10 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +4 -2
- package/package.json +6 -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/README.md
CHANGED
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
|
|
21
21
|
A tiny, fast, deterministic Markdown parser and renderer for blogs and documentation.
|
|
22
22
|
|
|
23
|
-
-
|
|
24
|
-
- 6.
|
|
25
|
-
- 6.
|
|
26
|
-
- 6.
|
|
23
|
+
- 5.0 KB gzip parser
|
|
24
|
+
- 6.8 KB gzip HTML renderer
|
|
25
|
+
- 6.7 KB gzip React adapter
|
|
26
|
+
- 6.7 KB gzip Octane adapter
|
|
27
27
|
- zero runtime dependencies
|
|
28
28
|
- serializable AST
|
|
29
29
|
- safe defaults for raw HTML and executable URLs
|
package/dist/html.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../src/html.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../src/html.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAA8E,UAAU,EAAE,gBAAgB,EAAE,aAAa,EAAE,aAAa,EAAiB,MAAM,YAAY,CAAA;AAGlM,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CAGpF;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CA0ChF;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CA4BlF;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,gBAAgB,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CAE9F"}
|
package/dist/html.js
CHANGED
|
@@ -72,6 +72,8 @@ export function renderInline(node, options = {}) {
|
|
|
72
72
|
return '<br>';
|
|
73
73
|
case 'inlineHtml':
|
|
74
74
|
return options.allowHtml ? node.value : escapeHtml(node.value);
|
|
75
|
+
case 'inlineComponent':
|
|
76
|
+
return renderComponent(node, options);
|
|
75
77
|
}
|
|
76
78
|
}
|
|
77
79
|
export function renderDocument(document, options = {}) {
|
|
@@ -83,10 +85,12 @@ function renderInlines(nodes, options) {
|
|
|
83
85
|
function renderCodeBlock(node, options) {
|
|
84
86
|
const lang = node.lang ?? 'plaintext';
|
|
85
87
|
const preAttrs = `class="tm-code${options.codeLineNumbers ? ' tm-code--line-numbers' : ''}" data-lang="${escapeAttr(lang)}"`
|
|
88
|
+
+ (node.meta ? ` data-meta="${escapeAttr(node.meta)}"` : '')
|
|
86
89
|
+ (node.title ? ` data-code-title="${escapeAttr(node.title)}"` : '')
|
|
87
90
|
+ (node.file ? ` data-filename="${escapeAttr(node.file)}"` : '')
|
|
88
91
|
+ (node.framework ? ` data-framework="${escapeAttr(node.framework)}"` : '');
|
|
89
92
|
const html = options.highlighter?.(node.value, lang, {
|
|
93
|
+
...(node.meta && { meta: node.meta }),
|
|
90
94
|
...(node.highlightLines && { highlightLines: node.highlightLines }),
|
|
91
95
|
...(options.codeLineNumbers !== undefined && { lineNumbers: options.codeLineNumbers }),
|
|
92
96
|
}) ?? escapeHtml(node.value);
|
|
@@ -142,9 +146,11 @@ function renderFootnoteBackrefs(item) {
|
|
|
142
146
|
return result;
|
|
143
147
|
}
|
|
144
148
|
function renderComponent(node, options) {
|
|
145
|
-
const tag = node.tagName ?? 'md-comment-component';
|
|
149
|
+
const tag = node.tagName ?? (node.type === 'inlineComponent' ? 'span' : 'md-comment-component');
|
|
146
150
|
const attrs = renderComponentAttrs(node);
|
|
147
|
-
const children = node.
|
|
151
|
+
const children = node.type === 'inlineComponent'
|
|
152
|
+
? renderInlines(node.children, options)
|
|
153
|
+
: node.children.map(child => renderBlock(child, options)).join('\n');
|
|
148
154
|
return `<${tag}${attrs}>${children}</${tag}>`;
|
|
149
155
|
}
|
|
150
156
|
function renderTableCell(tag, cell, align, options) {
|
package/dist/inline.js
CHANGED
|
@@ -70,8 +70,9 @@ function parseInlineRaw(value, options, budget = { scans: Math.max(value.length
|
|
|
70
70
|
const links = budget.links;
|
|
71
71
|
const children = parseInlineRaw(parsed.label, image ? (options.references ? { references: options.references } : {}) : options, budget);
|
|
72
72
|
const nested = !image && budget.links !== links;
|
|
73
|
-
const
|
|
74
|
-
|
|
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)))) {
|
|
75
76
|
pushText();
|
|
76
77
|
nodes.push({
|
|
77
78
|
...(image ? { type: 'image', src: href, alt: plainText(children) } : { type: 'link', href, children }),
|
package/dist/octane.d.ts.map
CHANGED
|
@@ -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;AAG1E,OAAO,KAAK,EAAE,SAAS,
|
|
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
|
@@ -69,6 +69,8 @@ export function renderInlineOctane(node, options = {}, key) {
|
|
|
69
69
|
return options.allowHtml
|
|
70
70
|
? h(options, 'span', { key, dangerouslySetInnerHTML: { __html: node.value } })
|
|
71
71
|
: node.value;
|
|
72
|
+
case 'inlineComponent':
|
|
73
|
+
return renderComponentOctane(node, options, key);
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
function renderInlines(nodes, options) {
|
|
@@ -82,6 +84,7 @@ function renderCodeBlockOctane(node, options, key) {
|
|
|
82
84
|
? {
|
|
83
85
|
dangerouslySetInnerHTML: {
|
|
84
86
|
__html: highlighter(node.value, lang, {
|
|
87
|
+
...(node.meta && { meta: node.meta }),
|
|
85
88
|
...(node.highlightLines && { highlightLines: node.highlightLines }),
|
|
86
89
|
...(options.codeLineNumbers !== undefined && { lineNumbers: options.codeLineNumbers }),
|
|
87
90
|
}),
|
|
@@ -91,6 +94,7 @@ function renderCodeBlockOctane(node, options, key) {
|
|
|
91
94
|
const pre = h(options, 'pre', {
|
|
92
95
|
className: `tm-code${options.codeLineNumbers ? ' tm-code--line-numbers' : ''}`,
|
|
93
96
|
'data-lang': lang,
|
|
97
|
+
...(node.meta ? { 'data-meta': node.meta } : {}),
|
|
94
98
|
...(node.title ? { 'data-code-title': node.title } : {}),
|
|
95
99
|
...(node.file ? { 'data-filename': node.file } : {}),
|
|
96
100
|
...(node.framework ? { 'data-framework': node.framework } : {}),
|
|
@@ -164,7 +168,7 @@ function h(options, tag, props, ...children) {
|
|
|
164
168
|
return createElement(component, props ?? undefined, ...children);
|
|
165
169
|
}
|
|
166
170
|
function renderComponentOctane(node, options, key) {
|
|
167
|
-
const tag = node.tagName ?? 'md-comment-component';
|
|
171
|
+
const tag = node.tagName ?? (node.type === 'inlineComponent' ? 'span' : 'md-comment-component');
|
|
168
172
|
const props = {
|
|
169
173
|
...(node.properties ?? {}),
|
|
170
174
|
};
|
|
@@ -173,7 +177,9 @@ function renderComponentOctane(node, options, key) {
|
|
|
173
177
|
if (!props['data-attributes'])
|
|
174
178
|
props['data-attributes'] = JSON.stringify(node.attributes);
|
|
175
179
|
}
|
|
176
|
-
return h(options, tag, { key, ...props }, node.
|
|
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}`)));
|
|
177
183
|
}
|
|
178
184
|
function renderHeadingAnchorOctane(id, options) {
|
|
179
185
|
if (!id || !options.headingAnchors)
|
package/dist/parser.js
CHANGED
|
@@ -22,12 +22,12 @@ export function parseMarkdown(markdown, options = {}) {
|
|
|
22
22
|
const parseOptions = hasReferences || hasFootnotes
|
|
23
23
|
? {
|
|
24
24
|
...options,
|
|
25
|
-
...(hasReferences
|
|
26
|
-
...(hasFootnotes
|
|
25
|
+
...(hasReferences && { references: definitions.references }),
|
|
26
|
+
...(hasFootnotes && { footnotes: definitions.footnotes, footnoteOrder, footnoteCounts }),
|
|
27
27
|
}
|
|
28
28
|
: options;
|
|
29
29
|
const slugger = createSlugger();
|
|
30
|
-
const parser =
|
|
30
|
+
const parser = createBlockParser(lines, parseOptions, slugger);
|
|
31
31
|
const children = parser.parse();
|
|
32
32
|
if (hasFootnotes && footnoteOrder.length > 0) {
|
|
33
33
|
children.push(createFootnotesBlock(definitions.footnotes, footnoteOrder, parseOptions, slugger));
|
|
@@ -38,89 +38,86 @@ export function parseMarkdown(markdown, options = {}) {
|
|
|
38
38
|
}
|
|
39
39
|
return document;
|
|
40
40
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (this.budget.depth >= maxBlockDepth) {
|
|
56
|
-
const value = this.lines.slice(this.index).join('\n');
|
|
57
|
-
this.index = this.lines.length;
|
|
58
|
-
return value ? [{ type: 'paragraph', children: parseInline(value, this.options) }] : [];
|
|
41
|
+
function createBlockParser(inputLines, options, slugger, budget = { depth: 0 }) {
|
|
42
|
+
let cursor = 0;
|
|
43
|
+
let looseBlocks = false;
|
|
44
|
+
return {
|
|
45
|
+
parse,
|
|
46
|
+
get loose() {
|
|
47
|
+
return looseBlocks;
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
function parse() {
|
|
51
|
+
if (budget.depth >= maxBlockDepth) {
|
|
52
|
+
const value = inputLines.slice(cursor).join('\n');
|
|
53
|
+
cursor = inputLines.length;
|
|
54
|
+
return value ? [{ type: 'paragraph', children: parseInline(value, options) }] : [];
|
|
59
55
|
}
|
|
60
|
-
|
|
56
|
+
budget.depth++;
|
|
61
57
|
const nodes = [];
|
|
62
|
-
while (
|
|
63
|
-
if (isBlank(
|
|
58
|
+
while (cursor < inputLines.length) {
|
|
59
|
+
if (isBlank(current())) {
|
|
64
60
|
if (nodes.length)
|
|
65
|
-
|
|
66
|
-
|
|
61
|
+
looseBlocks = true;
|
|
62
|
+
cursor++;
|
|
67
63
|
continue;
|
|
68
64
|
}
|
|
69
|
-
const extensionNode =
|
|
65
|
+
const extensionNode = parseExtensionBlock();
|
|
70
66
|
if (extensionNode) {
|
|
71
67
|
nodes.push(extensionNode);
|
|
72
68
|
continue;
|
|
73
69
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
70
|
+
// Letter-led text cannot open a fence, heading, rule, quote, or list.
|
|
71
|
+
// It can still be a table header, so table detection remains below.
|
|
72
|
+
const node = (/^[a-z]/i.test(current()) ? undefined : (parseFence() ??
|
|
73
|
+
parseHeading() ??
|
|
74
|
+
parseThematicBreak() ??
|
|
75
|
+
parseBlockquote() ??
|
|
76
|
+
parseList())) ??
|
|
77
|
+
parseTable() ??
|
|
78
|
+
parseHtmlBlock() ??
|
|
79
|
+
parseParagraph();
|
|
82
80
|
nodes.push(node);
|
|
83
81
|
}
|
|
84
|
-
|
|
82
|
+
budget.depth--;
|
|
85
83
|
return nodes;
|
|
86
84
|
}
|
|
87
|
-
parseExtensionBlock() {
|
|
88
|
-
for (const extension of
|
|
85
|
+
function parseExtensionBlock() {
|
|
86
|
+
for (const extension of options.extensions ?? []) {
|
|
89
87
|
let consumed = 0;
|
|
90
88
|
const node = extension.parseBlock?.({
|
|
91
|
-
lines:
|
|
92
|
-
index:
|
|
93
|
-
options:
|
|
94
|
-
parseInline: value => parseInline(value,
|
|
95
|
-
parseBlocks: value =>
|
|
89
|
+
lines: inputLines,
|
|
90
|
+
index: cursor,
|
|
91
|
+
options: options,
|
|
92
|
+
parseInline: value => parseInline(value, options),
|
|
93
|
+
parseBlocks: value => createBlockParser(normalizeInput(value).split('\n'), options, slugger, budget).parse(),
|
|
96
94
|
consume: lines => {
|
|
97
95
|
consumed = lines;
|
|
98
96
|
},
|
|
99
97
|
});
|
|
100
98
|
if (node) {
|
|
101
|
-
|
|
99
|
+
cursor += Math.max(consumed, 1);
|
|
102
100
|
return node;
|
|
103
101
|
}
|
|
104
102
|
}
|
|
105
103
|
return undefined;
|
|
106
104
|
}
|
|
107
|
-
parseFence() {
|
|
108
|
-
const match =
|
|
105
|
+
function parseFence() {
|
|
106
|
+
const match = current().match(/^( {0,3})(`{3,}|~{3,})(.*)$/);
|
|
109
107
|
if (!match)
|
|
110
108
|
return undefined;
|
|
111
109
|
const fence = match[2];
|
|
112
110
|
const info = match[3].trim();
|
|
113
111
|
const code = [];
|
|
114
|
-
|
|
115
|
-
while (
|
|
116
|
-
const line =
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
this.index++;
|
|
112
|
+
cursor++;
|
|
113
|
+
while (cursor < inputLines.length) {
|
|
114
|
+
const line = current();
|
|
115
|
+
if (line.includes(fence) && /^ {0,3}(?:`+|~+)\s*$/.test(line)) {
|
|
116
|
+
cursor++;
|
|
120
117
|
break;
|
|
121
118
|
}
|
|
122
119
|
code.push(stripIndent(line, match[1].length));
|
|
123
|
-
|
|
120
|
+
cursor++;
|
|
124
121
|
}
|
|
125
122
|
return {
|
|
126
123
|
type: 'code',
|
|
@@ -128,54 +125,54 @@ class BlockParser {
|
|
|
128
125
|
...parseCodeInfo(info),
|
|
129
126
|
};
|
|
130
127
|
}
|
|
131
|
-
parseHeading() {
|
|
132
|
-
const match =
|
|
128
|
+
function parseHeading() {
|
|
129
|
+
const match = current().match(/^ {0,3}(#{1,6})(?:[ \t]+(.*)|[ \t]*)$/);
|
|
133
130
|
if (!match)
|
|
134
131
|
return undefined;
|
|
135
132
|
const depth = match[1].length;
|
|
136
133
|
const rawValue = match[2] ?? '';
|
|
137
134
|
const value = (/^#+[ \t]*$/.test(rawValue) ? '' : rawValue.replace(/[ \t]+#+[ \t]*$/, '')).trim();
|
|
138
|
-
const children = parseInline(value,
|
|
139
|
-
const id =
|
|
140
|
-
|
|
135
|
+
const children = parseInline(value, options);
|
|
136
|
+
const id = createHeadingId(children);
|
|
137
|
+
cursor++;
|
|
141
138
|
return id ? { type: 'heading', depth, id, children } : { type: 'heading', depth, children };
|
|
142
139
|
}
|
|
143
|
-
parseThematicBreak() {
|
|
144
|
-
if (!/^ {0,3}([-*_])(?:\s*\1){2,}\s*$/.test(
|
|
140
|
+
function parseThematicBreak() {
|
|
141
|
+
if (!/^ {0,3}([-*_])(?:\s*\1){2,}\s*$/.test(current()))
|
|
145
142
|
return undefined;
|
|
146
|
-
|
|
143
|
+
cursor++;
|
|
147
144
|
return { type: 'thematicBreak' };
|
|
148
145
|
}
|
|
149
|
-
parseBlockquote() {
|
|
150
|
-
if (!/^ {0,3}>\s?/.test(
|
|
146
|
+
function parseBlockquote() {
|
|
147
|
+
if (!/^ {0,3}>\s?/.test(current()))
|
|
151
148
|
return undefined;
|
|
152
149
|
const quoted = [];
|
|
153
|
-
while (
|
|
154
|
-
const line =
|
|
150
|
+
while (cursor < inputLines.length) {
|
|
151
|
+
const line = current();
|
|
155
152
|
const match = line.match(/^ {0,3}>\s?(.*)$/);
|
|
156
153
|
if (!match) {
|
|
157
154
|
if (!isBlank(line))
|
|
158
155
|
break;
|
|
159
|
-
|
|
156
|
+
looseBlocks = true;
|
|
160
157
|
}
|
|
161
158
|
quoted.push(match?.[1] ?? '');
|
|
162
|
-
|
|
159
|
+
cursor++;
|
|
163
160
|
}
|
|
164
161
|
return {
|
|
165
162
|
type: 'blockquote',
|
|
166
|
-
children:
|
|
163
|
+
children: createBlockParser(quoted, options, slugger, budget).parse(),
|
|
167
164
|
};
|
|
168
165
|
}
|
|
169
|
-
parseList() {
|
|
170
|
-
const first = listMarker(
|
|
166
|
+
function parseList() {
|
|
167
|
+
const first = listMarker(current());
|
|
171
168
|
if (!first)
|
|
172
169
|
return undefined;
|
|
173
170
|
const items = [];
|
|
174
171
|
const ordered = first.ordered;
|
|
175
172
|
const baseIndent = first.indent;
|
|
176
173
|
let loose = false;
|
|
177
|
-
while (
|
|
178
|
-
const marker = listMarker(
|
|
174
|
+
while (cursor < inputLines.length) {
|
|
175
|
+
const marker = listMarker(current());
|
|
179
176
|
if (!marker || marker.marker !== first.marker || marker.indent !== baseIndent)
|
|
180
177
|
break;
|
|
181
178
|
let firstLine = marker.content;
|
|
@@ -184,17 +181,17 @@ class BlockParser {
|
|
|
184
181
|
if (task)
|
|
185
182
|
firstLine = task[2];
|
|
186
183
|
const itemLines = [firstLine];
|
|
187
|
-
|
|
188
|
-
while (
|
|
189
|
-
const line =
|
|
184
|
+
cursor++;
|
|
185
|
+
while (cursor < inputLines.length) {
|
|
186
|
+
const line = current();
|
|
190
187
|
const nextMarker = listMarker(line);
|
|
191
188
|
if (nextMarker && nextMarker.indent === baseIndent)
|
|
192
189
|
break;
|
|
193
190
|
if (isBlank(line)) {
|
|
194
|
-
let nextIndex =
|
|
195
|
-
while (nextIndex <
|
|
191
|
+
let nextIndex = cursor;
|
|
192
|
+
while (nextIndex < inputLines.length && isBlank(inputLines[nextIndex]))
|
|
196
193
|
nextIndex++;
|
|
197
|
-
const following =
|
|
194
|
+
const following = inputLines[nextIndex];
|
|
198
195
|
if (following === undefined)
|
|
199
196
|
break;
|
|
200
197
|
const followingMarker = listMarker(following);
|
|
@@ -202,26 +199,26 @@ class BlockParser {
|
|
|
202
199
|
if (followingMarker.marker !== first.marker)
|
|
203
200
|
break;
|
|
204
201
|
loose = true;
|
|
205
|
-
|
|
202
|
+
cursor = nextIndex;
|
|
206
203
|
break;
|
|
207
204
|
}
|
|
208
205
|
if (leadingSpaces(following) < marker.contentIndent)
|
|
209
206
|
break;
|
|
210
|
-
while (
|
|
211
|
-
itemLines.push(stripIndent(
|
|
207
|
+
while (cursor < nextIndex)
|
|
208
|
+
itemLines.push(stripIndent(inputLines[cursor++], marker.contentIndent));
|
|
212
209
|
continue;
|
|
213
210
|
}
|
|
214
211
|
if (leadingSpaces(line) >= marker.contentIndent) {
|
|
215
212
|
itemLines.push(stripIndent(line, marker.contentIndent));
|
|
216
|
-
|
|
213
|
+
cursor++;
|
|
217
214
|
continue;
|
|
218
215
|
}
|
|
219
|
-
if (isBlockStart(line,
|
|
216
|
+
if (isBlockStart(line, next()))
|
|
220
217
|
break;
|
|
221
218
|
itemLines.push(line.trimStart());
|
|
222
|
-
|
|
219
|
+
cursor++;
|
|
223
220
|
}
|
|
224
|
-
const parser =
|
|
221
|
+
const parser = createBlockParser(itemLines, options, slugger, budget);
|
|
225
222
|
const children = parser.parse();
|
|
226
223
|
loose ||= parser.loose;
|
|
227
224
|
const item = { type: 'listItem', children };
|
|
@@ -237,100 +234,96 @@ class BlockParser {
|
|
|
237
234
|
items,
|
|
238
235
|
};
|
|
239
236
|
}
|
|
240
|
-
parseTable() {
|
|
241
|
-
const header =
|
|
242
|
-
const delimiter =
|
|
237
|
+
function parseTable() {
|
|
238
|
+
const header = current();
|
|
239
|
+
const delimiter = next();
|
|
243
240
|
if (!delimiter || !looksLikeTableHeader(header, delimiter))
|
|
244
241
|
return undefined;
|
|
245
242
|
const headerCells = splitTableRow(header);
|
|
246
243
|
const align = splitTableRow(delimiter).map(parseAlign);
|
|
247
244
|
const columns = headerCells.length;
|
|
248
245
|
const rows = [];
|
|
249
|
-
|
|
250
|
-
while (
|
|
251
|
-
const line =
|
|
252
|
-
if (isBlank(line) || isBlockStart(line,
|
|
246
|
+
cursor += 2;
|
|
247
|
+
while (cursor < inputLines.length) {
|
|
248
|
+
const line = current();
|
|
249
|
+
if (isBlank(line) || isBlockStart(line, next()))
|
|
253
250
|
break;
|
|
254
251
|
const values = splitTableRow(line);
|
|
255
|
-
rows.push(Array.from({ length: columns }, (_, index) => cell(values[index] ?? '',
|
|
256
|
-
|
|
252
|
+
rows.push(Array.from({ length: columns }, (_, index) => cell(values[index] ?? '', options)));
|
|
253
|
+
cursor++;
|
|
257
254
|
}
|
|
258
255
|
return {
|
|
259
256
|
type: 'table',
|
|
260
257
|
align,
|
|
261
|
-
header: headerCells.map(value => cell(value,
|
|
258
|
+
header: headerCells.map(value => cell(value, options)),
|
|
262
259
|
rows,
|
|
263
260
|
};
|
|
264
261
|
}
|
|
265
|
-
parseHtmlBlock() {
|
|
266
|
-
if (!
|
|
262
|
+
function parseHtmlBlock() {
|
|
263
|
+
if (!options.allowHtml || !/^ {0,3}<([A-Za-z][\w:-]*|!--|\/[A-Za-z])/.test(current()))
|
|
267
264
|
return undefined;
|
|
268
265
|
const html = [];
|
|
269
|
-
if (/^ {0,3}<!--/.test(
|
|
270
|
-
while (
|
|
271
|
-
const line =
|
|
266
|
+
if (/^ {0,3}<!--/.test(current())) {
|
|
267
|
+
while (cursor < inputLines.length) {
|
|
268
|
+
const line = current();
|
|
272
269
|
html.push(line);
|
|
273
|
-
|
|
270
|
+
cursor++;
|
|
274
271
|
if (line.includes('-->'))
|
|
275
272
|
break;
|
|
276
273
|
}
|
|
277
274
|
return { type: 'html', value: html.join('\n') };
|
|
278
275
|
}
|
|
279
|
-
while (
|
|
280
|
-
html.push(
|
|
281
|
-
|
|
276
|
+
while (cursor < inputLines.length && !isBlank(current())) {
|
|
277
|
+
html.push(current());
|
|
278
|
+
cursor++;
|
|
282
279
|
}
|
|
283
280
|
return { type: 'html', value: html.join('\n') };
|
|
284
281
|
}
|
|
285
|
-
parseParagraph() {
|
|
282
|
+
function parseParagraph() {
|
|
286
283
|
const lines = [];
|
|
287
|
-
while (
|
|
288
|
-
const line =
|
|
284
|
+
while (cursor < inputLines.length) {
|
|
285
|
+
const line = current();
|
|
289
286
|
if (isBlank(line))
|
|
290
287
|
break;
|
|
291
|
-
if (lines.length > 0 && isBlockStart(line,
|
|
288
|
+
if (lines.length > 0 && isBlockStart(line, next()))
|
|
292
289
|
break;
|
|
293
290
|
lines.push(line.trim());
|
|
294
|
-
|
|
291
|
+
cursor++;
|
|
295
292
|
}
|
|
296
293
|
return {
|
|
297
294
|
type: 'paragraph',
|
|
298
|
-
children: parseInline(lines.join('\n'),
|
|
295
|
+
children: parseInline(lines.join('\n'), options),
|
|
299
296
|
};
|
|
300
297
|
}
|
|
301
|
-
createHeadingId(children) {
|
|
302
|
-
if (
|
|
298
|
+
function createHeadingId(children) {
|
|
299
|
+
if (options.headingIds === false)
|
|
303
300
|
return undefined;
|
|
304
301
|
const text = plainText(children);
|
|
305
|
-
if (typeof
|
|
306
|
-
return
|
|
307
|
-
return
|
|
302
|
+
if (typeof options.headingIds === 'function')
|
|
303
|
+
return options.headingIds(text, cursor);
|
|
304
|
+
return slugger(text);
|
|
308
305
|
}
|
|
309
|
-
current() {
|
|
310
|
-
return
|
|
306
|
+
function current() {
|
|
307
|
+
return inputLines[cursor] ?? '';
|
|
311
308
|
}
|
|
312
|
-
next() {
|
|
313
|
-
return
|
|
309
|
+
function next() {
|
|
310
|
+
return inputLines[cursor + 1];
|
|
314
311
|
}
|
|
315
312
|
}
|
|
316
313
|
function parseCodeInfo(info) {
|
|
317
|
-
if (!info)
|
|
318
|
-
return {};
|
|
319
314
|
const langMatch = info.match(/^([A-Za-z0-9_+.#-]+)/);
|
|
320
315
|
const lang = langMatch?.[1];
|
|
321
316
|
const meta = lang ? info.slice(lang.length).trim() : info;
|
|
322
|
-
const
|
|
323
|
-
const
|
|
317
|
+
const title = meta.match(/(?:^|\s)(?:title|file)=(?:"([^"]+)"|'([^']+)'|([^\s}]+))/)?.slice(1).find(Boolean);
|
|
318
|
+
const framework = meta.match(/(?:^|\s)framework=(?:"([^"]+)"|'([^']+)'|([^\s}]+))/)?.slice(1).find(Boolean);
|
|
324
319
|
const rangeMatch = meta.match(/\{([^}]+)\}|(?:^|\s)lines=([^\s]+)/);
|
|
325
320
|
const highlightLines = rangeMatch ? parseLineRanges(rangeMatch[1] ?? rangeMatch[2]) : [];
|
|
326
|
-
const title = titleMatch ? titleMatch[1] ?? titleMatch[2] ?? titleMatch[3] : undefined;
|
|
327
|
-
const framework = frameworkMatch ? frameworkMatch[1] ?? frameworkMatch[2] ?? frameworkMatch[3] : undefined;
|
|
328
321
|
return {
|
|
329
|
-
...(lang
|
|
330
|
-
...(meta
|
|
331
|
-
...(title
|
|
332
|
-
...(framework
|
|
333
|
-
...(highlightLines.length
|
|
322
|
+
...(lang && { lang }),
|
|
323
|
+
...(meta && { meta }),
|
|
324
|
+
...(title && { title, file: title }),
|
|
325
|
+
...(framework && { framework: framework.toLowerCase() }),
|
|
326
|
+
...(highlightLines.length && { highlightLines }),
|
|
334
327
|
};
|
|
335
328
|
}
|
|
336
329
|
function extractDefinitions(lines) {
|
|
@@ -342,7 +335,7 @@ function extractDefinitions(lines) {
|
|
|
342
335
|
let index = 0;
|
|
343
336
|
while (index < lines.length) {
|
|
344
337
|
const line = lines[index];
|
|
345
|
-
const fence = line.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
|
|
338
|
+
const fence = (!activeFence || line.includes(activeFence)) && line.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
|
|
346
339
|
if (fence) {
|
|
347
340
|
if (!activeFence)
|
|
348
341
|
activeFence = fence[1];
|
|
@@ -391,7 +384,7 @@ function createFootnotesBlock(footnotes, footnoteOrder, options, slugger) {
|
|
|
391
384
|
items.push({
|
|
392
385
|
id: definition.id ?? footnoteId(definition.label),
|
|
393
386
|
number: index + 1,
|
|
394
|
-
children:
|
|
387
|
+
children: createBlockParser(normalizeInput(definition.content).split('\n'), options, slugger).parse(),
|
|
395
388
|
});
|
|
396
389
|
}
|
|
397
390
|
for (const item of items) {
|
|
@@ -425,7 +418,7 @@ function listMarker(line) {
|
|
|
425
418
|
const ordered = /\d/.test(marker[0]);
|
|
426
419
|
return {
|
|
427
420
|
ordered,
|
|
428
|
-
...(ordered
|
|
421
|
+
...(ordered && { number: Number.parseInt(marker, 10) }),
|
|
429
422
|
indent: match[1].length,
|
|
430
423
|
marker: ordered ? marker.at(-1) : marker,
|
|
431
424
|
contentIndent: match[1].length + marker.length + (match[3]?.length ?? 1),
|
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;AAGlG,OAAO,KAAK,EAAE,SAAS,
|
|
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,EAAwD,UAAU,EAAE,aAAa,EAAE,aAAa,EAAiB,MAAM,YAAY,CAAA;AAE1J,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,CA0C/G"}
|
package/dist/react.js
CHANGED
|
@@ -11,14 +11,14 @@ export function renderMarkdownReact(input, options = {}) {
|
|
|
11
11
|
export function renderBlockReact(node, options = {}, key) {
|
|
12
12
|
switch (node.type) {
|
|
13
13
|
case 'heading':
|
|
14
|
-
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));
|
|
15
15
|
case 'paragraph':
|
|
16
16
|
return h(options, 'p', { key }, renderInlines(node.children, options));
|
|
17
17
|
case 'code':
|
|
18
18
|
return renderCodeBlockReact(node, options, key);
|
|
19
19
|
case 'list': {
|
|
20
20
|
const tag = node.ordered ? 'ol' : 'ul';
|
|
21
|
-
return h(options, tag, { key, ...(node.ordered && node.start !== undefined && 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}`))));
|
|
22
22
|
}
|
|
23
23
|
case 'blockquote':
|
|
24
24
|
return h(options, 'blockquote', { key }, node.children.map((child, index) => renderBlockReact(child, options, `${key}:${index}`)));
|
|
@@ -60,15 +60,17 @@ export function renderInlineReact(node, options = {}, key) {
|
|
|
60
60
|
href: `#user-content-fn-${node.id}`,
|
|
61
61
|
}, node.number));
|
|
62
62
|
case 'link':
|
|
63
|
-
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));
|
|
64
64
|
case 'image':
|
|
65
|
-
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 }) });
|
|
66
66
|
case 'break':
|
|
67
67
|
return h(options, 'br', { key });
|
|
68
68
|
case 'inlineHtml':
|
|
69
69
|
return options.allowHtml
|
|
70
70
|
? h(options, 'span', { key, dangerouslySetInnerHTML: { __html: node.value } })
|
|
71
71
|
: node.value;
|
|
72
|
+
case 'inlineComponent':
|
|
73
|
+
return renderComponentReact(node, options, key);
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
function renderInlines(nodes, options) {
|
|
@@ -77,14 +79,17 @@ function renderInlines(nodes, options) {
|
|
|
77
79
|
function renderCodeBlockReact(node, options, key) {
|
|
78
80
|
const lang = node.lang ?? 'plaintext';
|
|
79
81
|
const highlighter = options.highlighter;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
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;
|
|
84
88
|
const highlighted = highlighter
|
|
85
89
|
? {
|
|
86
90
|
dangerouslySetInnerHTML: {
|
|
87
91
|
__html: highlighter(node.value, lang, {
|
|
92
|
+
...(node.meta && { meta: node.meta }),
|
|
88
93
|
...(node.highlightLines && { highlightLines: node.highlightLines }),
|
|
89
94
|
...(options.codeLineNumbers !== undefined && { lineNumbers: options.codeLineNumbers }),
|
|
90
95
|
}),
|
|
@@ -92,14 +97,16 @@ function renderCodeBlockReact(node, options, key) {
|
|
|
92
97
|
}
|
|
93
98
|
: undefined;
|
|
94
99
|
const pre = h(options, 'pre', {
|
|
100
|
+
key,
|
|
95
101
|
className: `tm-code${options.codeLineNumbers ? ' tm-code--line-numbers' : ''}`,
|
|
96
102
|
'data-lang': lang,
|
|
97
|
-
...(node.
|
|
98
|
-
...(node.
|
|
99
|
-
...(node.
|
|
100
|
-
|
|
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));
|
|
101
108
|
if (!node.title)
|
|
102
|
-
return
|
|
109
|
+
return pre;
|
|
103
110
|
return h(options, 'figure', { key, className: 'tm-code-frame', 'data-lang': lang }, h(options, 'figcaption', null, node.title), pre);
|
|
104
111
|
}
|
|
105
112
|
function renderListItemChildrenReact(children, checked, loose, options, key) {
|
|
@@ -129,7 +136,7 @@ function renderListItemChildrenReact(children, checked, loose, options, key) {
|
|
|
129
136
|
return result;
|
|
130
137
|
}
|
|
131
138
|
function renderTableCellReact(tag, cell, align, options, key) {
|
|
132
|
-
return h(options, tag, { key, ...(align
|
|
139
|
+
return h(options, tag, { key, ...(align && { style: { textAlign: align } }) }, renderInlines(cell.children, options));
|
|
133
140
|
}
|
|
134
141
|
function renderFootnotesReact(items, options, key) {
|
|
135
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)))));
|
|
@@ -167,16 +174,18 @@ function h(options, tag, props, ...children) {
|
|
|
167
174
|
return createElement(component, props, ...children);
|
|
168
175
|
}
|
|
169
176
|
function renderComponentReact(node, options, key) {
|
|
170
|
-
const tag = node.tagName ?? 'md-comment-component';
|
|
177
|
+
const tag = node.tagName ?? (node.type === 'inlineComponent' ? 'span' : 'md-comment-component');
|
|
171
178
|
const props = {
|
|
172
|
-
...
|
|
179
|
+
...node.properties,
|
|
173
180
|
};
|
|
174
181
|
if (!node.tagName) {
|
|
175
182
|
props['data-component'] = node.name;
|
|
176
183
|
if (!props['data-attributes'])
|
|
177
184
|
props['data-attributes'] = JSON.stringify(node.attributes);
|
|
178
185
|
}
|
|
179
|
-
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}`)));
|
|
180
189
|
}
|
|
181
190
|
function renderHeadingAnchorReact(id, options) {
|
|
182
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.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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;
|
|
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);
|
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",
|
|
@@ -100,11 +100,14 @@
|
|
|
100
100
|
"marked": "^18.0.5",
|
|
101
101
|
"micromark": "^4.0.2",
|
|
102
102
|
"octane": "0.1.12",
|
|
103
|
+
"playwright": "1.63.0",
|
|
103
104
|
"react": "^19.0.0",
|
|
104
105
|
"react-dom": "^19.0.0",
|
|
105
106
|
"rehype-stringify": "^10.0.1",
|
|
106
107
|
"remark-parse": "^11.0.0",
|
|
107
108
|
"remark-rehype": "^11.1.2",
|
|
109
|
+
"streamdown": "2.6.0",
|
|
110
|
+
"streaming-markdown": "0.2.15",
|
|
108
111
|
"tsx": "^4.20.0",
|
|
109
112
|
"typescript": "^5.8.0",
|
|
110
113
|
"unified": "^11.0.5",
|
|
@@ -117,6 +120,7 @@
|
|
|
117
120
|
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
118
121
|
"typecheck": "tsc --noEmit",
|
|
119
122
|
"test": "vitest run",
|
|
123
|
+
"test:streaming-browser": "tsx scripts/verify-streaming-browser.ts",
|
|
120
124
|
"test:corpus": "vitest run tests/corpus.test.tsx",
|
|
121
125
|
"corpus:audit": "tsx scripts/audit-corpus.ts",
|
|
122
126
|
"corpus:audit:tanstack": "tsx scripts/audit-tanstack-corpus.ts",
|
|
@@ -132,6 +136,7 @@
|
|
|
132
136
|
"release:publish": "pnpm run verify && changeset publish",
|
|
133
137
|
"conformance": "tsx scripts/conformance.ts",
|
|
134
138
|
"bench": "tsx scripts/bench.ts",
|
|
139
|
+
"bench:streaming": "tsx scripts/bench-streaming-browser.ts",
|
|
135
140
|
"size": "tsx scripts/measure-size.ts",
|
|
136
141
|
"research": "pnpm run conformance && pnpm run size && pnpm run bench",
|
|
137
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
|
}
|