markdown-to-jsx 9.0.0-rc.1 → 9.1.0
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 +311 -215
- package/dist/html.cjs +85 -85
- package/dist/html.d.cts +38 -34
- package/dist/html.d.ts +38 -34
- package/dist/html.js +89 -89
- package/dist/html.js.map +6 -6
- package/dist/index.cjs +103 -103
- package/dist/index.d.cts +38 -34
- package/dist/index.d.ts +38 -34
- package/dist/index.js +103 -103
- package/dist/index.js.map +6 -6
- package/dist/markdown.cjs +111 -111
- package/dist/markdown.js +109 -109
- package/dist/markdown.js.map +6 -6
- package/dist/react.cjs +103 -103
- package/dist/react.d.cts +38 -34
- package/dist/react.d.ts +38 -34
- package/dist/react.js +103 -103
- package/dist/react.js.map +6 -6
- package/package.json +7 -10
package/README.md
CHANGED
|
@@ -9,25 +9,30 @@ Some special features of the library:
|
|
|
9
9
|
|
|
10
10
|
- Any HTML tags rendered by the compiler and/or `<Markdown>` component can be overridden to include additional props or even a different HTML representation entirely.
|
|
11
11
|
|
|
12
|
-
- All GFM special syntaxes are supported, including tables, task lists, strikethrough, autolinks, and more.
|
|
12
|
+
- All GFM special syntaxes are supported, including tables, task lists, strikethrough, autolinks, tag filtering, and more.
|
|
13
13
|
|
|
14
14
|
- Fenced code blocks with [highlight.js](https://highlightjs.org/) support; see [Syntax highlighting](#syntax-highlighting) for instructions on setting up highlight.js.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
<h2>Table of Contents</h2>
|
|
17
17
|
|
|
18
18
|
<!-- TOC -->
|
|
19
19
|
|
|
20
|
-
- [Table of Contents](#table-of-contents)
|
|
21
20
|
- [Upgrading](#upgrading)
|
|
22
21
|
- [From v8.x to v9.x](#from-v8x-to-v9x)
|
|
23
22
|
- [From v7.x to v8.x](#from-v7x-to-v8x)
|
|
24
23
|
- [Installation](#installation)
|
|
25
24
|
- [Usage](#usage)
|
|
26
|
-
- [
|
|
25
|
+
- [Entry Points](#entry-points)
|
|
26
|
+
- [Main](#main)
|
|
27
|
+
- [React](#react)
|
|
28
|
+
- [HTML](#html)
|
|
29
|
+
- [Markdown](#markdown)
|
|
30
|
+
- [Library Options](#library-options)
|
|
27
31
|
- [options.forceBlock](#optionsforceblock)
|
|
28
32
|
- [options.forceInline](#optionsforceinline)
|
|
29
33
|
- [options.wrapper](#optionswrapper)
|
|
30
34
|
- [Other useful recipes](#other-useful-recipes)
|
|
35
|
+
- [options.wrapperProps](#optionswrapperprops)
|
|
31
36
|
- [options.forceWrapper](#optionsforcewrapper)
|
|
32
37
|
- [options.overrides - Void particular banned tags](#optionsoverrides---void-particular-banned-tags)
|
|
33
38
|
- [options.overrides - Override Any HTML Tag's Representation](#optionsoverrides---override-any-html-tags-representation)
|
|
@@ -38,25 +43,21 @@ Some special features of the library:
|
|
|
38
43
|
- [options.sanitizer](#optionssanitizer)
|
|
39
44
|
- [options.slugify](#optionsslugify)
|
|
40
45
|
- [options.disableAutoLink](#optionsdisableautolink)
|
|
46
|
+
- [options.preserveFrontmatter](#optionspreservefrontmatter)
|
|
41
47
|
- [options.disableParsingRawHTML](#optionsdisableparsingrawhtml)
|
|
48
|
+
- [options.tagfilter](#optionstagfilter)
|
|
42
49
|
- [Syntax highlighting](#syntax-highlighting)
|
|
43
50
|
- [Handling shortcodes](#handling-shortcodes)
|
|
44
51
|
- [Getting the smallest possible bundle size](#getting-the-smallest-possible-bundle-size)
|
|
45
52
|
- [Usage with Preact](#usage-with-preact)
|
|
46
|
-
- [
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
- [AST Anatomy](#ast-anatomy)
|
|
54
|
+
- [Node Types](#node-types)
|
|
55
|
+
- [Example AST Structure](#example-ast-structure)
|
|
56
|
+
- [Type Checking](#type-checking)
|
|
57
|
+
- [Gotchas](#gotchas)
|
|
58
|
+
- [Passing props to stringified React components](#passing-props-to-stringified-react-components)
|
|
59
|
+
- [Significant indentation inside arbitrary HTML](#significant-indentation-inside-arbitrary-html)
|
|
49
60
|
- [Code blocks](#code-blocks)
|
|
50
|
-
- [Entry Points](#entry-points)
|
|
51
|
-
- [Main](#main)
|
|
52
|
-
- [React](#react)
|
|
53
|
-
- [HTML](#html)
|
|
54
|
-
- [Markdown](#markdown)
|
|
55
|
-
- [Using The Parser Low-Level AST API](#using-the-parser-low-level-ast-api)
|
|
56
|
-
- [AST Anatomy](#ast-anatomy)
|
|
57
|
-
- [Node Types](#node-types)
|
|
58
|
-
- [Example AST Structure](#example-ast-structure)
|
|
59
|
-
- [Type Checking](#type-checking)
|
|
60
61
|
- [Changelog](#changelog)
|
|
61
62
|
- [Donate](#donate)
|
|
62
63
|
|
|
@@ -92,6 +93,19 @@ import { compiler } from 'markdown-to-jsx'
|
|
|
92
93
|
compiler('≤ symbol') // All entities supported automatically
|
|
93
94
|
```
|
|
94
95
|
|
|
96
|
+
- **`tagfilter` enabled by default**: Dangerous HTML tags (`script`, `iframe`, `style`, `title`, `textarea`, `xmp`, `noembed`, `noframes`, `plaintext`) are now escaped by default in both HTML string output and React JSX output. Previously these tags were rendered as JSX elements in React output.
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
// Before (v8) - tags rendered as JSX elements
|
|
100
|
+
compiler('<script>alert("xss")</script>') // Rendered as <script> element
|
|
101
|
+
|
|
102
|
+
// After (v9) - tags escaped by default
|
|
103
|
+
compiler('<script>alert("xss")</script>') // Renders as <span><script></span>
|
|
104
|
+
|
|
105
|
+
// To restore old behavior:
|
|
106
|
+
compiler('<script>alert("xss")</script>', { tagfilter: false })
|
|
107
|
+
```
|
|
108
|
+
|
|
95
109
|
**New Features:**
|
|
96
110
|
|
|
97
111
|
- **New `parser` function**: Provides direct access to the parsed AST without rendering. This is the recommended way to get AST nodes.
|
|
@@ -204,7 +218,75 @@ render(<Markdown># Hello world!</Markdown>, document.body)
|
|
|
204
218
|
|
|
205
219
|
\* **NOTE: JSX does not natively preserve newlines in multiline text. In general, writing markdown directly in JSX is discouraged and it's a better idea to keep your content in separate .md files and require them, perhaps using webpack's [raw-loader](https://github.com/webpack-contrib/raw-loader).**
|
|
206
220
|
|
|
207
|
-
###
|
|
221
|
+
### Entry Points
|
|
222
|
+
|
|
223
|
+
`markdown-to-jsx` provides multiple entry points for different use cases:
|
|
224
|
+
|
|
225
|
+
#### Main
|
|
226
|
+
|
|
227
|
+
The legacy\*default entry point exports everything, including the React compiler and component:
|
|
228
|
+
|
|
229
|
+
```tsx
|
|
230
|
+
import Markdown, { compiler, parser } from 'markdown-to-jsx'
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
_The React code in this entry point is deprecated and will be removed in a future major release, migrate to `markdown-to-jsx/react`._
|
|
234
|
+
|
|
235
|
+
#### React
|
|
236
|
+
|
|
237
|
+
For React-specific usage, import from the `/react` entry point:
|
|
238
|
+
|
|
239
|
+
```tsx
|
|
240
|
+
import Markdown, { compiler, parser, astToJSX } from 'markdown-to-jsx/react'
|
|
241
|
+
|
|
242
|
+
// Use compiler for markdown → JSX
|
|
243
|
+
const jsxElement = compiler('# Hello world')
|
|
244
|
+
|
|
245
|
+
const markdown = `# Hello world`
|
|
246
|
+
|
|
247
|
+
function App() {
|
|
248
|
+
return <Markdown children={markdown} />
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Or use parser + astToJSX for total control
|
|
252
|
+
const ast = parser('# Hello world')
|
|
253
|
+
const jsxElement2 = astToJSX(ast)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
#### HTML
|
|
257
|
+
|
|
258
|
+
For HTML string output (server-side rendering), import from the `/html` entry point:
|
|
259
|
+
|
|
260
|
+
```tsx
|
|
261
|
+
import { compiler, html, parser } from 'markdown-to-jsx/html'
|
|
262
|
+
|
|
263
|
+
// Convenience function that combines parsing and HTML rendering
|
|
264
|
+
const htmlString = compiler('# Hello world')
|
|
265
|
+
// Returns: '<h1>Hello world</h1>'
|
|
266
|
+
|
|
267
|
+
// Or use parser + html separately for more control
|
|
268
|
+
const ast = parser('# Hello world')
|
|
269
|
+
const htmlString2 = html(ast)
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
#### Markdown
|
|
273
|
+
|
|
274
|
+
For markdown-to-markdown compilation (normalization and formatting), import from the `/markdown` entry point:
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
import { compiler, astToMarkdown, parser } from 'markdown-to-jsx/markdown'
|
|
278
|
+
|
|
279
|
+
// Convenience function that parses and recompiles markdown
|
|
280
|
+
const normalizedMarkdown = compiler('# Hello world\n\nExtra spaces!')
|
|
281
|
+
// Returns: '# Hello world\n\nExtra spaces!\n'
|
|
282
|
+
|
|
283
|
+
// Or work with AST directly
|
|
284
|
+
const ast = parser('# Hello world')
|
|
285
|
+
const normalizedMarkdown2 = astToMarkdown(ast)
|
|
286
|
+
// Returns: '# Hello world\n'
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Library Options
|
|
208
290
|
|
|
209
291
|
#### options.forceBlock
|
|
210
292
|
|
|
@@ -283,6 +365,24 @@ compiler('One\n\nTwo\n\nThree', { wrapper: null })
|
|
|
283
365
|
|
|
284
366
|
To render children at the same DOM level as `<Markdown>` with no HTML wrapper, set `wrapper` to `React.Fragment`. This will still wrap your children in a React node for the purposes of rendering, but the wrapper element won't show up in the DOM.
|
|
285
367
|
|
|
368
|
+
#### options.wrapperProps
|
|
369
|
+
|
|
370
|
+
Props to apply to the wrapper element when `wrapper` is used.
|
|
371
|
+
|
|
372
|
+
```tsx
|
|
373
|
+
<Markdown options={{
|
|
374
|
+
wrapper: 'article',
|
|
375
|
+
wrapperProps: { className: 'post', 'data-testid': 'markdown-content' }
|
|
376
|
+
}}>
|
|
377
|
+
# Hello World
|
|
378
|
+
</Markdown>
|
|
379
|
+
|
|
380
|
+
// renders
|
|
381
|
+
<article class="post" data-testid="markdown-content">
|
|
382
|
+
<h1>Hello World</h1>
|
|
383
|
+
</article>
|
|
384
|
+
```
|
|
385
|
+
|
|
286
386
|
#### options.forceWrapper
|
|
287
387
|
|
|
288
388
|
By default, the compiler does not wrap the rendered contents if there is only a single child. You can change this by setting `forceWrapper` to `true`. If the child is inline, it will not necessarily be wrapped in a `span`.
|
|
@@ -300,7 +400,15 @@ By default, the compiler does not wrap the rendered contents if there is only a
|
|
|
300
400
|
|
|
301
401
|
#### options.overrides - Void particular banned tags
|
|
302
402
|
|
|
303
|
-
Pass the `options.overrides` prop to the compiler or `<Markdown>` component with an implementation that return `null` for tags you wish to exclude from the rendered output.
|
|
403
|
+
Pass the `options.overrides` prop to the compiler or `<Markdown>` component with an implementation that return `null` for tags you wish to exclude from the rendered output. This provides complete removal of tags from the output.
|
|
404
|
+
|
|
405
|
+
**Note**: The `tagfilter` option provides default escaping of dangerous tags (`script`, `iframe`, `style`, `title`, `textarea`, `xmp`, `noembed`, `noframes`, `plaintext`). Use `overrides` when you need to:
|
|
406
|
+
|
|
407
|
+
- Remove additional tags not covered by `tagfilter` (like `object`)
|
|
408
|
+
- Have more control over tag removal vs. escaping
|
|
409
|
+
- Disable `tagfilter` but still want to remove specific tags
|
|
410
|
+
|
|
411
|
+
For example, to void the `iframe` tag:
|
|
304
412
|
|
|
305
413
|
```tsx
|
|
306
414
|
import Markdown from 'markdown-to-jsx'
|
|
@@ -317,7 +425,7 @@ render(
|
|
|
317
425
|
// renders: ""
|
|
318
426
|
```
|
|
319
427
|
|
|
320
|
-
The library does not void any tags by default to
|
|
428
|
+
The library does not void any tags by default (except through `tagfilter` escaping), allowing you to choose the appropriate security approach for your use case.
|
|
321
429
|
|
|
322
430
|
#### options.overrides - Override Any HTML Tag's Representation
|
|
323
431
|
|
|
@@ -638,6 +746,61 @@ compiler(
|
|
|
638
746
|
</span>
|
|
639
747
|
```
|
|
640
748
|
|
|
749
|
+
#### options.preserveFrontmatter
|
|
750
|
+
|
|
751
|
+
By default, YAML frontmatter at the beginning of markdown documents is parsed but not rendered in the output. Set this option to `true` to include the frontmatter in the rendered output. For HTML/JSX output, frontmatter is rendered as a `<pre>` element. For markdown-to-markdown compilation, frontmatter is included in the output markdown.
|
|
752
|
+
|
|
753
|
+
| Compiler Type | Default Behavior | When `preserveFrontmatter: true` | When `preserveFrontmatter: false` |
|
|
754
|
+
| ------------------------ | --------------------------- | -------------------------------- | --------------------------------- |
|
|
755
|
+
| **React/HTML** | ❌ Don't render frontmatter | ✅ Render as `<pre>` element | ❌ Don't render frontmatter |
|
|
756
|
+
| **Markdown-to-Markdown** | ✅ Preserve frontmatter | ✅ Preserve frontmatter | ❌ Exclude frontmatter |
|
|
757
|
+
|
|
758
|
+
```tsx
|
|
759
|
+
<Markdown options={{ preserveFrontmatter: true }}>
|
|
760
|
+
{`---
|
|
761
|
+
title: My Document
|
|
762
|
+
author: John Doe
|
|
763
|
+
---
|
|
764
|
+
|
|
765
|
+
# Content
|
|
766
|
+
|
|
767
|
+
This is the main content.`
|
|
768
|
+
}
|
|
769
|
+
</Markdown>
|
|
770
|
+
|
|
771
|
+
// renders:
|
|
772
|
+
|
|
773
|
+
<div>
|
|
774
|
+
<pre>---
|
|
775
|
+
title: My Document
|
|
776
|
+
author: John Doe
|
|
777
|
+
---</pre>
|
|
778
|
+
<h1>Content</h1>
|
|
779
|
+
<p>This is the main content.</p>
|
|
780
|
+
</div>
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
For markdown-to-markdown compilation:
|
|
784
|
+
|
|
785
|
+
```tsx
|
|
786
|
+
import { compiler } from 'markdown-to-jsx/markdown'
|
|
787
|
+
|
|
788
|
+
const markdown = `---
|
|
789
|
+
title: My Document
|
|
790
|
+
author: John Doe
|
|
791
|
+
---
|
|
792
|
+
|
|
793
|
+
# Content`
|
|
794
|
+
|
|
795
|
+
// With preserveFrontmatter: true (default)
|
|
796
|
+
compiler(markdown, { preserveFrontmatter: true })
|
|
797
|
+
// returns: "---\ntitle: My Document\nauthor: John Doe\n---\n\n# Content"
|
|
798
|
+
|
|
799
|
+
// With preserveFrontmatter: false
|
|
800
|
+
compiler(markdown, { preserveFrontmatter: false })
|
|
801
|
+
// returns: "# Content"
|
|
802
|
+
```
|
|
803
|
+
|
|
641
804
|
#### options.disableParsingRawHTML
|
|
642
805
|
|
|
643
806
|
By default, raw HTML is parsed to JSX. This behavior can be disabled if desired.
|
|
@@ -656,6 +819,27 @@ compiler('This text has <span>html</span> in it but it won't be rendered', { dis
|
|
|
656
819
|
<span>This text has <span>html</span> in it but it won't be rendered</span>
|
|
657
820
|
```
|
|
658
821
|
|
|
822
|
+
#### options.tagfilter
|
|
823
|
+
|
|
824
|
+
By default, dangerous HTML tags are filtered and escaped to prevent XSS attacks. This applies to both HTML string output and React JSX output. The following tags are filtered: `script`, `iframe`, `style`, `title`, `textarea`, `xmp`, `noembed`, `noframes`, `plaintext`.
|
|
825
|
+
|
|
826
|
+
```tsx
|
|
827
|
+
// Tags are escaped by default (GFM-compliant)
|
|
828
|
+
compiler('<script>alert("xss")</script>')
|
|
829
|
+
// HTML output: '<span><script></span>'
|
|
830
|
+
// React output: <span><script></span>
|
|
831
|
+
|
|
832
|
+
// Disable tag filtering:
|
|
833
|
+
compiler('<script>alert("xss")</script>', { tagfilter: false })
|
|
834
|
+
// HTML output: '<script></script>'
|
|
835
|
+
// React output: <script></script>
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
**Note**: Even when `tagfilter` is disabled, other security measures remain active:
|
|
839
|
+
|
|
840
|
+
- URL sanitization preventing `javascript:` and `vbscript:` schemes in `href` and `src` attributes
|
|
841
|
+
- Protection against `data:` URLs (except safe `data:image/*` MIME types)
|
|
842
|
+
|
|
659
843
|
### Syntax highlighting
|
|
660
844
|
|
|
661
845
|
When using [fenced code blocks](https://www.markdownguide.org/extended-syntax/#syntax-highlighting) with language annotation, that language will be added to the `<code>` element as `class="lang-${language}"`. For best results, you can use `options.overrides` to provide an appropriate syntax highlighting integration like this one using `highlight.js`:
|
|
@@ -771,198 +955,13 @@ Here are instructions for some of the popular bundlers:
|
|
|
771
955
|
|
|
772
956
|
Everything will work just fine! Simply [Alias `react` to `preact/compat`](https://preactjs.com/guide/v10/switching-to-preact#setting-up-compat) like you probably already are doing.
|
|
773
957
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
### Passing props to stringified React components
|
|
777
|
-
|
|
778
|
-
Using the [`options.overrides`](#optionsoverrides---rendering-arbitrary-react-components) functionality to render React components, props are passed into the component in stringifed form. It is up to you to parse the string to make use of the data.
|
|
779
|
-
|
|
780
|
-
```tsx
|
|
781
|
-
const Table: React.FC<
|
|
782
|
-
JSX.IntrinsicElements['table'] & {
|
|
783
|
-
columns: string
|
|
784
|
-
dataSource: string
|
|
785
|
-
}
|
|
786
|
-
> = ({ columns, dataSource, ...props }) => {
|
|
787
|
-
const parsedColumns = JSON.parse(columns)
|
|
788
|
-
const parsedData = JSON.parse(dataSource)
|
|
789
|
-
|
|
790
|
-
return (
|
|
791
|
-
<div {...props}>
|
|
792
|
-
<h1>Columns</h1>
|
|
793
|
-
{parsedColumns.map(column => (
|
|
794
|
-
<span key={column.key}>{column.title}</span>
|
|
795
|
-
))}
|
|
796
|
-
|
|
797
|
-
<h2>Data</h2>
|
|
798
|
-
{parsedData.map(datum => (
|
|
799
|
-
<span key={datum.key}>{datum.Month}</span>
|
|
800
|
-
))}
|
|
801
|
-
</div>
|
|
802
|
-
)
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
/**
|
|
806
|
-
* Example HTML in markdown:
|
|
807
|
-
*
|
|
808
|
-
* <Table
|
|
809
|
-
* columns={[{ title: 'Month', dataIndex: 'Month', key: 'Month' }]}
|
|
810
|
-
* dataSource={[
|
|
811
|
-
* {
|
|
812
|
-
* Month: '2024-09-01',
|
|
813
|
-
* 'Forecasted Revenue': '$3,137,678.85',
|
|
814
|
-
* 'Forecasted Expenses': '$2,036,660.28',
|
|
815
|
-
* key: 0,
|
|
816
|
-
* },
|
|
817
|
-
* ]}
|
|
818
|
-
* />
|
|
819
|
-
*/
|
|
820
|
-
```
|
|
821
|
-
|
|
822
|
-
### Significant indentation inside arbitrary HTML
|
|
823
|
-
|
|
824
|
-
People usually write HTML like this:
|
|
825
|
-
|
|
826
|
-
```html
|
|
827
|
-
<div>Hey, how are you?</div>
|
|
828
|
-
```
|
|
829
|
-
|
|
830
|
-
Note the leading spaces before the inner content. This sort of thing unfortunately clashes with existing markdown syntaxes since 4 spaces === a code block and other similar collisions.
|
|
831
|
-
|
|
832
|
-
To get around this, `markdown-to-jsx` left-trims approximately as much whitespace as the first line inside the HTML block. So for example:
|
|
833
|
-
|
|
834
|
-
```html
|
|
835
|
-
<div># Hello How are you?</div>
|
|
836
|
-
```
|
|
837
|
-
|
|
838
|
-
The two leading spaces in front of "# Hello" would be left-trimmed from all lines inside the HTML block. In the event that there are varying amounts of indentation, only the amount of the first line is trimmed.
|
|
839
|
-
|
|
840
|
-
> NOTE! These syntaxes work just fine when you aren't writing arbitrary HTML wrappers inside your markdown. This is very much an edge case of an edge case. 🙃
|
|
841
|
-
|
|
842
|
-
#### Code blocks
|
|
843
|
-
|
|
844
|
-
⛔️
|
|
845
|
-
|
|
846
|
-
```md
|
|
847
|
-
<div>
|
|
848
|
-
var some = code();
|
|
849
|
-
</div>
|
|
850
|
-
```
|
|
851
|
-
|
|
852
|
-
✅
|
|
853
|
-
|
|
854
|
-
````md
|
|
855
|
-
<div>
|
|
856
|
-
```js
|
|
857
|
-
var some = code();
|
|
858
|
-
```
|
|
859
|
-
</div>
|
|
860
|
-
````
|
|
861
|
-
|
|
862
|
-
## Entry Points
|
|
863
|
-
|
|
864
|
-
`markdown-to-jsx` provides multiple entry points for different use cases:
|
|
865
|
-
|
|
866
|
-
### Main
|
|
867
|
-
|
|
868
|
-
The legacy\*default entry point exports everything, including the React compiler and component:
|
|
869
|
-
|
|
870
|
-
```tsx
|
|
871
|
-
import Markdown, { compiler, parser } from 'markdown-to-jsx'
|
|
872
|
-
```
|
|
873
|
-
|
|
874
|
-
_The React code in this entry point is deprecated and will be removed in a future major release, migrate to `markdown-to-jsx/react`._
|
|
875
|
-
|
|
876
|
-
### React
|
|
877
|
-
|
|
878
|
-
For React-specific usage, import from the `/react` entry point:
|
|
879
|
-
|
|
880
|
-
```tsx
|
|
881
|
-
import Markdown, { compiler, parser, astToJSX } from 'markdown-to-jsx/react'
|
|
882
|
-
|
|
883
|
-
// Use compiler for markdown → JSX
|
|
884
|
-
const jsxElement = compiler('# Hello world')
|
|
885
|
-
|
|
886
|
-
const markdown = `# Hello world`
|
|
887
|
-
|
|
888
|
-
function App() {
|
|
889
|
-
return <Markdown children={markdown} />
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
// Or use parser + astToJSX for total control
|
|
893
|
-
const ast = parser('# Hello world')
|
|
894
|
-
const jsxElement2 = astToJSX(ast)
|
|
895
|
-
```
|
|
896
|
-
|
|
897
|
-
### HTML
|
|
898
|
-
|
|
899
|
-
For HTML string output (server-side rendering), import from the `/html` entry point:
|
|
900
|
-
|
|
901
|
-
```tsx
|
|
902
|
-
import { compiler, html, parser } from 'markdown-to-jsx/html'
|
|
903
|
-
|
|
904
|
-
// Convenience function that combines parsing and HTML rendering
|
|
905
|
-
const htmlString = compiler('# Hello world')
|
|
906
|
-
// Returns: '<h1>Hello world</h1>'
|
|
907
|
-
|
|
908
|
-
// Or use parser + html separately for more control
|
|
909
|
-
const ast = parser('# Hello world')
|
|
910
|
-
const htmlString2 = html(ast)
|
|
911
|
-
```
|
|
912
|
-
|
|
913
|
-
### Markdown
|
|
914
|
-
|
|
915
|
-
For markdown-to-markdown compilation (normalization and formatting), import from the `/markdown` entry point:
|
|
916
|
-
|
|
917
|
-
```typescript
|
|
918
|
-
import { compiler, astToMarkdown, parser } from 'markdown-to-jsx/markdown'
|
|
919
|
-
|
|
920
|
-
// Convenience function that parses and recompiles markdown
|
|
921
|
-
const normalizedMarkdown = compiler('# Hello world\n\nExtra spaces!')
|
|
922
|
-
// Returns: '# Hello world\n\nExtra spaces!\n'
|
|
923
|
-
|
|
924
|
-
// Or work with AST directly
|
|
925
|
-
const ast = parser('# Hello world')
|
|
926
|
-
const normalizedMarkdown2 = astToMarkdown(ast)
|
|
927
|
-
// Returns: '# Hello world\n'
|
|
928
|
-
```
|
|
929
|
-
|
|
930
|
-
## Using The Parser (Low-Level AST API)
|
|
931
|
-
|
|
932
|
-
The `parser` function provides direct access to the parsed AST without rendering:
|
|
933
|
-
|
|
934
|
-
```tsx
|
|
935
|
-
import { parser } from 'markdown-to-jsx'
|
|
936
|
-
import type { MarkdownToJSX } from 'markdown-to-jsx'
|
|
937
|
-
|
|
938
|
-
// Parse markdown to AST
|
|
939
|
-
const ast = parser('# Hello world')
|
|
940
|
-
|
|
941
|
-
// TypeScript: AST is MarkdownToJSX.ASTNode[]
|
|
942
|
-
console.log(ast) // Array of parsed nodes
|
|
943
|
-
|
|
944
|
-
// You can then render with html() or compiler()
|
|
945
|
-
```
|
|
946
|
-
|
|
947
|
-
The `parser` function accepts:
|
|
948
|
-
|
|
949
|
-
- `source: string` - markdown source
|
|
950
|
-
- `state?: MarkdownToJSX.State` - parsing state (defaults: `{ inline: false, refs: {} }`)
|
|
951
|
-
- `options?: MarkdownToJSX.Options` - parsing options
|
|
952
|
-
|
|
953
|
-
Use `parser` when you need:
|
|
954
|
-
|
|
955
|
-
- Direct AST access without React rendering
|
|
956
|
-
- Custom rendering logic
|
|
957
|
-
- AST manipulation before rendering
|
|
958
|
-
|
|
959
|
-
## AST Anatomy
|
|
958
|
+
### AST Anatomy
|
|
960
959
|
|
|
961
960
|
The Abstract Syntax Tree (AST) is a structured representation of parsed markdown. Each node in the AST has a `type` property that identifies its kind, and type-specific properties.
|
|
962
961
|
|
|
963
962
|
**Important:** The first node in the AST is typically a `RuleType.refCollection` node that contains all reference definitions found in the document, including footnotes (stored with keys prefixed with `^`). This node is skipped during rendering but is useful for accessing reference data. Footnotes are automatically extracted from the refCollection and rendered in a `<footer>` element by both `compiler()` and `astToJSX()`.
|
|
964
963
|
|
|
965
|
-
|
|
964
|
+
#### Node Types
|
|
966
965
|
|
|
967
966
|
The AST consists of the following node types (use `RuleType` to check node types):
|
|
968
967
|
|
|
@@ -987,7 +986,7 @@ The AST consists of the following node types (use `RuleType` to check node types
|
|
|
987
986
|
- `RuleType.orderedList` / `RuleType.unorderedList` - Lists
|
|
988
987
|
```tsx
|
|
989
988
|
{ type: RuleType.orderedList, items: [[...]], start?: 1 }
|
|
990
|
-
{ type: RuleType.unorderedList, items: [[...]]
|
|
989
|
+
{ type: RuleType.unorderedList, items: [[...]] }
|
|
991
990
|
```
|
|
992
991
|
- `RuleType.table` - Tables
|
|
993
992
|
```tsx
|
|
@@ -1020,21 +1019,30 @@ The AST consists of the following node types (use `RuleType` to check node types
|
|
|
1020
1019
|
```tsx
|
|
1021
1020
|
{ type: RuleType.image, target: "image.png", alt: "description" }
|
|
1022
1021
|
```
|
|
1023
|
-
- `RuleType.refLink` / `RuleType.refImage` - Reference-style links/images
|
|
1024
|
-
```tsx
|
|
1025
|
-
{ type: RuleType.refLink, ref: "linkref", children: [...] }
|
|
1026
|
-
```
|
|
1027
1022
|
|
|
1028
1023
|
**Other nodes:**
|
|
1029
1024
|
|
|
1030
1025
|
- `RuleType.breakLine` - Hard line breaks (` `)
|
|
1031
1026
|
- `RuleType.breakThematic` - Horizontal rules (`---`)
|
|
1032
1027
|
- `RuleType.gfmTask` - GFM task list items (`- [ ]`)
|
|
1028
|
+
- `RuleType.ref` - Reference definition node (not rendered, stored in refCollection)
|
|
1033
1029
|
- `RuleType.refCollection` - Reference definitions collection (appears at AST root, includes footnotes with `^` prefix)
|
|
1034
1030
|
- `RuleType.footnote` - Footnote definition node (not rendered, stored in refCollection)
|
|
1035
1031
|
- `RuleType.footnoteReference` - Footnote reference (`[^identifier]`)
|
|
1032
|
+
- `RuleType.frontmatter` - YAML frontmatter blocks
|
|
1033
|
+
```tsx
|
|
1034
|
+
{ type: RuleType.frontmatter, text: "---\ntitle: My Title\n---" }
|
|
1035
|
+
```
|
|
1036
|
+
- `RuleType.htmlComment` - HTML comment nodes
|
|
1037
|
+
```tsx
|
|
1038
|
+
{ type: RuleType.htmlComment, text: "<!-- comment -->" }
|
|
1039
|
+
```
|
|
1040
|
+
- `RuleType.htmlSelfClosing` - Self-closing HTML tags
|
|
1041
|
+
```tsx
|
|
1042
|
+
{ type: RuleType.htmlSelfClosing, tag: "img", attrs: { src: "image.png" } }
|
|
1043
|
+
```
|
|
1036
1044
|
|
|
1037
|
-
|
|
1045
|
+
#### Example AST Structure
|
|
1038
1046
|
|
|
1039
1047
|
````tsx
|
|
1040
1048
|
import { parser, RuleType } from 'markdown-to-jsx'
|
|
@@ -1093,9 +1101,9 @@ console.log('code')
|
|
|
1093
1101
|
|
|
1094
1102
|
````
|
|
1095
1103
|
|
|
1096
|
-
|
|
1104
|
+
#### Type Checking
|
|
1097
1105
|
|
|
1098
|
-
Use `RuleType`
|
|
1106
|
+
Use the `RuleType` enum to identify AST nodes:
|
|
1099
1107
|
|
|
1100
1108
|
```tsx
|
|
1101
1109
|
import { RuleType } from 'markdown-to-jsx'
|
|
@@ -1112,6 +1120,94 @@ if (node.type === RuleType.heading) {
|
|
|
1112
1120
|
- Use `compiler` when you need React JSX output from markdown (the component uses this internally).
|
|
1113
1121
|
- Use `parser` + `astToJSX` when you need the AST for custom processing before rendering to JSX, or just the AST itself.
|
|
1114
1122
|
|
|
1123
|
+
### Gotchas
|
|
1124
|
+
|
|
1125
|
+
#### Passing props to stringified React components
|
|
1126
|
+
|
|
1127
|
+
Using the [`options.overrides`](#optionsoverrides---rendering-arbitrary-react-components) functionality to render React components, props are passed into the component in stringifed form. It is up to you to parse the string to make use of the data.
|
|
1128
|
+
|
|
1129
|
+
```tsx
|
|
1130
|
+
const Table: React.FC<
|
|
1131
|
+
JSX.IntrinsicElements['table'] & {
|
|
1132
|
+
columns: string
|
|
1133
|
+
dataSource: string
|
|
1134
|
+
}
|
|
1135
|
+
> = ({ columns, dataSource, ...props }) => {
|
|
1136
|
+
const parsedColumns = JSON.parse(columns)
|
|
1137
|
+
const parsedData = JSON.parse(dataSource)
|
|
1138
|
+
|
|
1139
|
+
return (
|
|
1140
|
+
<div {...props}>
|
|
1141
|
+
<h1>Columns</h1>
|
|
1142
|
+
{parsedColumns.map(column => (
|
|
1143
|
+
<span key={column.key}>{column.title}</span>
|
|
1144
|
+
))}
|
|
1145
|
+
|
|
1146
|
+
<h2>Data</h2>
|
|
1147
|
+
{parsedData.map(datum => (
|
|
1148
|
+
<span key={datum.key}>{datum.Month}</span>
|
|
1149
|
+
))}
|
|
1150
|
+
</div>
|
|
1151
|
+
)
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
/**
|
|
1155
|
+
* Example HTML in markdown:
|
|
1156
|
+
*
|
|
1157
|
+
* <Table
|
|
1158
|
+
* columns={[{ title: 'Month', dataIndex: 'Month', key: 'Month' }]}
|
|
1159
|
+
* dataSource={[
|
|
1160
|
+
* {
|
|
1161
|
+
* Month: '2024-09-01',
|
|
1162
|
+
* 'Forecasted Revenue': '$3,137,678.85',
|
|
1163
|
+
* 'Forecasted Expenses': '$2,036,660.28',
|
|
1164
|
+
* key: 0,
|
|
1165
|
+
* },
|
|
1166
|
+
* ]}
|
|
1167
|
+
* />
|
|
1168
|
+
*/
|
|
1169
|
+
```
|
|
1170
|
+
|
|
1171
|
+
#### Significant indentation inside arbitrary HTML
|
|
1172
|
+
|
|
1173
|
+
People usually write HTML like this:
|
|
1174
|
+
|
|
1175
|
+
```html
|
|
1176
|
+
<div>Hey, how are you?</div>
|
|
1177
|
+
```
|
|
1178
|
+
|
|
1179
|
+
Note the leading spaces before the inner content. This sort of thing unfortunately clashes with existing markdown syntaxes since 4 spaces === a code block and other similar collisions.
|
|
1180
|
+
|
|
1181
|
+
To get around this, `markdown-to-jsx` left-trims approximately as much whitespace as the first line inside the HTML block. So for example:
|
|
1182
|
+
|
|
1183
|
+
```html
|
|
1184
|
+
<div># Hello How are you?</div>
|
|
1185
|
+
```
|
|
1186
|
+
|
|
1187
|
+
The two leading spaces in front of "# Hello" would be left-trimmed from all lines inside the HTML block. In the event that there are varying amounts of indentation, only the amount of the first line is trimmed.
|
|
1188
|
+
|
|
1189
|
+
> NOTE! These syntaxes work just fine when you aren't writing arbitrary HTML wrappers inside your markdown. This is very much an edge case of an edge case. 🙃
|
|
1190
|
+
|
|
1191
|
+
#### Code blocks
|
|
1192
|
+
|
|
1193
|
+
⛔️
|
|
1194
|
+
|
|
1195
|
+
```md
|
|
1196
|
+
<div>
|
|
1197
|
+
var some = code();
|
|
1198
|
+
</div>
|
|
1199
|
+
```
|
|
1200
|
+
|
|
1201
|
+
✅
|
|
1202
|
+
|
|
1203
|
+
````md
|
|
1204
|
+
<div>
|
|
1205
|
+
```js
|
|
1206
|
+
var some = code();
|
|
1207
|
+
```
|
|
1208
|
+
</div>
|
|
1209
|
+
````
|
|
1210
|
+
|
|
1115
1211
|
## Changelog
|
|
1116
1212
|
|
|
1117
1213
|
See [Github Releases](https://github.com/quantizor/markdown-to-jsx/releases).
|