diff --git a/lib/index.js b/lib/index.js index 06f07e1..5ef2fa0 100644 --- a/lib/index.js +++ b/lib/index.js @@ -216,7 +216,17 @@ export function MarkdownHooks(options) { function () { return createProcessor(options) }, - [options.rehypePlugins, options.remarkPlugins, options.remarkRehypeOptions] + [ + options.allowElement, + options.allowedElements, + options.disallowedElements, + options.rehypePlugins, + options.remarkPlugins, + options.remarkRehypeOptions, + options.skipHtml, + options.unwrapDisallowed, + options.urlTransform + ] ) const [error, setError] = useState( /** @type {Error | undefined} */ (undefined) @@ -271,6 +281,7 @@ function createProcessor(options) { .use(remarkPlugins) .use(remarkRehype, remarkRehypeOptions) .use(rehypePlugins) + .use(rehypeTransform, options) return processor } @@ -301,60 +312,25 @@ function createFile(options) { } /** - * Process the result from unified some more. + * Add the transforms that must run once for each processed tree. * - * @param {Nodes} tree - * Tree. * @param {Readonly} options * Props. - * @returns {ReactElement} - * React element. + * @returns {(tree: Root) => undefined} + * Transform. */ -function post(tree, options) { +function rehypeTransform(options) { const allowedElements = options.allowedElements const allowElement = options.allowElement - const components = options.components const disallowedElements = options.disallowedElements const skipHtml = options.skipHtml const unwrapDisallowed = options.unwrapDisallowed const urlTransform = options.urlTransform || defaultUrlTransform - for (const deprecation of deprecations) { - if (Object.hasOwn(options, deprecation.from)) { - unreachable( - 'Unexpected `' + - deprecation.from + - '` prop, ' + - (deprecation.to - ? 'use `' + deprecation.to + '` instead' - : 'remove it') + - ' (see <' + - changelog + - '#' + - deprecation.id + - '> for more info)' - ) - } - } - - if (allowedElements && disallowedElements) { - unreachable( - 'Unexpected combined `allowedElements` and `disallowedElements`, expected one or the other' - ) + return function (tree) { + visit(tree, transform) } - visit(tree, transform) - - return toJsxRuntime(tree, { - Fragment, - components, - ignoreInvalidStyle: true, - jsx, - jsxs, - passKeys: true, - passNode: true - }) - /** @type {BuildVisitor} */ function transform(node, index, parent) { if (node.type === 'raw' && parent && typeof index === 'number') { @@ -383,9 +359,7 @@ function post(tree, options) { } } } - } - if (node.type === 'element') { let remove = allowedElements ? !allowedElements.includes(node.tagName) : disallowedElements @@ -409,6 +383,56 @@ function post(tree, options) { } } +/** + * Process the result from unified some more. + * + * @param {Nodes} tree + * Tree. + * @param {Readonly} options + * Props. + * @returns {ReactElement} + * React element. + */ +function post(tree, options) { + const allowedElements = options.allowedElements + const components = options.components + const disallowedElements = options.disallowedElements + + for (const deprecation of deprecations) { + if (Object.hasOwn(options, deprecation.from)) { + unreachable( + 'Unexpected `' + + deprecation.from + + '` prop, ' + + (deprecation.to + ? 'use `' + deprecation.to + '` instead' + : 'remove it') + + ' (see <' + + changelog + + '#' + + deprecation.id + + '> for more info)' + ) + } + } + + if (allowedElements && disallowedElements) { + unreachable( + 'Unexpected combined `allowedElements` and `disallowedElements`, expected one or the other' + ) + } + + return toJsxRuntime(tree, { + Fragment, + components, + ignoreInvalidStyle: true, + jsx, + jsxs, + passKeys: true, + passNode: true + }) +} + /** * Make a URL safe. * diff --git a/test.jsx b/test.jsx index 1b254db..1128b0a 100644 --- a/test.jsx +++ b/test.jsx @@ -1215,6 +1215,39 @@ test('MarkdownHooks', async function (t) { assert.equal(result.container.innerHTML, '

b

') }) + + await t.test( + 'should not reapply transforms to cached trees', + async function () { + /** + * @param {string} url + * @returns {string} + */ + function urlTransform(url) { + return '/proxy' + url + } + + const result = render( + + ) + + await waitFor(function () { + assert.equal( + result.container.innerHTML, + '

a

' + ) + }) + + result.rerender( + + ) + + assert.equal( + result.container.innerHTML, + '

a

' + ) + } + ) }) /**