UNPKG

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
# 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">&lt;div class=&quot;nav&quot;&gt; &lt;div class=&quot;nav-item&quot;&gt; &lt;a href=&quot;/features&quot; data-rr-ui-event-key=&quot;/features&quot; class=&quot;nav-link&quot; &gt;Features&lt;/a &gt; &lt;/div&gt; &lt;div class=&quot;nav-item&quot;&gt; &lt;a href=&quot;pricing&quot; data-rr-ui-event-key=&quot;pricing&quot; class=&quot;nav-link&quot; &gt;Pricing&lt;/a &gt; &lt;/div&gt; &lt;div class=&quot;nav-item&quot;&gt; &lt;a href=&quot;about&quot; data-rr-ui-event-key=&quot;about&quot; class=&quot;nav-link&quot;&gt;About&lt;/a&gt; &lt;/div&gt; &lt;/div&gt; </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 💛&nbsp; 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 ![GitHub](https://img.shields.io/github/license/bent10/marked-extensions) A project by [Stilearning](https://stilearning.com) &copy; 2023-2024.