@tanstack/markdown 0.0.8 → 0.0.10

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.
@@ -0,0 +1,436 @@
1
+ ---
2
+ name: 'docs-features'
3
+ description: >
4
+ Build documentation metadata with docsMarkdownExtensions, GitHub-style
5
+ callouts, heading collection, comment components, heading/file/package-
6
+ manager/bundler tabs, framework panels, and code-fence metadata. Load when
7
+ authoring or consuming TanStack-style docs syntax and custom-element data
8
+ contracts.
9
+ metadata:
10
+ type: core
11
+ library: '@tanstack/markdown'
12
+ library_version: '0.0.10'
13
+ requires:
14
+ - 'render-markdown'
15
+ sources:
16
+ - 'TanStack/markdown:docs/guides/docs-preset.md'
17
+ - 'TanStack/markdown:docs/reference/extensions.md'
18
+ - 'TanStack/markdown:src/extensions/docs.ts'
19
+ - 'TanStack/markdown:src/extensions/tabs.ts'
20
+ - 'TanStack/markdown:src/extensions/framework.ts'
21
+ - 'TanStack/markdown:src/extensions/headings.ts'
22
+ - 'TanStack/markdown:src/extensions/comment-components.ts'
23
+ ---
24
+
25
+ This skill builds on [render-markdown](../render-markdown/SKILL.md). Read it first for parser options, AST reuse, rendering, and core trust boundaries.
26
+
27
+ # Docs Features
28
+
29
+ ## Setup
30
+
31
+ Create one extension array and use it for parsing and rendering:
32
+
33
+ ```ts
34
+ import { renderHtml } from '@tanstack/markdown/html'
35
+ import { parseMarkdown } from '@tanstack/markdown/parser'
36
+ import { docsMarkdownExtensions } from '@tanstack/markdown/extensions/docs'
37
+
38
+ const source = `# Deployment
39
+
40
+ > [!TIP] Parse once
41
+ > Reuse the document for every renderer.
42
+
43
+ ## Install
44
+
45
+ Run the package-manager command for your application.`
46
+
47
+ const extensions = docsMarkdownExtensions()
48
+ const document = parseMarkdown(source, { extensions })
49
+
50
+ export const headings = document.headings
51
+ export const html = renderHtml(document, {
52
+ extensions,
53
+ headingAnchors: true,
54
+ })
55
+ ```
56
+
57
+ The preset composes callouts, transformed comment components, and heading collection. It emits metadata and custom elements; it does not install documentation UI behavior.
58
+
59
+ ## Core Patterns
60
+
61
+ ### Collect headings without selector headings
62
+
63
+ ```ts
64
+ import { parseMarkdown } from '@tanstack/markdown/parser'
65
+ import { docsMarkdownExtensions } from '@tanstack/markdown/extensions/docs'
66
+
67
+ const source = `# Guide
68
+
69
+ <!-- ::start:tabs -->
70
+
71
+ ## React
72
+
73
+ React setup
74
+
75
+ ## Solid
76
+
77
+ Solid setup
78
+
79
+ <!-- ::end:tabs -->
80
+
81
+ ## API`
82
+
83
+ const extensions = docsMarkdownExtensions()
84
+
85
+ export const document = parseMarkdown(source, { extensions })
86
+ export const headings = document.headings
87
+ ```
88
+
89
+ Heading collection defaults to skipping every heading inside a component named `tabs`. Pass `docsMarkdownExtensions({ collectHeadings: false })` to disable collection or an options object with `skipComponentNames` to replace the default skip set.
90
+
91
+ ### Author each tab input shape
92
+
93
+ Heading tabs split at the shallowest heading:
94
+
95
+ ```md
96
+ <!-- ::start:tabs -->
97
+
98
+ ## React
99
+
100
+ React setup
101
+
102
+ ## Solid
103
+
104
+ Solid setup
105
+
106
+ <!-- ::end:tabs -->
107
+ ```
108
+
109
+ File tabs select fenced code blocks and use `title=` or `file=` as labels:
110
+
111
+ ````md
112
+ <!-- ::start:tabs variant="files" -->
113
+
114
+ ```tsx file="app.tsx"
115
+ export function App() {
116
+ return <main>Docs</main>
117
+ }
118
+ ```
119
+
120
+ ```css file="app.css"
121
+ main {
122
+ display: block;
123
+ }
124
+ ```
125
+
126
+ <!-- ::end:tabs -->
127
+ ````
128
+
129
+ Package-manager tabs consume `framework: package...` lines and remove their source children after creating metadata:
130
+
131
+ ```md
132
+ <!-- ::start:tabs variant="package-manager" mode="dev-install" -->
133
+
134
+ react: @tanstack/react-query @tanstack/react-router
135
+ solid: @tanstack/solid-query @tanstack/solid-router
136
+
137
+ <!-- ::end:tabs -->
138
+ ```
139
+
140
+ Bundler tabs accept only Vite and Rsbuild heading sections:
141
+
142
+ ````md
143
+ <!-- ::start:tabs variant="bundler" -->
144
+
145
+ ## Vite
146
+
147
+ ```ts
148
+ export default { plugins: [] }
149
+ ```
150
+
151
+ ## Rsbuild
152
+
153
+ ```ts
154
+ export default { plugins: [] }
155
+ ```
156
+
157
+ <!-- ::end:tabs -->
158
+ ````
159
+
160
+ Every transform returns the original component unchanged when its required structure is absent. See [docs metadata contracts](references/docs-metadata.md) for exact properties and fallback rules.
161
+
162
+ ### Build framework-specific panels
163
+
164
+ ```md
165
+ <!-- ::start:framework -->
166
+
167
+ # React
168
+
169
+ ## Install
170
+
171
+ ```tsx title="react.tsx"
172
+ export const framework = 'react'
173
+ ```
174
+
175
+ # Solid
176
+
177
+ ## Install
178
+
179
+ ```tsx title="solid.tsx"
180
+ export const framework = 'solid'
181
+ ```
182
+
183
+ <!-- ::end:framework -->
184
+ ```
185
+
186
+ Framework blocks require level-one selector headings. They emit `md-framework-panel` children, lowercase framework names, label nested headings, and expose code-block metadata by framework.
187
+
188
+ ### Read component metadata before rendering
189
+
190
+ ```ts
191
+ import type { ComponentNode } from '@tanstack/markdown'
192
+ import { parseMarkdown } from '@tanstack/markdown/parser'
193
+ import { docsMarkdownExtensions } from '@tanstack/markdown/extensions/docs'
194
+
195
+ interface TabDescriptor {
196
+ slug: string
197
+ name: string
198
+ }
199
+
200
+ function isTabsNode(node: unknown): node is ComponentNode {
201
+ return (
202
+ typeof node === 'object' &&
203
+ node !== null &&
204
+ 'type' in node &&
205
+ node.type === 'component' &&
206
+ 'name' in node &&
207
+ node.name === 'tabs'
208
+ )
209
+ }
210
+
211
+ const source = `<!-- ::start:tabs -->
212
+
213
+ ## React
214
+
215
+ React content
216
+
217
+ ## Solid
218
+
219
+ Solid content
220
+
221
+ <!-- ::end:tabs -->`
222
+
223
+ const document = parseMarkdown(source, {
224
+ extensions: docsMarkdownExtensions(),
225
+ })
226
+ const tabsNode = document.children.find(isTabsNode)
227
+ const metadata = tabsNode?.properties?.['data-attributes']
228
+
229
+ export const tabs: TabDescriptor[] = metadata
230
+ ? (JSON.parse(metadata) as { tabs: TabDescriptor[] }).tabs
231
+ : []
232
+ ```
233
+
234
+ `ComponentNode.properties` holds strings. JSON-bearing values are serialized into escaped HTML attributes by the HTML renderer and must be parsed by the consuming application.
235
+
236
+ ## Common Mistakes
237
+
238
+ ### HIGH Assuming transformed tabs are interactive
239
+
240
+ Wrong:
241
+
242
+ ```ts
243
+ import { renderHtml } from '@tanstack/markdown/html'
244
+ import { docsMarkdownExtensions } from '@tanstack/markdown/extensions/docs'
245
+
246
+ const source = `<!-- ::start:tabs -->
247
+
248
+ ## React
249
+
250
+ React content
251
+
252
+ <!-- ::end:tabs -->`
253
+
254
+ export const interactiveTabs = renderHtml(source, {
255
+ extensions: docsMarkdownExtensions(),
256
+ })
257
+ ```
258
+
259
+ Correct:
260
+
261
+ ```ts
262
+ import type { ComponentNode } from '@tanstack/markdown'
263
+ import { parseMarkdown } from '@tanstack/markdown/parser'
264
+ import { docsMarkdownExtensions } from '@tanstack/markdown/extensions/docs'
265
+
266
+ const source = `<!-- ::start:tabs -->
267
+
268
+ ## React
269
+
270
+ React content
271
+
272
+ <!-- ::end:tabs -->`
273
+
274
+ const document = parseMarkdown(source, {
275
+ extensions: docsMarkdownExtensions(),
276
+ })
277
+ const tabsNode = document.children.find(
278
+ (node): node is ComponentNode =>
279
+ node.type === 'component' && node.name === 'tabs',
280
+ )
281
+
282
+ export const tabPanels =
283
+ tabsNode?.children.filter(
284
+ (node): node is ComponentNode =>
285
+ node.type === 'component' && node.tagName === 'md-tab-panel',
286
+ ) ?? []
287
+ ```
288
+
289
+ The transform supplies a model and custom-element names; the site must bind those values to its own state, controls, and renderer components.
290
+
291
+ Source: `docs/guides/docs-preset.md`
292
+
293
+ ### HIGH Leaving a component block unmatched
294
+
295
+ Wrong:
296
+
297
+ ```ts
298
+ import { parseMarkdown } from '@tanstack/markdown/parser'
299
+ import { docsMarkdownExtensions } from '@tanstack/markdown/extensions/docs'
300
+
301
+ const source = `<!-- ::start:tabs -->
302
+
303
+ ## React
304
+
305
+ React content`
306
+
307
+ export const document = parseMarkdown(source, {
308
+ extensions: docsMarkdownExtensions(),
309
+ })
310
+ ```
311
+
312
+ Correct:
313
+
314
+ ```ts
315
+ import { parseMarkdown } from '@tanstack/markdown/parser'
316
+ import { docsMarkdownExtensions } from '@tanstack/markdown/extensions/docs'
317
+
318
+ const source = `<!-- ::start:tabs -->
319
+
320
+ ## React
321
+
322
+ React content
323
+
324
+ <!-- ::end:tabs -->`
325
+
326
+ export const document = parseMarkdown(source, {
327
+ extensions: docsMarkdownExtensions(),
328
+ })
329
+ ```
330
+
331
+ An unmatched start is consumed as an empty component, while the following body remains ordinary Markdown.
332
+
333
+ Source: `src/extensions/comment-components.ts:41-63`
334
+
335
+ ### MEDIUM Passing the wrong tab content shape
336
+
337
+ Wrong:
338
+
339
+ ```ts
340
+ import { parseMarkdown } from '@tanstack/markdown/parser'
341
+ import { docsMarkdownExtensions } from '@tanstack/markdown/extensions/docs'
342
+
343
+ const source = `<!-- ::start:tabs variant="files" -->
344
+
345
+ This variant does not turn prose into a file.
346
+
347
+ <!-- ::end:tabs -->`
348
+
349
+ export const document = parseMarkdown(source, {
350
+ extensions: docsMarkdownExtensions(),
351
+ })
352
+ ```
353
+
354
+ Correct:
355
+
356
+ ````ts
357
+ import { parseMarkdown } from '@tanstack/markdown/parser'
358
+ import { docsMarkdownExtensions } from '@tanstack/markdown/extensions/docs'
359
+
360
+ const source = `<!-- ::start:tabs variant="files" -->
361
+
362
+ \`\`\`ts file="app.ts"
363
+ export const app = true
364
+ \`\`\`
365
+
366
+ <!-- ::end:tabs -->`
367
+
368
+ export const document = parseMarkdown(source, {
369
+ extensions: docsMarkdownExtensions(),
370
+ })
371
+ ````
372
+
373
+ The file transform silently returns the original component when it finds no direct code-block children; the other variants have similarly specific input contracts.
374
+
375
+ Source: `src/extensions/tabs.ts:16-18`
376
+
377
+ ### MEDIUM Expecting fence metadata to highlight code
378
+
379
+ Wrong:
380
+
381
+ ````ts
382
+ import { renderHtml } from '@tanstack/markdown/html'
383
+
384
+ const source = `\`\`\`ts file="app.ts" {2}
385
+ const one = 1
386
+ const two = 2
387
+ \`\`\``
388
+
389
+ export const html = renderHtml(source)
390
+ ````
391
+
392
+ Correct:
393
+
394
+ ````ts
395
+ import {
396
+ renderNodesToHtml,
397
+ renderTokens,
398
+ tokenize,
399
+ } from '@tanstack/highlight'
400
+ import { renderHtml } from '@tanstack/markdown/html'
401
+
402
+ const source = `\`\`\`ts file="app.ts" {2}
403
+ const one = 1
404
+ const two = 2
405
+ \`\`\``
406
+
407
+ export const html = renderHtml(source, {
408
+ highlighter(code, lang, options) {
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
+ },
419
+ })
420
+ ````
421
+
422
+ Fence metadata populates the AST and highlighter options, but tokenization, themes, and CSS stay outside this package.
423
+
424
+ Source: `docs/core-concepts/syntax-profile.md`
425
+
426
+ ## Boundaries
427
+
428
+ - Docs transforms enrich trusted repository-authored content but do not execute MDX, JSX, or JavaScript expressions.
429
+ - Highlighter output is trusted HTML. Keep highlighting at build time or on the server and apply the checks in [production-pipelines](../production-pipelines/SKILL.md).
430
+ - Import individual extension entry points when the complete preset adds unused behavior or bundle cost.
431
+ - Use [custom-extensions](../custom-extensions/SKILL.md) when built-in comment components or transforms do not express the required deterministic syntax.
432
+ - Use the React or Octane renderer skill to map emitted tags such as `md-tab-panel` and `md-framework-panel` to framework components.
433
+
434
+ ## References
435
+
436
+ - [Docs metadata contracts](references/docs-metadata.md)