marked-code-jsx-renderer
Version:
A marked extension to render JSX code blocks using a custom renderer and components
253 lines (182 loc) âĸ 8.09 kB
Markdown
# marked-code-jsx-renderer
A [marked](https://marked.js.org/) extension to render JSX code blocks using a custom renderer and components. This extension is especially useful when you want to incorporate React JSX code directly into your Markdown documents and control how it's rendered.
## Install
You can install the `marked-code-jsx-renderer` using npm or yarn:
```bash
npm i -D marked-code-jsx-renderer
# or
yarn add --dev marked-code-jsx-renderer
```
**â ī¸ NOTE:** This extension exclusively supports server-side operations and is not compatible with web browsers.
## Usage
To use this extension, you need to incorporate it into your marked processing pipeline. Here's an example of how to do it:
Say we have the following file `example.md`:
````md
This is some code:
```jsx renderable prettier
<Nav>
<Nav.Item>
<Nav.Link href='/features'>Features</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link href='/pricing'>Pricing</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link href='/about'>About</Nav.Link>
</Nav.Item>
</Nav>
```
````
**đ¨ Important:** The `renderable` attribute must be specified in code fence blocks!
And our module `example.js` looks as follows:
```js
import { readFileSync } from 'node:fs'
import { Marked } from 'marked'
import markedCodeFormat from 'marked-code-format'
import markedCodeJsxRenderer from 'marked-code-jsx-renderer'
// runner
import * as runtime from 'react/jsx-runtime'
import { Nav } from 'react-bootstrap'
import { renderToStaticMarkup } from 'react-dom/server'
const content = readFileSync('example.md', 'utf-8')
const html = await new Marked({ async: true })
.use(
markedCodeJsxRenderer({
...runtime,
components: { Nav },
renderer: renderToStaticMarkup
})
)
.use(markedCodeFormat())
.parse(content)
console.log(html)
```
Now, running node `example.js` yields:
```html
<p>This is some code:</p>
<pre><code class="language-html"><div class="nav">
<div class="nav-item">
<a href="/features" data-rr-ui-event-key="/features" class="nav-link"
>Features</a
>
</div>
<div class="nav-item">
<a href="pricing" data-rr-ui-event-key="pricing" class="nav-link"
>Pricing</a
>
</div>
<div class="nav-item">
<a href="about" data-rr-ui-event-key="about" class="nav-link">About</a>
</div>
</div>
</code></pre>
```
> âšī¸ This extension offers support for inline options, specifically
> tailored to the `unwrap` option. With inline options, you have
> fine-grained control over the behavior of the `unwrap` feature.
>
> ````md
> ```jsx renderable="{unwrap: true}"
> // jsx code here
> ```
> ````
## Options
This extension accepts several options to customize its behavior:
### `components`
An object where keys represent component names and values are React component types. These components are used for rendering JSX code blocks.
```js
import { Alert, Button } from 'react-bootstrap'
marked.use(
markedCodeJsxRenderer({
components: { Alert, Button }
})
)
```
### `Fragment`
Symbol to use for fragments. This option can be helpful if your JSX code specifically requires a particular type of Fragment.
```js
import { Fragment } from 'react/jsx-runtime'
marked.use(markedCodeJsxRenderer({ Fragment }))
```
### `jsx`
The `jsx` function to use when rendering JSX code. You can customize this function if your rendering process relies on a custom `jsx` implementation.
```js
import { jsx } from 'react/jsx-runtime'
marked.use(markedCodeJsxRenderer({ jsx }))
```
### `jsxs`
The `jsxs` function to use when rendering JSX code. Similar to `jsx`, this option allows you to customize the `jsxs` function if needed.
```js
import { jsxs } from 'react/jsx-runtime'
marked.use(markedCodeJsxRenderer({ jsxs }))
```
### `renderer`
A custom rendering function for rendering JSX code. This function should return a string. You can use this to render JSX using various methods, such as converting it to HTML or rendering it on the client-side.
```js
import { renderToStaticMarkup } from 'react-dom/server'
marked.use(markedCodeJsxRenderer({ renderer: renderToStaticMarkup }))
```
### `sanitizer`
The `sanitizer` option is an optional function that allows you to sanitize the JSX code before rendering. You can use this function to enhance security and prevent code injection.
```js
import { renderToStaticMarkup } from 'react-dom/server'
import xss from 'xss'
marked.use(markedCodeJsxRenderer({ sanitizer: customSanitizer }))
// Sanitize the JSX code using the xss library
// you can replace it with any sanitizer you want (e.g. DOMPurify)
function sanitizeJSX(jsxCode) {
const options = {
// Define your custom xss options here
}
return xss(jsxCode, options)
}
```
### `errorHandler`
Implement monitoring and logging mechanisms to keep track of any unusual or potentially malicious activities during transformation, if the markdown input contains untrusted or user-generated content.
```js
// in this example, errors will be logged to the console for debugging and monitoring purposes.
marked.use(markedCodeJsxRenderer({ errorHandler: console.error }))
// you can replace console.error with a more advanced logging solution like Winston or Morgan
// for better error tracking and management.
```
### `unwrap`
If `true`, the extension will not wrap the rendered code in a `codefence` element. Based on the example above, this will result in the following output:
```html
<p>This is some code:</p>
<div class="nav">
<div class="nav-item">
<a href="/features" data-rr-ui-event-key="/features" class="nav-link"
>Features</a
>
</div>
<div class="nav-item">
<a href="pricing" data-rr-ui-event-key="pricing" class="nav-link"
>Pricing</a
>
</div>
<div class="nav-item">
<a href="about" data-rr-ui-event-key="about" class="nav-link">About</a>
</div>
</div>
```
## Security considerations
It's essential to be aware of potential security risks, especially when the markdown input contains untrusted or user-generated content. Here are some security considerations when using this extension:
- **Code Injection:** This extension uses the `new Function` constructor to dynamically create a function from the transformed code. While this is a common technique for rendering JSX, it can be risky if the input code contains malicious code. Ensure that you thoroughly [sanitize](#sanitizer) and validate the input code to prevent code injection attacks.
- **Untrusted Markdown:** If your application allows users to input markdown content, there is a risk of users injecting malicious code within code blocks. Make sure to [sanitize](#sanitizer) and validate user-generated markdown content to prevent any security vulnerabilities.
- **Error Handling:** The code includes an error handling mechanism ([`errorHandler`](#errorhandler)) to catch and handle exceptions. While this is a good practice, be cautious not to expose sensitive information in error messages, which could aid attackers in understanding your system's architecture.
## Related
- [marked-code-format](https://github.com/bent10/marked-extensions/tree/main/packages/code-format)
- [marked-code-preview](https://github.com/bent10/marked-extensions/tree/main/packages/code-preview)
## Contributing
We đ issues.
When committing, please conform to [the semantic-release commit standards](https://www.conventionalcommits.org/). Please install `commitizen` and the adapter globally, if you have not already.
```bash
npm i -g commitizen cz-conventional-changelog
```
Now you can use `git cz` or just `cz` instead of `git commit` when committing. You can also use `git-cz`, which is an alias for `cz`.
```bash
git add . && git cz
```
## License

A project by [Stilearning](https://stilearning.com) © 2023-2024.