Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: remarkjs/react-markdown
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 9.0.0
Choose a base ref
...
head repository: remarkjs/react-markdown
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 10.1.0
Choose a head ref

Commits on Sep 27, 2023

  1. Fix to close details

    wooorm committed Sep 27, 2023

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    5445cbb View commit details

Commits on Sep 28, 2023

  1. Refactor some more

    wooorm committed Sep 28, 2023

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    1d5cbf5 View commit details
  2. Fix typos

    wooorm committed Sep 28, 2023

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    bc09364 View commit details

Commits on Oct 15, 2023

  1. Fix typo

    Closes GH-782.
    wooorm committed Oct 15, 2023

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    2245c64 View commit details

Commits on Nov 6, 2023

  1. Refactor docs to use createRoot

    Closes GH-779.
    
    Co-authored-by: tris203 <tris203@gmail.com>
    wooorm and tris203 committed Nov 6, 2023

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    55d8d83 View commit details

Commits on Nov 13, 2023

  1. Fix double encoding in new url transform

    Closes GH-797.
    wooorm committed Nov 13, 2023

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    d8e3787 View commit details
  2. 9.0.1

    wooorm committed Nov 13, 2023

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    a27d335 View commit details

Commits on Mar 25, 2024

  1. Update dev-dependencies

    wooorm committed Mar 25, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    78160f5 View commit details

Commits on Jun 16, 2024

  1. Update dev-dependencies

    wooorm committed Jun 16, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    7f32314 View commit details

Commits on Jun 28, 2024

  1. Refactor to use @import to import types

    Closes GH-836.
    
    Reviewed-by: Titus Wormer <tituswormer@gmail.com>
    remcohaszing authored Jun 28, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    aa5933b View commit details
  2. Replace JSX global with explicit ReactElement

    Closes GH-837.
    
    Reviewed-by: Titus Wormer <tituswormer@gmail.com>
    remcohaszing authored Jun 28, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    7bf4c04 View commit details

Commits on Jul 15, 2024

  1. Add missing dev-dependency

    Closes GH-846.
    
    Reviewed-by: Remco Haszing <remcohaszing@gmail.com>
    Reviewed-by: Titus Wormer <tituswormer@gmail.com>
    mayank1513 authored Jul 15, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    fd337ff View commit details

Commits on Aug 19, 2024

  1. Update dev-dependencies

    wooorm committed Aug 19, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    ab6703a View commit details
  2. Update Actions

    wooorm committed Aug 19, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    70fca29 View commit details

Commits on Sep 19, 2024

  1. Update dev-dependencies

    wooorm committed Sep 19, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    ac51b50 View commit details
  2. Add .tsbuildinfo to .gitignore

    wooorm committed Sep 19, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    dace107 View commit details
  3. Add declaration maps

    wooorm committed Sep 19, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    6962af7 View commit details
  4. Remove license year

    wooorm committed Sep 19, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    baad6c5 View commit details

Commits on Oct 15, 2024

  1. Update dev-dependencies

    wooorm committed Oct 15, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    277048c View commit details
  2. Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    492b53e View commit details
  3. Refactor Actions

    wooorm committed Oct 15, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    bb6c8e8 View commit details
  4. Refactor .editorconfig

    wooorm committed Oct 15, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    a7ca8ed View commit details

Commits on Oct 25, 2024

  1. Fix typo

    Closes GH-868.
    
    Reviewed-by: Remco Haszing <remcohaszing@gmail.com>
    Reviewed-by: Titus Wormer <tituswormer@gmail.com>
    deep-lyra authored Oct 25, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    515bf19 View commit details

Commits on Nov 21, 2024

  1. Fix typo in changelog

    Closes GH-874.
    
    Reviewed-by: Remco Haszing <remcohaszing@gmail.com>
    Reviewed-by: Titus Wormer <tituswormer@gmail.com>
    NicholasWilsonDEV authored Nov 21, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    9eb589e View commit details

Commits on Jan 2, 2025

  1. Separate all typedefs into their own JSDoc blocks (#878)

    Having them in the same block, can be problematic sometimes. For
    example when they contain `@template` tags.
    remcohaszing authored Jan 2, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    27d3949 View commit details
  2. Fix types for React 19

    Closes GH-879.
    
    Reviewed-by: Christian Murphy <christian.murphy.42@gmail.com>
    Reviewed-by: Titus Wormer <tituswormer@gmail.com>
    remcohaszing authored Jan 2, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    b151a90 View commit details
  3. Update dev-dependencies

    wooorm committed Jan 2, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    e686551 View commit details

Commits on Jan 6, 2025

  1. Update Actions

    wooorm committed Jan 6, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    b664ac4 View commit details
  2. Refactor .gitignore

    wooorm committed Jan 6, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    2c6ffe8 View commit details
  3. 9.0.2

    wooorm committed Jan 6, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    40c097e View commit details
  4. 9.0.3

    wooorm committed Jan 6, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    aed0010 View commit details

Commits on Feb 13, 2025

  1. Update dev-dependencies

    wooorm committed Feb 13, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    c44e246 View commit details
  2. Refactor package.json

    wooorm committed Feb 13, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    bcdc5b3 View commit details
  3. Refactor to remove warning in tests

    wooorm committed Feb 13, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    78d08de View commit details

Commits on Feb 20, 2025

  1. Add support for async plugins

    This commit adds 2 new components that support
    turning markdown into react nodes,
    asynchronously.
    There are different ways to support async things in React.
    Component with hooks only run on the client.
    Components yielding promises are not supported on the client.
    To support different scenarios and the different ways the future
    could develop,
    these choices are made explicit to users.
    Users can choose whether `MarkdownAsync` or `MarkdownHooks` fits
    their use case.
    
    Closes GH-680.
    Closes GH-682.
    Closes GH-890.
    Closes GH-891.
    
    Reviewed-by: Christian Murphy <christian.murphy.42@gmail.com>
    Reviewed-by: Remco Haszing <remcohaszing@gmail.com>
    wooorm authored Feb 20, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    6ce120e View commit details
  2. 9.1.0

    wooorm committed Feb 20, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    747e505 View commit details
  3. Remove support for className prop

    Closes GH-781.
    Closes GH-799.
    
    Reviewed-by: Christian Murphy <christian.murphy.42@gmail.com>
    
    Co-authored-by: Remco Haszing <remcohaszing@gmail.com>
    wooorm and remcohaszing committed Feb 20, 2025

    Partially verified

    This commit is signed with the committer’s verified signature.
    wooorm’s contribution has been verified via GPG key.
    We cannot verify signatures from co-authors, and some of the co-authors attributed to this commit require their commits to be signed.
    Copy the full SHA
    aaaa40b View commit details
  4. Update changelog

    wooorm committed Feb 20, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    5768374 View commit details
  5. 10.0.0

    wooorm committed Feb 20, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    33c31e7 View commit details

Commits on Feb 25, 2025

  1. Remove local use of JSX

    wooorm committed Feb 25, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    21b47b9 View commit details

Commits on Mar 3, 2025

  1. Fix performance around components

    Related-to: GH-895.
    Related-to: GH-883.
    
    Closes GH-893.
    
    Reviewed-by: Christian Murphy <christian.murphy.42@gmail.com>
    Reviewed-by: Titus Wormer <tituswormer@gmail.com>
    remcohaszing authored Mar 3, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    7c17ede View commit details
  2. 10.0.1

    wooorm committed Mar 3, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    2792c32 View commit details
  3. Add lifecycle tests for MarkdownHooks

    Closes GH-894.
    
    Reviewed-by: Christian Murphy <christian.murphy.42@gmail.com>
    Reviewed-by: Titus Wormer <tituswormer@gmail.com>
    remcohaszing authored Mar 3, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    ad7f37f View commit details

Commits on Mar 7, 2025

  1. Fix race condition in MarkdownHooks

    Running asynchronous code inside a `useEffect` causes race conditions.
    This is now handled correctly.
    
    Closes GH-896.
    
    Reviewed-by: Titus Wormer <tituswormer@gmail.com>
    remcohaszing authored Mar 7, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    a40ae2e View commit details
  2. Add fallback prop to MarkdownHooks

    The `<MarkdownHooks/>` component now supports a new prop named
    `fallback`. This fallback is displayed while the initial content hasn’t
    loaded yet. The name `fallback` was chosen to match the same prop from
    `<Suspense/>`.
    
    Closes GH-897.
    
    Reviewed-by: Titus Wormer <tituswormer@gmail.com>
    remcohaszing authored Mar 7, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    939c667 View commit details
  3. Refactor code-style

    wooorm committed Mar 7, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    544bff6 View commit details
  4. Update docs

    wooorm committed Mar 7, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    26fdfe0 View commit details
  5. Refactor docs

    wooorm committed Mar 7, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    f2369cd View commit details
  6. 10.1.0

    wooorm committed Mar 7, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    wooorm Titus
    Copy the full SHA
    44d2e4a View commit details
Showing with 1,486 additions and 979 deletions.
  1. +4 −4 .editorconfig
  2. +6 −6 .github/workflows/bb.yml
  3. +4 −4 .github/workflows/main.yml
  4. +4 −2 .gitignore
  5. +466 −409 changelog.md
  6. +8 −2 index.js
  7. +204 −59 lib/index.js
  8. +1 −1 license
  9. +77 −84 package.json
  10. +337 −288 readme.md
  11. +373 −119 test.jsx
  12. +2 −1 tsconfig.json
8 changes: 4 additions & 4 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
12 changes: 6 additions & 6 deletions .github/workflows/bb.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
name: bb
on:
issues:
types: [opened, reopened, edited, closed, labeled, unlabeled]
pull_request_target:
types: [opened, reopened, edited, closed, labeled, unlabeled]
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: unifiedjs/beep-boop-beta@main
with:
repo-token: ${{secrets.GITHUB_TOKEN}}
name: bb
on:
issues:
types: [closed, edited, labeled, opened, reopened, unlabeled]
pull_request_target:
types: [closed, edited, labeled, opened, reopened, unlabeled]
8 changes: 4 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -3,17 +3,17 @@ jobs:
name: ${{matrix.node}}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{matrix.node}}
- run: npm install
- run: npm test
- uses: codecov/codecov-action@v3
- uses: codecov/codecov-action@v5
strategy:
matrix:
node:
- lts/gallium
- lts/hydrogen
- node
name: main
on:
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
coverage/
node_modules/
*.d.ts
*.log
*.map
*.tsbuildinfo
.DS_Store
coverage/
node_modules/
react-markdown.min.js
yarn.lock
875 changes: 466 additions & 409 deletions changelog.md

Large diffs are not rendered by default.

10 changes: 8 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
/**
* @typedef {import('hast-util-to-jsx-runtime').ExtraProps} ExtraProps
* @typedef {import('./lib/index.js').AllowElement} AllowElement
* @typedef {import('./lib/index.js').Components} Components
* @typedef {import('./lib/index.js').ExtraProps} ExtraProps
* @typedef {import('./lib/index.js').HooksOptions} HooksOptions
* @typedef {import('./lib/index.js').Options} Options
* @typedef {import('./lib/index.js').UrlTransform} UrlTransform
*/

export {Markdown as default, defaultUrlTransform} from './lib/index.js'
export {
MarkdownAsync,
MarkdownHooks,
Markdown as default,
defaultUrlTransform
} from './lib/index.js'
263 changes: 204 additions & 59 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
// Register `Raw` in tree:
/// <reference types="mdast-util-to-hast" />

/**
* @typedef {import('hast').Element} Element
* @typedef {import('hast').ElementContent} ElementContent
* @typedef {import('hast').Nodes} Nodes
* @typedef {import('hast').Parents} Parents
* @typedef {import('hast').Root} Root
* @typedef {import('hast-util-to-jsx-runtime').Components} JsxRuntimeComponents
* @typedef {import('remark-rehype').Options} RemarkRehypeOptions
* @typedef {import('unist-util-visit').BuildVisitor<Root>} Visitor
* @typedef {import('unified').PluggableList} PluggableList
* @import {Element, Nodes, Parents, Root} from 'hast'
* @import {Root as MdastRoot} from 'mdast'
* @import {ComponentType, JSX, ReactElement, ReactNode} from 'react'
* @import {Options as RemarkRehypeOptions} from 'remark-rehype'
* @import {BuildVisitor} from 'unist-util-visit'
* @import {PluggableList, Processor} from 'unified'
*/

/**
@@ -24,10 +18,23 @@
* Parent of `element`.
* @returns {boolean | null | undefined}
* Whether to allow `element` (default: `false`).
*
* @typedef {Partial<JsxRuntimeComponents>} Components
*/

/**
* @typedef ExtraProps
* Extra fields we pass.
* @property {Element | undefined} [node]
* passed when `passNode` is on.
*/

/**
* @typedef {{
* [Key in keyof JSX.IntrinsicElements]?: ComponentType<JSX.IntrinsicElements[Key] & ExtraProps> | keyof JSX.IntrinsicElements
* }} Components
* Map tag names to components.
*
*/

/**
* @typedef Deprecation
* Deprecation.
* @property {string} from
@@ -36,7 +43,9 @@
* ID in readme.
* @property {keyof Options} [to]
* New field.
*
*/

/**
* @typedef Options
* Configuration.
* @property {AllowElement | null | undefined} [allowElement]
@@ -47,8 +56,6 @@
* cannot combine w/ `disallowedElements`.
* @property {string | null | undefined} [children]
* Markdown.
* @property {string | null | undefined} [className]
* Wrap in a `div` with this class name.
* @property {Components | null | undefined} [components]
* Map tag names to components.
* @property {ReadonlyArray<string> | null | undefined} [disallowedElements]
@@ -68,7 +75,22 @@
* with `unwrapDisallowed` the element itself is replaced by its children.
* @property {UrlTransform | null | undefined} [urlTransform]
* Change URLs (default: `defaultUrlTransform`)
*
*/

/**
* @typedef HooksOptionsOnly
* Configuration specifically for {@linkcode MarkdownHooks}.
* @property {ReactNode | null | undefined} [fallback]
* Content to render while the processor processing the markdown (optional).
*/

/**
* @typedef {Options & HooksOptionsOnly} HooksOptions
* Configuration for {@linkcode MarkdownHooks};
* extends the regular {@linkcode Options} with a `fallback` prop.
*/

/**
* @callback UrlTransform
* Transform all URLs.
* @param {string} url
@@ -84,16 +106,14 @@
import {unreachable} from 'devlop'
import {toJsxRuntime} from 'hast-util-to-jsx-runtime'
import {urlAttributes} from 'html-url-attributes'
import {sanitizeUri} from 'micromark-util-sanitize-uri'
// @ts-expect-error: untyped.
import {Fragment, jsx, jsxs} from 'react/jsx-runtime'
import {useEffect, useState} from 'react'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import {unified} from 'unified'
import {visit} from 'unist-util-visit'
import {VFile} from 'vfile'

const own = {}.hasOwnProperty
const changelog =
'https://github.com/remarkjs/react-markdown/blob/main/changelog.md'

@@ -118,6 +138,7 @@ const deprecations = [
id: 'replace-allownode-allowedtypes-and-disallowedtypes',
to: 'allowedElements'
},
{from: 'className', id: 'remove-classname'},
{
from: 'disallowedTypes',
id: 'replace-allownode-allowedtypes-and-disallowedtypes',
@@ -142,33 +163,128 @@ const deprecations = [
/**
* Component to render markdown.
*
* This is a synchronous component.
* When using async plugins,
* see {@linkcode MarkdownAsync} or {@linkcode MarkdownHooks}.
*
* @param {Readonly<Options>} options
* Props.
* @returns {JSX.Element}
* @returns {ReactElement}
* React element.
*/
export function Markdown(options) {
const allowedElements = options.allowedElements
const allowElement = options.allowElement
const children = options.children || ''
const className = options.className
const components = options.components
const disallowedElements = options.disallowedElements
const processor = createProcessor(options)
const file = createFile(options)
return post(processor.runSync(processor.parse(file), file), options)
}

/**
* Component to render markdown with support for async plugins
* through async/await.
*
* Components returning promises are supported on the server.
* For async support on the client,
* see {@linkcode MarkdownHooks}.
*
* @param {Readonly<Options>} options
* Props.
* @returns {Promise<ReactElement>}
* Promise to a React element.
*/
export async function MarkdownAsync(options) {
const processor = createProcessor(options)
const file = createFile(options)
const tree = await processor.run(processor.parse(file), file)
return post(tree, options)
}

/**
* Component to render markdown with support for async plugins through hooks.
*
* This uses `useEffect` and `useState` hooks.
* Hooks run on the client and do not immediately render something.
* For async support on the server,
* see {@linkcode MarkdownAsync}.
*
* @param {Readonly<HooksOptions>} options
* Props.
* @returns {ReactNode}
* React node.
*/
export function MarkdownHooks(options) {
const processor = createProcessor(options)
const [error, setError] = useState(
/** @type {Error | undefined} */ (undefined)
)
const [tree, setTree] = useState(/** @type {Root | undefined} */ (undefined))

useEffect(
function () {
let cancelled = false
const file = createFile(options)

processor.run(processor.parse(file), file, function (error, tree) {
if (!cancelled) {
setError(error)
setTree(tree)
}
})

/**
* @returns {undefined}
* Nothing.
*/
return function () {
cancelled = true
}
},
[
options.children,
options.rehypePlugins,
options.remarkPlugins,
options.remarkRehypeOptions
]
)

if (error) throw error

return tree ? post(tree, options) : options.fallback
}

/**
* Set up the `unified` processor.
*
* @param {Readonly<Options>} options
* Props.
* @returns {Processor<MdastRoot, MdastRoot, Root, undefined, undefined>}
* Result.
*/
function createProcessor(options) {
const rehypePlugins = options.rehypePlugins || emptyPlugins
const remarkPlugins = options.remarkPlugins || emptyPlugins
const remarkRehypeOptions = options.remarkRehypeOptions
? {...options.remarkRehypeOptions, ...emptyRemarkRehypeOptions}
: emptyRemarkRehypeOptions
const skipHtml = options.skipHtml
const unwrapDisallowed = options.unwrapDisallowed
const urlTransform = options.urlTransform || defaultUrlTransform

const processor = unified()
.use(remarkParse)
.use(remarkPlugins)
.use(remarkRehype, remarkRehypeOptions)
.use(rehypePlugins)

return processor
}

/**
* Set up the virtual file.
*
* @param {Readonly<Options>} options
* Props.
* @returns {VFile}
* Result.
*/
function createFile(options) {
const children = options.children || ''
const file = new VFile()

if (typeof children === 'string') {
@@ -181,11 +297,27 @@ export function Markdown(options) {
)
}

if (allowedElements && disallowedElements) {
unreachable(
'Unexpected combined `allowedElements` and `disallowedElements`, expected one or the other'
)
}
return file
}

/**
* 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 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)) {
@@ -205,26 +337,15 @@ export function Markdown(options) {
}
}

const mdastTree = processor.parse(file)
/** @type {Nodes} */
let hastTree = processor.runSync(mdastTree, file)

// Wrap in `div` if there’s a class name.
if (className) {
hastTree = {
type: 'element',
tagName: 'div',
properties: {className},
// Assume no doctypes.
children: /** @type {Array<ElementContent>} */ (
hastTree.type === 'root' ? hastTree.children : [hastTree]
)
}
if (allowedElements && disallowedElements) {
unreachable(
'Unexpected combined `allowedElements` and `disallowedElements`, expected one or the other'
)
}

visit(hastTree, transform)
visit(tree, transform)

return toJsxRuntime(hastTree, {
return toJsxRuntime(tree, {
Fragment,
components,
ignoreInvalidStyle: true,
@@ -234,7 +355,7 @@ export function Markdown(options) {
passNode: true
})

/** @type {Visitor} */
/** @type {BuildVisitor<Root>} */
function transform(node, index, parent) {
if (node.type === 'raw' && parent && typeof index === 'number') {
if (skipHtml) {
@@ -251,7 +372,10 @@ export function Markdown(options) {
let key

for (key in urlAttributes) {
if (own.call(urlAttributes, key) && own.call(node.properties, key)) {
if (
Object.hasOwn(urlAttributes, key) &&
Object.hasOwn(node.properties, key)
) {
const value = node.properties[key]
const test = urlAttributes[key]
if (test === null || test.includes(node.tagName)) {
@@ -265,8 +389,8 @@ export function Markdown(options) {
let remove = allowedElements
? !allowedElements.includes(node.tagName)
: disallowedElements
? disallowedElements.includes(node.tagName)
: false
? disallowedElements.includes(node.tagName)
: false

if (!remove && allowElement && typeof index === 'number') {
remove = !allowElement(node, index, parent)
@@ -295,5 +419,26 @@ export function Markdown(options) {
* Safe URL.
*/
export function defaultUrlTransform(value) {
return sanitizeUri(value, safeProtocol)
// Same as:
// <https://github.com/micromark/micromark/blob/929275e/packages/micromark-util-sanitize-uri/dev/index.js#L34>
// But without the `encode` part.
const colon = value.indexOf(':')
const questionMark = value.indexOf('?')
const numberSign = value.indexOf('#')
const slash = value.indexOf('/')

if (
// If there is no protocol, it’s relative.
colon === -1 ||
// If the first colon is after a `?`, `#`, or `/`, it’s not a protocol.
(slash !== -1 && colon > slash) ||
(questionMark !== -1 && colon > questionMark) ||
(numberSign !== -1 && colon > numberSign) ||
// It is a protocol, it should be allowed.
safeProtocol.test(value.slice(0, colon))
) {
return value
}

return ''
}
2 changes: 1 addition & 1 deletion license
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2015 Espen Hovlandsdal
Copyright (c) Espen Hovlandsdal

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
161 changes: 77 additions & 84 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,123 +1,116 @@
{
"name": "react-markdown",
"version": "9.0.0",
"description": "React component to render markdown",
"license": "MIT",
"keywords": [
"ast",
"commonmark",
"component",
"gfm",
"markdown",
"react",
"react-component",
"remark",
"unified"
],
"repository": "remarkjs/react-markdown",
"bugs": "https://github.com/remarkjs/react-markdown/issues",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
},
"author": "Espen Hovlandsdal <espen@hovlandsdal.com>",
"bugs": "https://github.com/remarkjs/react-markdown/issues",
"contributors": [
"Alexander Wallin <office@alexanderwallin.com>",
"Alexander Wong <admin@alexander-wong.com>",
"André Staltz <andre@staltz.com>",
"Angus MacIsaac <angus.macisaac@busbud.com>",
"Beau Roberts <beau.roberts@autodesk.com>",
"Charlie Chen <doveccl@live.com>",
"Christian Murphy <christian.murphy.42@gmail.com>",
"Christoph Werner <christoph@codepunkt.de>",
"Danny <dannyharding10@gmail.com>",
"Dennis S <denis.s@svsg.co>",
"Espen Hovlandsdal <espen@hovlandsdal.com>",
"Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)",
"Thomas Lindstrøm <t@hom.as>",
"Evan Hensleigh <futuraprime@gmail.com>",
"Fabian Irsara <info@fabianirsara.com>",
"René Kooi <renee@kooi.me>",
"Nicolas Venegas <nvenegas@atlassian.com>",
"Christian Murphy <christian.murphy.42@gmail.com>",
"Linus Unnebäck <linus@folkdatorn.se>",
"Peng Guanwen <pg999w@outlook.com>",
"mudrz <mudrz@outlook.com>",
"Jesse Pinho <jesse@jessepinho.com>",
"Florentin Luca Rieger <florentin.rieger@gmail.com>",
"Frank <frankieali4@gmail.com>",
"Igor Kamyshev <garik.novel@gmail.com>",
"Jack Williams <jsw547@gmail.com>",
"Jakub Chrzanowski <jakub@chrzanowski.info>",
"Jeremy Moseley <jeremy@jeremymoseley.net>",
"Jesse Pinho <jesse@jessepinho.com>",
"Kelvin Chan <kchan@securitycompass.com>",
"Kohei Asai <me@axross.io>",
"Linus Unnebäck <linus@folkdatorn.se>",
"Marshall Smith <marshall@radialdevgroup.com>",
"Nathan Bierema <nbierema@gmail.com>",
"Nicolas Venegas <nvenegas@atlassian.com>",
"Peng Guanwen <pg999w@outlook.com>",
"Petr Gazarov <petrgazarov@gmail.com>",
"Phil Rajchgot <tophil@outlook.com>",
"Rasmus Eneman <rasmus@eneman.eu>",
"René Kooi <renee@kooi.me>",
"Riku Rouvila <riku.rouvila@gmail.com>",
"Robin Wieruch <wrobin@gmx.net>",
"Rostyslav Melnychuk <blackswordgc@gmail.com>",
"Ted Piotrowski <tppiotrowski@gmail.com>",
"Thibaud Courtoison <do.not.press.enter@gmail.com>",
"Thomas Lindstrøm <t@hom.as>",
"Tiago Roldão <focus5.6@gmail.com>",
"Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)",
"cerkiewny <mstarzycki@gmail.com>",
"evoye <rosej@gmx.net>",
"gRoberts84 <gavin@gav-roberts.co.uk>",
"Alexander Wallin <office@alexanderwallin.com>",
"vanchagreen <vanchagreen@gmail.com>",
"Alexander Wong <admin@alexander-wong.com>",
"André Staltz <andre@staltz.com>",
"Angus MacIsaac <angus.macisaac@busbud.com>",
"Beau Roberts <beau.roberts@autodesk.com>",
"Charlie Chen <doveccl@live.com>",
"Christoph Werner <christoph@codepunkt.de>",
"Danny <dannyharding10@gmail.com>",
"Dennis S <denis.s@svsg.co>",
"Evan Hensleigh <futuraprime@gmail.com>"
],
"sideEffects": false,
"type": "module",
"exports": "./index.js",
"files": [
"lib/",
"index.d.ts",
"index.js"
"mudrz <mudrz@outlook.com>",
"vanchagreen <vanchagreen@gmail.com>"
],
"dependencies": {
"@types/hast": "^3.0.0",
"@types/mdast": "^4.0.0",
"devlop": "^1.0.0",
"hast-util-to-jsx-runtime": "^2.0.0",
"html-url-attributes": "^3.0.0",
"mdast-util-to-hast": "^13.0.0",
"micromark-util-sanitize-uri": "^2.0.0",
"remark-parse": "^11.0.0",
"remark-rehype": "^11.0.0",
"unified": "^11.0.0",
"unist-util-visit": "^5.0.0",
"vfile": "^6.0.0"
},
"peerDependencies": {
"@types/react": ">=18",
"react": ">=18"
},
"description": "React component to render markdown",
"devDependencies": {
"@types/node": "^20.0.0",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"c8": "^8.0.0",
"esbuild": "^0.19.0",
"@testing-library/react": "^16.0.0",
"@types/node": "^22.0.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"c8": "^10.0.0",
"concat-stream": "^2.0.0",
"esbuild": "^0.25.0",
"eslint-plugin-react": "^7.0.0",
"global-jsdom": "^26.0.0",
"prettier": "^3.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"rehype-raw": "^7.0.0",
"remark-cli": "^11.0.0",
"rehype-starry-night": "^2.0.0",
"remark-cli": "^12.0.0",
"remark-gfm": "^4.0.0",
"remark-preset-wooorm": "^9.0.0",
"remark-preset-wooorm": "^11.0.0",
"remark-toc": "^9.0.0",
"type-coverage": "^2.0.0",
"typescript": "^5.0.0",
"xo": "^0.56.0"
"xo": "^0.60.0"
},
"scripts": {
"build": "tsc --build --clean && tsc --build && type-coverage",
"format": "remark . --frail --output --quiet && prettier . --log-level warn --write && xo --fix",
"prepack": "npm run build && npm run format",
"test": "npm run build && npm run format && npm run test-coverage",
"test-api": "node --conditions development --experimental-loader=./script/load-jsx.js --no-warnings test.jsx",
"test-coverage": "c8 --100 --exclude script/ --reporter lcov npm run test-api"
"exports": "./index.js",
"files": [
"index.d.ts.map",
"index.d.ts",
"index.js",
"lib/"
],
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
},
"keywords": [
"ast",
"commonmark",
"component",
"gfm",
"markdown",
"react",
"react-component",
"remark",
"unified"
],
"license": "MIT",
"name": "react-markdown",
"peerDependencies": {
"@types/react": ">=18",
"react": ">=18"
},
"prettier": {
"bracketSpacing": false,
@@ -130,28 +123,27 @@
"remarkConfig": {
"plugins": [
"remark-preset-wooorm",
[
"./node_modules/remark-preset-wooorm/node_modules/remark-gfm/index.js",
{
"tablePipeAlign": false
}
],
[
"remark-lint-table-pipe-alignment",
false
],
[
"remark-lint-no-html",
false
]
]
},
"repository": "remarkjs/react-markdown",
"scripts": {
"build": "tsc --build --clean && tsc --build && type-coverage",
"format": "remark --frail --output --quiet -- . && prettier --log-level warn --write -- . && xo --fix",
"test-api": "node --conditions development --experimental-loader=./script/load-jsx.js --no-warnings test.jsx",
"test-coverage": "c8 --100 --exclude script/ --reporter lcov -- npm run test-api",
"test": "npm run build && npm run format && npm run test-coverage"
},
"sideEffects": false,
"typeCoverage": {
"atLeast": 100,
"detail": true,
"ignoreCatch": true,
"strict": true
},
"type": "module",
"version": "10.1.0",
"xo": {
"envs": [
"shared-node-browser"
@@ -170,7 +162,8 @@
"prettier": true,
"rules": {
"complexity": "off",
"n/file-extension-in-import": "off"
"n/file-extension-in-import": "off",
"unicorn/prevent-abbreviations": "off"
}
}
}
625 changes: 337 additions & 288 deletions readme.md

Large diffs are not rendered by default.

492 changes: 373 additions & 119 deletions test.jsx

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
"compilerOptions": {
"checkJs": true,
"customConditions": ["development"],
"declarationMap": true,
"declaration": true,
"emitDeclarationOnly": true,
"exactOptionalPropertyTypes": true,
@@ -12,5 +13,5 @@
"target": "es2022"
},
"exclude": ["coverage/", "node_modules/"],
"include": ["**/*.js", "**/*.jsx", "lib/complex-types.d.ts"]
"include": ["**/*.js", "**/*.jsx"]
}