Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 68 additions & 44 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -271,6 +281,7 @@ function createProcessor(options) {
.use(remarkPlugins)
.use(remarkRehype, remarkRehypeOptions)
.use(rehypePlugins)
.use(rehypeTransform, options)

return processor
}
Expand Down Expand Up @@ -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>} 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<Root>} */
function transform(node, index, parent) {
if (node.type === 'raw' && parent && typeof index === 'number') {
Expand Down Expand Up @@ -383,9 +359,7 @@ function post(tree, options) {
}
}
}
}

if (node.type === 'element') {
let remove = allowedElements
? !allowedElements.includes(node.tagName)
: disallowedElements
Expand All @@ -409,6 +383,56 @@ function post(tree, options) {
}
}

/**
* Process the result from unified some more.
*
* @param {Nodes} tree
* Tree.
* @param {Readonly<Options>} 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.
*
Expand Down
33 changes: 33 additions & 0 deletions test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,7 @@
tree.children.unshift({
type: 'element',
tagName: 'input',
properties: {id: 'a', ariaDescribedBy: 'b', required: true},

Check failure on line 830 in test.jsx

View workflow job for this annotation

GitHub Actions / node

Type 'string' is not assignable to type 'string[]'.

Check failure on line 830 in test.jsx

View workflow job for this annotation

GitHub Actions / lts/hydrogen

Type 'string' is not assignable to type 'string[]'.
children: []
})
}
Expand Down Expand Up @@ -978,7 +978,7 @@
{
type: 'element',
tagName: 'circle',
properties: {cx: 120, cy: 120, r: 100},

Check failure on line 981 in test.jsx

View workflow job for this annotation

GitHub Actions / node

Type 'number' is not assignable to type 'string'.

Check failure on line 981 in test.jsx

View workflow job for this annotation

GitHub Actions / node

Type 'number' is not assignable to type 'string'.

Check failure on line 981 in test.jsx

View workflow job for this annotation

GitHub Actions / node

Type 'number' is not assignable to type 'string'.

Check failure on line 981 in test.jsx

View workflow job for this annotation

GitHub Actions / lts/hydrogen

Type 'number' is not assignable to type 'string'.

Check failure on line 981 in test.jsx

View workflow job for this annotation

GitHub Actions / lts/hydrogen

Type 'number' is not assignable to type 'string'.

Check failure on line 981 in test.jsx

View workflow job for this annotation

GitHub Actions / lts/hydrogen

Type 'number' is not assignable to type 'string'.
children: []
},
// `strokeMiterLimit` in hast, `strokeMiterlimit` in React.
Expand Down Expand Up @@ -1215,6 +1215,39 @@

assert.equal(result.container.innerHTML, '<p>b</p>')
})

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(
<MarkdownHooks children={'[a](/a)'} urlTransform={urlTransform} />
)

await waitFor(function () {
assert.equal(
result.container.innerHTML,
'<p><a href="/proxy/a">a</a></p>'
)
})

result.rerender(
<MarkdownHooks children={'[a](/a)'} urlTransform={urlTransform} />
)

assert.equal(
result.container.innerHTML,
'<p><a href="/proxy/a">a</a></p>'
)
}
)
})

/**
Expand Down
Loading