Skip to content

Commit

Permalink
Add async processing support to support async plugins
Browse files Browse the repository at this point in the history
Resolves remarkjs#680

Signed-off-by: Michael Irwin <mikesir87@gmail.com>
  • Loading branch information
mikesir87 committed Apr 6, 2022
1 parent 8143c12 commit 1d04e4a
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 2 deletions.
18 changes: 16 additions & 2 deletions lib/react-markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* @property {PluggableList} [remarkPlugins=[]]
* @property {PluggableList} [rehypePlugins=[]]
* @property {import('remark-rehype').Options | undefined} [remarkRehypeOptions={}]
* @property {boolean} [async=false]
*
* @typedef LayoutOptions
* @property {string} [className]
Expand Down Expand Up @@ -69,9 +70,13 @@ const deprecated = {
* React component to render markdown.
*
* @param {ReactMarkdownOptions} options
* @returns {ReactElement}
* @returns {ReactElement | null}
*/
export function ReactMarkdown(options) {
const [asyncHastNode, setAsyncHastNode] = React.useState(
/** @type {?Root} */ (null)
)

for (const key in deprecated) {
if (own.call(deprecated, key) && own.call(options, key)) {
const deprecation = deprecated[key]
Expand Down Expand Up @@ -104,7 +109,16 @@ export function ReactMarkdown(options) {
)
}

const hastNode = processor.runSync(processor.parse(file), file)
if (options.async && !asyncHastNode) {
processor
.run(processor.parse(file), file)
.then((node) => setAsyncHastNode(node))
return null
}

const hastNode = options.async
? /** @type Root */ (asyncHastNode)
: processor.runSync(processor.parse(file), file)

if (hastNode.type !== 'root') {
throw new TypeError('Expected a `root` node')
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@types/react-is": "^17.0.0",
"@types/react-test-renderer": "^17.0.0",
"c8": "^7.0.0",
"esbuild": "^0.14.0",
"eslint-config-xo-react": "^0.27.0",
Expand All @@ -113,6 +114,7 @@
"prettier": "^2.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-test-renderer": "^18.0.0",
"rehype-raw": "^6.0.0",
"remark-cli": "^10.0.0",
"remark-gfm": "^3.0.0",
Expand Down
23 changes: 23 additions & 0 deletions test/test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
* @typedef {import('hast').Text} Text
* @typedef {import('react').ReactNode} ReactNode
* @typedef {import('../index.js').Components} Components
* @typedef {import('react-test-renderer').ReactTestRenderer} ReactTestRenderer
*/

import fs from 'node:fs'
import path from 'node:path'
import {fail} from 'node:assert'
import {test} from 'uvu'
import * as assert from 'uvu/assert'
import React from 'react'
Expand All @@ -18,6 +20,7 @@ import {visit} from 'unist-util-visit'
import raw from 'rehype-raw'
import toc from 'remark-toc'
import ReactDom from 'react-dom/server'
import renderer, {act} from 'react-test-renderer'
import Markdown from '../index.js'

const own = {}.hasOwnProperty
Expand All @@ -27,6 +30,7 @@ const own = {}.hasOwnProperty
* @returns {string}
*/
function asHtml(input) {
if (!input) return ''
return ReactDom.renderToStaticMarkup(input)
}

Expand Down Expand Up @@ -1424,4 +1428,23 @@ test('should crash on a plugin replacing `root`', () => {
}, /Expected a `root` node/)
})

test('should work correctly when executed asynchronously', async () => {
const input = '# Test'

/** @type {ReactTestRenderer | undefined} */
let component
await act(async () => {
component = renderer.create(<Markdown children={input} async />)
})

if (!component) fail('component not set')

const renderedOutput = component.toJSON()
if (!renderedOutput) fail('No rendered output provided')
if (Array.isArray(renderedOutput)) fail('Not expecting multiple children')

assert.equal(renderedOutput.type, 'h1')
assert.equal(renderedOutput.children, ['Test'])
})

test.run()

0 comments on commit 1d04e4a

Please sign in to comment.