Initial commit
This commit is contained in:
commit
78f8d225ee
21173 changed files with 2907774 additions and 0 deletions
8
node_modules/react-markdown/index.d.ts
generated
vendored
Normal file
8
node_modules/react-markdown/index.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
export type AllowElement = import("./lib/index.js").AllowElement;
|
||||
export type Components = import("./lib/index.js").Components;
|
||||
export type ExtraProps = import("./lib/index.js").ExtraProps;
|
||||
export type HooksOptions = import("./lib/index.js").HooksOptions;
|
||||
export type Options = import("./lib/index.js").Options;
|
||||
export type UrlTransform = import("./lib/index.js").UrlTransform;
|
||||
export { MarkdownAsync, MarkdownHooks, Markdown as default, defaultUrlTransform } from "./lib/index.js";
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
1
node_modules/react-markdown/index.d.ts.map
generated
vendored
Normal file
1
node_modules/react-markdown/index.d.ts.map
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":"2BACa,OAAO,gBAAgB,EAAE,YAAY;yBACrC,OAAO,gBAAgB,EAAE,UAAU;yBACnC,OAAO,gBAAgB,EAAE,UAAU;2BACnC,OAAO,gBAAgB,EAAE,YAAY;sBACrC,OAAO,gBAAgB,EAAE,OAAO;2BAChC,OAAO,gBAAgB,EAAE,YAAY"}
|
||||
15
node_modules/react-markdown/index.js
generated
vendored
Normal file
15
node_modules/react-markdown/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* @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 {
|
||||
MarkdownAsync,
|
||||
MarkdownHooks,
|
||||
Markdown as default,
|
||||
defaultUrlTransform
|
||||
} from './lib/index.js'
|
||||
166
node_modules/react-markdown/lib/index.d.ts
generated
vendored
Normal file
166
node_modules/react-markdown/lib/index.d.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
/**
|
||||
* 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 {ReactElement}
|
||||
* React element.
|
||||
*/
|
||||
export function Markdown(options: Readonly<Options>): ReactElement;
|
||||
/**
|
||||
* 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 function MarkdownAsync(options: Readonly<Options>): Promise<ReactElement>;
|
||||
/**
|
||||
* 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: Readonly<HooksOptions>): ReactNode;
|
||||
/**
|
||||
* Make a URL safe.
|
||||
*
|
||||
* @satisfies {UrlTransform}
|
||||
* @param {string} value
|
||||
* URL.
|
||||
* @returns {string}
|
||||
* Safe URL.
|
||||
*/
|
||||
export function defaultUrlTransform(value: string): string;
|
||||
/**
|
||||
* Filter elements.
|
||||
*/
|
||||
export type AllowElement = (element: Readonly<Element>, index: number, parent: Readonly<Parents> | undefined) => boolean | null | undefined;
|
||||
/**
|
||||
* Extra fields we pass.
|
||||
*/
|
||||
export type ExtraProps = {
|
||||
/**
|
||||
* passed when `passNode` is on.
|
||||
*/
|
||||
node?: Element | undefined;
|
||||
};
|
||||
/**
|
||||
* Map tag names to components.
|
||||
*/
|
||||
export type Components = { [Key in keyof JSX.IntrinsicElements]?: ComponentType<JSX.IntrinsicElements[Key] & ExtraProps> | keyof JSX.IntrinsicElements; };
|
||||
/**
|
||||
* Deprecation.
|
||||
*/
|
||||
export type Deprecation = {
|
||||
/**
|
||||
* Old field.
|
||||
*/
|
||||
from: string;
|
||||
/**
|
||||
* ID in readme.
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* New field.
|
||||
*/
|
||||
to?: keyof Options;
|
||||
};
|
||||
/**
|
||||
* Configuration.
|
||||
*/
|
||||
export type Options = {
|
||||
/**
|
||||
* Filter elements (optional);
|
||||
* `allowedElements` / `disallowedElements` is used first.
|
||||
*/
|
||||
allowElement?: AllowElement | null | undefined;
|
||||
/**
|
||||
* Tag names to allow (default: all tag names);
|
||||
* cannot combine w/ `disallowedElements`.
|
||||
*/
|
||||
allowedElements?: ReadonlyArray<string> | null | undefined;
|
||||
/**
|
||||
* Markdown.
|
||||
*/
|
||||
children?: string | null | undefined;
|
||||
/**
|
||||
* Map tag names to components.
|
||||
*/
|
||||
components?: Components | null | undefined;
|
||||
/**
|
||||
* Tag names to disallow (default: `[]`);
|
||||
* cannot combine w/ `allowedElements`.
|
||||
*/
|
||||
disallowedElements?: ReadonlyArray<string> | null | undefined;
|
||||
/**
|
||||
* List of rehype plugins to use.
|
||||
*/
|
||||
rehypePlugins?: PluggableList | null | undefined;
|
||||
/**
|
||||
* List of remark plugins to use.
|
||||
*/
|
||||
remarkPlugins?: PluggableList | null | undefined;
|
||||
/**
|
||||
* Options to pass through to `remark-rehype`.
|
||||
*/
|
||||
remarkRehypeOptions?: Readonly<RemarkRehypeOptions> | null | undefined;
|
||||
/**
|
||||
* Ignore HTML in markdown completely (default: `false`).
|
||||
*/
|
||||
skipHtml?: boolean | null | undefined;
|
||||
/**
|
||||
* Extract (unwrap) what’s in disallowed elements (default: `false`);
|
||||
* normally when say `strong` is not allowed, it and it’s children are dropped,
|
||||
* with `unwrapDisallowed` the element itself is replaced by its children.
|
||||
*/
|
||||
unwrapDisallowed?: boolean | null | undefined;
|
||||
/**
|
||||
* Change URLs (default: `defaultUrlTransform`)
|
||||
*/
|
||||
urlTransform?: UrlTransform | null | undefined;
|
||||
};
|
||||
/**
|
||||
* Configuration specifically for {@linkcode MarkdownHooks}.
|
||||
*/
|
||||
export type HooksOptionsOnly = {
|
||||
/**
|
||||
* Content to render while the processor processing the markdown (optional).
|
||||
*/
|
||||
fallback?: ReactNode | null | undefined;
|
||||
};
|
||||
/**
|
||||
* Configuration for {@linkcode MarkdownHooks};
|
||||
* extends the regular {@linkcode Options} with a `fallback` prop.
|
||||
*/
|
||||
export type HooksOptions = Options & HooksOptionsOnly;
|
||||
/**
|
||||
* Transform all URLs.
|
||||
*/
|
||||
export type UrlTransform = (url: string, key: string, node: Readonly<Element>) => string | null | undefined;
|
||||
import type { ReactElement } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { Element } from 'hast';
|
||||
import type { Parents } from 'hast';
|
||||
import type { JSX } from 'react';
|
||||
import type { ComponentType } from 'react';
|
||||
import type { PluggableList } from 'unified';
|
||||
import type { Options as RemarkRehypeOptions } from 'remark-rehype';
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
1
node_modules/react-markdown/lib/index.d.ts.map
generated
vendored
Normal file
1
node_modules/react-markdown/lib/index.d.ts.map
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":"AAkKA;;;;;;;;;;;GAWG;AACH,kCALW,QAAQ,CAAC,OAAO,CAAC,GAEf,YAAY,CAOxB;AAED;;;;;;;;;;;;GAYG;AACH,uCALW,QAAQ,CAAC,OAAO,CAAC,GAEf,OAAO,CAAC,YAAY,CAAC,CAQjC;AAED;;;;;;;;;;;;GAYG;AACH,uCALW,QAAQ,CAAC,YAAY,CAAC,GAEpB,SAAS,CAyCrB;AAgKD;;;;;;;;GAQG;AACH,2CALW,MAAM,GAEJ,MAAM,CA0BlB;;;;qCA/aU,QAAQ,CAAC,OAAO,CAAC,SAEjB,MAAM,UAEN,QAAQ,CAAC,OAAO,CAAC,GAAG,SAAS,KAE3B,OAAO,GAAG,IAAI,GAAG,SAAS;;;;;;;;WAOzB,OAAO,GAAG,SAAS;;;;;yBAKpB,GACP,GAAG,IAAI,MAAM,qBAAqB,CAAC,CAAC,EAAE,cAAc,qBAAqB,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,GAAG,MAAM,qBAAqB,GAC5H;;;;;;;;UAOU,MAAM;;;;QAEN,MAAM;;;;SAEN,MAAM,OAAO;;;;;;;;;;mBAOb,YAAY,GAAG,IAAI,GAAG,SAAS;;;;;sBAG/B,aAAa,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS;;;;eAGxC,MAAM,GAAG,IAAI,GAAG,SAAS;;;;iBAEzB,UAAU,GAAG,IAAI,GAAG,SAAS;;;;;yBAE7B,aAAa,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS;;;;oBAGxC,aAAa,GAAG,IAAI,GAAG,SAAS;;;;oBAEhC,aAAa,GAAG,IAAI,GAAG,SAAS;;;;0BAEhC,QAAQ,CAAC,mBAAmB,CAAC,GAAG,IAAI,GAAG,SAAS;;;;eAEhD,OAAO,GAAG,IAAI,GAAG,SAAS;;;;;;uBAE1B,OAAO,GAAG,IAAI,GAAG,SAAS;;;;mBAI1B,YAAY,GAAG,IAAI,GAAG,SAAS;;;;;;;;;eAO/B,SAAS,GAAG,IAAI,GAAG,SAAS;;;;;;2BAK7B,OAAO,GAAG,gBAAgB;;;;iCAQ5B,MAAM,OAEN,MAAM,QAEN,QAAQ,CAAC,OAAO,CAAC,KAEf,MAAM,GAAG,IAAI,GAAG,SAAS;kCAlGwB,OAAO;+BAAP,OAAO;6BAFrB,MAAM;6BAAN,MAAM;yBAEQ,OAAO;mCAAP,OAAO;mCAG1B,SAAS;oDAFH,eAAe"}
|
||||
444
node_modules/react-markdown/lib/index.js
generated
vendored
Normal file
444
node_modules/react-markdown/lib/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1,444 @@
|
|||
/**
|
||||
* @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'
|
||||
*/
|
||||
|
||||
/**
|
||||
* @callback AllowElement
|
||||
* Filter elements.
|
||||
* @param {Readonly<Element>} element
|
||||
* Element to check.
|
||||
* @param {number} index
|
||||
* Index of `element` in `parent`.
|
||||
* @param {Readonly<Parents> | undefined} parent
|
||||
* Parent of `element`.
|
||||
* @returns {boolean | null | undefined}
|
||||
* Whether to allow `element` (default: `false`).
|
||||
*/
|
||||
|
||||
/**
|
||||
* @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
|
||||
* Old field.
|
||||
* @property {string} id
|
||||
* ID in readme.
|
||||
* @property {keyof Options} [to]
|
||||
* New field.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef Options
|
||||
* Configuration.
|
||||
* @property {AllowElement | null | undefined} [allowElement]
|
||||
* Filter elements (optional);
|
||||
* `allowedElements` / `disallowedElements` is used first.
|
||||
* @property {ReadonlyArray<string> | null | undefined} [allowedElements]
|
||||
* Tag names to allow (default: all tag names);
|
||||
* cannot combine w/ `disallowedElements`.
|
||||
* @property {string | null | undefined} [children]
|
||||
* Markdown.
|
||||
* @property {Components | null | undefined} [components]
|
||||
* Map tag names to components.
|
||||
* @property {ReadonlyArray<string> | null | undefined} [disallowedElements]
|
||||
* Tag names to disallow (default: `[]`);
|
||||
* cannot combine w/ `allowedElements`.
|
||||
* @property {PluggableList | null | undefined} [rehypePlugins]
|
||||
* List of rehype plugins to use.
|
||||
* @property {PluggableList | null | undefined} [remarkPlugins]
|
||||
* List of remark plugins to use.
|
||||
* @property {Readonly<RemarkRehypeOptions> | null | undefined} [remarkRehypeOptions]
|
||||
* Options to pass through to `remark-rehype`.
|
||||
* @property {boolean | null | undefined} [skipHtml=false]
|
||||
* Ignore HTML in markdown completely (default: `false`).
|
||||
* @property {boolean | null | undefined} [unwrapDisallowed=false]
|
||||
* Extract (unwrap) what’s in disallowed elements (default: `false`);
|
||||
* normally when say `strong` is not allowed, it and it’s children are dropped,
|
||||
* 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
|
||||
* URL.
|
||||
* @param {string} key
|
||||
* Property name (example: `'href'`).
|
||||
* @param {Readonly<Element>} node
|
||||
* Node.
|
||||
* @returns {string | null | undefined}
|
||||
* Transformed URL (optional).
|
||||
*/
|
||||
|
||||
import {unreachable} from 'devlop'
|
||||
import {toJsxRuntime} from 'hast-util-to-jsx-runtime'
|
||||
import {urlAttributes} from 'html-url-attributes'
|
||||
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 changelog =
|
||||
'https://github.com/remarkjs/react-markdown/blob/main/changelog.md'
|
||||
|
||||
/** @type {PluggableList} */
|
||||
const emptyPlugins = []
|
||||
/** @type {Readonly<RemarkRehypeOptions>} */
|
||||
const emptyRemarkRehypeOptions = {allowDangerousHtml: true}
|
||||
const safeProtocol = /^(https?|ircs?|mailto|xmpp)$/i
|
||||
|
||||
// Mutable because we `delete` any time it’s used and a message is sent.
|
||||
/** @type {ReadonlyArray<Readonly<Deprecation>>} */
|
||||
const deprecations = [
|
||||
{from: 'astPlugins', id: 'remove-buggy-html-in-markdown-parser'},
|
||||
{from: 'allowDangerousHtml', id: 'remove-buggy-html-in-markdown-parser'},
|
||||
{
|
||||
from: 'allowNode',
|
||||
id: 'replace-allownode-allowedtypes-and-disallowedtypes',
|
||||
to: 'allowElement'
|
||||
},
|
||||
{
|
||||
from: 'allowedTypes',
|
||||
id: 'replace-allownode-allowedtypes-and-disallowedtypes',
|
||||
to: 'allowedElements'
|
||||
},
|
||||
{from: 'className', id: 'remove-classname'},
|
||||
{
|
||||
from: 'disallowedTypes',
|
||||
id: 'replace-allownode-allowedtypes-and-disallowedtypes',
|
||||
to: 'disallowedElements'
|
||||
},
|
||||
{from: 'escapeHtml', id: 'remove-buggy-html-in-markdown-parser'},
|
||||
{from: 'includeElementIndex', id: '#remove-includeelementindex'},
|
||||
{
|
||||
from: 'includeNodeIndex',
|
||||
id: 'change-includenodeindex-to-includeelementindex'
|
||||
},
|
||||
{from: 'linkTarget', id: 'remove-linktarget'},
|
||||
{from: 'plugins', id: 'change-plugins-to-remarkplugins', to: 'remarkPlugins'},
|
||||
{from: 'rawSourcePos', id: '#remove-rawsourcepos'},
|
||||
{from: 'renderers', id: 'change-renderers-to-components', to: 'components'},
|
||||
{from: 'source', id: 'change-source-to-children', to: 'children'},
|
||||
{from: 'sourcePos', id: '#remove-sourcepos'},
|
||||
{from: 'transformImageUri', id: '#add-urltransform', to: 'urlTransform'},
|
||||
{from: 'transformLinkUri', id: '#add-urltransform', to: 'urlTransform'}
|
||||
]
|
||||
|
||||
/**
|
||||
* 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 {ReactElement}
|
||||
* React element.
|
||||
*/
|
||||
export function Markdown(options) {
|
||||
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 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') {
|
||||
file.value = children
|
||||
} else {
|
||||
unreachable(
|
||||
'Unexpected value `' +
|
||||
children +
|
||||
'` for `children` prop, expected `string`'
|
||||
)
|
||||
}
|
||||
|
||||
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)) {
|
||||
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'
|
||||
)
|
||||
}
|
||||
|
||||
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') {
|
||||
if (skipHtml) {
|
||||
parent.children.splice(index, 1)
|
||||
} else {
|
||||
parent.children[index] = {type: 'text', value: node.value}
|
||||
}
|
||||
|
||||
return index
|
||||
}
|
||||
|
||||
if (node.type === 'element') {
|
||||
/** @type {string} */
|
||||
let key
|
||||
|
||||
for (key in urlAttributes) {
|
||||
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)) {
|
||||
node.properties[key] = urlTransform(String(value || ''), key, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (node.type === 'element') {
|
||||
let remove = allowedElements
|
||||
? !allowedElements.includes(node.tagName)
|
||||
: disallowedElements
|
||||
? disallowedElements.includes(node.tagName)
|
||||
: false
|
||||
|
||||
if (!remove && allowElement && typeof index === 'number') {
|
||||
remove = !allowElement(node, index, parent)
|
||||
}
|
||||
|
||||
if (remove && parent && typeof index === 'number') {
|
||||
if (unwrapDisallowed && node.children) {
|
||||
parent.children.splice(index, 1, ...node.children)
|
||||
} else {
|
||||
parent.children.splice(index, 1)
|
||||
}
|
||||
|
||||
return index
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a URL safe.
|
||||
*
|
||||
* @satisfies {UrlTransform}
|
||||
* @param {string} value
|
||||
* URL.
|
||||
* @returns {string}
|
||||
* Safe URL.
|
||||
*/
|
||||
export function defaultUrlTransform(value) {
|
||||
// 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 ''
|
||||
}
|
||||
21
node_modules/react-markdown/license
generated
vendored
Normal file
21
node_modules/react-markdown/license
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
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
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
169
node_modules/react-markdown/package.json
generated
vendored
Normal file
169
node_modules/react-markdown/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
{
|
||||
"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>",
|
||||
"Evan Hensleigh <futuraprime@gmail.com>",
|
||||
"Fabian Irsara <info@fabianirsara.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>",
|
||||
"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",
|
||||
"remark-parse": "^11.0.0",
|
||||
"remark-rehype": "^11.0.0",
|
||||
"unified": "^11.0.0",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"vfile": "^6.0.0"
|
||||
},
|
||||
"description": "React component to render markdown",
|
||||
"devDependencies": {
|
||||
"@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": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"rehype-raw": "^7.0.0",
|
||||
"rehype-starry-night": "^2.0.0",
|
||||
"remark-cli": "^12.0.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-preset-wooorm": "^11.0.0",
|
||||
"remark-toc": "^9.0.0",
|
||||
"type-coverage": "^2.0.0",
|
||||
"typescript": "^5.0.0",
|
||||
"xo": "^0.60.0"
|
||||
},
|
||||
"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,
|
||||
"singleQuote": true,
|
||||
"semi": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "none",
|
||||
"useTabs": false
|
||||
},
|
||||
"remarkConfig": {
|
||||
"plugins": [
|
||||
"remark-preset-wooorm",
|
||||
[
|
||||
"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,
|
||||
"strict": true
|
||||
},
|
||||
"type": "module",
|
||||
"version": "10.1.0",
|
||||
"xo": {
|
||||
"envs": [
|
||||
"shared-node-browser"
|
||||
],
|
||||
"extends": "plugin:react/jsx-runtime",
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"**/*.jsx"
|
||||
],
|
||||
"rules": {
|
||||
"no-unused-vars": "off"
|
||||
}
|
||||
}
|
||||
],
|
||||
"prettier": true,
|
||||
"rules": {
|
||||
"complexity": "off",
|
||||
"n/file-extension-in-import": "off",
|
||||
"unicorn/prevent-abbreviations": "off"
|
||||
}
|
||||
}
|
||||
}
|
||||
940
node_modules/react-markdown/readme.md
generated
vendored
Normal file
940
node_modules/react-markdown/readme.md
generated
vendored
Normal file
|
|
@ -0,0 +1,940 @@
|
|||
<!--
|
||||
Notes for maintaining this document:
|
||||
|
||||
* update the version of the link for `commonmark-html` once in a while
|
||||
-->
|
||||
|
||||
# react-markdown
|
||||
|
||||
[![Build][badge-build-image]][badge-build-url]
|
||||
[![Coverage][badge-coverage-image]][badge-coverage-url]
|
||||
[![Downloads][badge-downloads-image]][badge-downloads-url]
|
||||
[![Size][badge-size-image]][badge-size-url]
|
||||
|
||||
React component to render markdown.
|
||||
|
||||
## Feature highlights
|
||||
|
||||
* [x] **[safe][section-security] by default**
|
||||
(no `dangerouslySetInnerHTML` or XSS attacks)
|
||||
* [x] **[components][section-components]**
|
||||
(pass your own component to use instead of `<h2>` for `## hi`)
|
||||
* [x] **[plugins][section-plugins]**
|
||||
(many plugins you can pick and choose from)
|
||||
* [x] **[compliant][section-syntax]**
|
||||
(100% to CommonMark, 100% to GFM with a plugin)
|
||||
|
||||
## Contents
|
||||
|
||||
* [What is this?](#what-is-this)
|
||||
* [When should I use this?](#when-should-i-use-this)
|
||||
* [Install](#install)
|
||||
* [Use](#use)
|
||||
* [API](#api)
|
||||
* [`Markdown`](#markdown)
|
||||
* [`MarkdownAsync`](#markdownasync)
|
||||
* [`MarkdownHooks`](#markdownhooks)
|
||||
* [`defaultUrlTransform(url)`](#defaulturltransformurl)
|
||||
* [`AllowElement`](#allowelement)
|
||||
* [`Components`](#components)
|
||||
* [`ExtraProps`](#extraprops)
|
||||
* [`HooksOptions`](#hooksoptions)
|
||||
* [`Options`](#options)
|
||||
* [`UrlTransform`](#urltransform)
|
||||
* [Examples](#examples)
|
||||
* [Use a plugin](#use-a-plugin)
|
||||
* [Use a plugin with options](#use-a-plugin-with-options)
|
||||
* [Use custom components (syntax highlight)](#use-custom-components-syntax-highlight)
|
||||
* [Use remark and rehype plugins (math)](#use-remark-and-rehype-plugins-math)
|
||||
* [Plugins](#plugins)
|
||||
* [Syntax](#syntax)
|
||||
* [Compatibility](#compatibility)
|
||||
* [Architecture](#architecture)
|
||||
* [Appendix A: HTML in markdown](#appendix-a-html-in-markdown)
|
||||
* [Appendix B: Components](#appendix-b-components)
|
||||
* [Appendix C: line endings in markdown (and JSX)](#appendix-c-line-endings-in-markdown-and-jsx)
|
||||
* [Security](#security)
|
||||
* [Related](#related)
|
||||
* [Contribute](#contribute)
|
||||
* [License](#license)
|
||||
|
||||
## What is this?
|
||||
|
||||
This package is a [React][] component that can be given a string of markdown
|
||||
that it’ll safely render to React elements.
|
||||
You can pass plugins to change how markdown is transformed and pass components
|
||||
that will be used instead of normal HTML elements.
|
||||
|
||||
* to learn markdown, see this [cheatsheet and tutorial][commonmark-help]
|
||||
* to try out `react-markdown`, see [our demo][github-io-react-markdown]
|
||||
|
||||
## When should I use this?
|
||||
|
||||
There are other ways to use markdown in React out there so why use this one?
|
||||
The three main reasons are that they often rely on `dangerouslySetInnerHTML`,
|
||||
have bugs with how they handle markdown, or don’t let you swap elements for
|
||||
components.
|
||||
`react-markdown` builds a virtual DOM, so React only replaces what changed,
|
||||
from a syntax tree.
|
||||
That’s supported because we use [unified][github-unified],
|
||||
specifically [remark][github-remark] for markdown and [rehype][github-rehype]
|
||||
for HTML,
|
||||
which are popular tools to transform content with plugins.
|
||||
|
||||
This package focusses on making it easy for beginners to safely use markdown in
|
||||
React.
|
||||
When you’re familiar with unified, you can use a modern hooks based alternative
|
||||
[`react-remark`][github-react-remark] or [`rehype-react`][github-rehype-react]
|
||||
manually.
|
||||
If you instead want to use JavaScript and JSX *inside* markdown files, use
|
||||
[MDX][github-mdx].
|
||||
|
||||
## Install
|
||||
|
||||
This package is [ESM only][esm].
|
||||
In Node.js (version 16+), install with [npm][npm-install]:
|
||||
|
||||
```sh
|
||||
npm install react-markdown
|
||||
```
|
||||
|
||||
In Deno with [`esm.sh`][esmsh]:
|
||||
|
||||
```js
|
||||
import Markdown from 'https://esm.sh/react-markdown@10'
|
||||
```
|
||||
|
||||
In browsers with [`esm.sh`][esmsh]:
|
||||
|
||||
```html
|
||||
<script type="module">
|
||||
import Markdown from 'https://esm.sh/react-markdown@10?bundle'
|
||||
</script>
|
||||
```
|
||||
|
||||
## Use
|
||||
|
||||
A basic hello world:
|
||||
|
||||
```js
|
||||
import React from 'react'
|
||||
import {createRoot} from 'react-dom/client'
|
||||
import Markdown from 'react-markdown'
|
||||
|
||||
const markdown = '# Hi, *Pluto*!'
|
||||
|
||||
createRoot(document.body).render(<Markdown>{markdown}</Markdown>)
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Show equivalent JSX</summary>
|
||||
|
||||
```js
|
||||
<h1>
|
||||
Hi, <em>Pluto</em>!
|
||||
</h1>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
Here is an example that shows how to use a plugin
|
||||
([`remark-gfm`][github-remark-gfm],
|
||||
which adds support for footnotes, strikethrough, tables, tasklists and
|
||||
URLs directly):
|
||||
|
||||
```js
|
||||
import React from 'react'
|
||||
import {createRoot} from 'react-dom/client'
|
||||
import Markdown from 'react-markdown'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
|
||||
const markdown = `Just a link: www.nasa.gov.`
|
||||
|
||||
createRoot(document.body).render(
|
||||
<Markdown remarkPlugins={[remarkGfm]}>{markdown}</Markdown>
|
||||
)
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Show equivalent JSX</summary>
|
||||
|
||||
```js
|
||||
<p>
|
||||
Just a link: <a href="http://www.nasa.gov">www.nasa.gov</a>.
|
||||
</p>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## API
|
||||
|
||||
This package exports the identifiers
|
||||
[`MarkdownAsync`][api-markdown-async],
|
||||
[`MarkdownHooks`][api-markdown-hooks],
|
||||
and
|
||||
[`defaultUrlTransform`][api-default-url-transform].
|
||||
The default export is [`Markdown`][api-markdown].
|
||||
|
||||
It also exports the additional [TypeScript][] types
|
||||
[`AllowElement`][api-allow-element],
|
||||
[`Components`][api-components],
|
||||
[`ExtraProps`][api-extra-props],
|
||||
[`HooksOptions`][api-hooks-options],
|
||||
[`Options`][api-options],
|
||||
and
|
||||
[`UrlTransform`][api-url-transform].
|
||||
|
||||
### `Markdown`
|
||||
|
||||
Component to render markdown.
|
||||
|
||||
This is a synchronous component.
|
||||
When using async plugins,
|
||||
see [`MarkdownAsync`][api-markdown-async] or
|
||||
[`MarkdownHooks`][api-markdown-hooks].
|
||||
|
||||
###### Parameters
|
||||
|
||||
* `options` ([`Options`][api-options])
|
||||
— props
|
||||
|
||||
###### Returns
|
||||
|
||||
React element (`ReactElement`).
|
||||
|
||||
### `MarkdownAsync`
|
||||
|
||||
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 [`MarkdownHooks`][api-markdown-hooks].
|
||||
|
||||
###### Parameters
|
||||
|
||||
* `options` ([`Options`][api-options])
|
||||
— props
|
||||
|
||||
###### Returns
|
||||
|
||||
Promise to a React element (`Promise<ReactElement>`).
|
||||
|
||||
### `MarkdownHooks`
|
||||
|
||||
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 [`MarkdownAsync`][api-markdown-async].
|
||||
|
||||
###### Parameters
|
||||
|
||||
* `options` ([`Options`][api-options])
|
||||
— props
|
||||
|
||||
###### Returns
|
||||
|
||||
React node (`ReactNode`).
|
||||
|
||||
### `defaultUrlTransform(url)`
|
||||
|
||||
Make a URL safe.
|
||||
|
||||
###### Parameters
|
||||
|
||||
* `url` (`string`)
|
||||
— URL
|
||||
|
||||
###### Returns
|
||||
|
||||
Safe URL (`string`).
|
||||
|
||||
### `AllowElement`
|
||||
|
||||
Filter elements (TypeScript type).
|
||||
|
||||
###### Parameters
|
||||
|
||||
* `node` ([`Element` from `hast`][github-hast-element])
|
||||
— element to check
|
||||
* `index` (`number | undefined`)
|
||||
— index of `element` in `parent`
|
||||
* `parent` ([`Node` from `hast`][github-hast-nodes])
|
||||
— parent of `element`
|
||||
|
||||
###### Returns
|
||||
|
||||
Whether to allow `element` (`boolean`, optional).
|
||||
|
||||
### `Components`
|
||||
|
||||
Map tag names to components (TypeScript type).
|
||||
|
||||
###### Type
|
||||
|
||||
```ts
|
||||
import type {ExtraProps} from 'react-markdown'
|
||||
import type {ComponentProps, ElementType} from 'react'
|
||||
|
||||
type Components = {
|
||||
[Key in Extract<ElementType, string>]?: ElementType<ComponentProps<Key> & ExtraProps>
|
||||
}
|
||||
```
|
||||
|
||||
### `ExtraProps`
|
||||
|
||||
Extra fields we pass to components (TypeScript type).
|
||||
|
||||
###### Fields
|
||||
|
||||
* `node` ([`Element` from `hast`][github-hast-element], optional)
|
||||
— original node
|
||||
|
||||
### `HooksOptions`
|
||||
|
||||
Configuration for [`MarkdownHooks`][api-markdown-hooks] (TypeScript type);
|
||||
extends the regular [`Options`][api-options] with a `fallback` prop.
|
||||
|
||||
###### Extends
|
||||
|
||||
[`Options`][api-options].
|
||||
|
||||
###### Fields
|
||||
|
||||
* `fallback` (`ReactNode`, optional)
|
||||
— content to render while the processor processing the markdown
|
||||
|
||||
### `Options`
|
||||
|
||||
Configuration (TypeScript type).
|
||||
|
||||
###### Fields
|
||||
|
||||
* `allowElement` ([`AllowElement`][api-allow-element], optional)
|
||||
— filter elements;
|
||||
`allowedElements` / `disallowedElements` is used first
|
||||
* `allowedElements` (`Array<string>`, default: all tag names)
|
||||
— tag names to allow;
|
||||
cannot combine w/ `disallowedElements`
|
||||
* `children` (`string`, optional)
|
||||
— markdown
|
||||
* `components` ([`Components`][api-components], optional)
|
||||
— map tag names to components
|
||||
* `disallowedElements` (`Array<string>`, default: `[]`)
|
||||
— tag names to disallow;
|
||||
cannot combine w/ `allowedElements`
|
||||
* `rehypePlugins` (`Array<Plugin>`, optional)
|
||||
— list of [rehype plugins][github-rehype-plugins] to use
|
||||
* `remarkPlugins` (`Array<Plugin>`, optional)
|
||||
— list of [remark plugins][github-remark-plugins] to use
|
||||
* `remarkRehypeOptions`
|
||||
([`Options` from `remark-rehype`][github-remark-rehype-options],
|
||||
optional)
|
||||
— options to pass through to `remark-rehype`
|
||||
* `skipHtml` (`boolean`, default: `false`)
|
||||
— ignore HTML in markdown completely
|
||||
* `unwrapDisallowed` (`boolean`, default: `false`)
|
||||
— extract (unwrap) what’s in disallowed elements;
|
||||
normally when say `strong` is not allowed, it and it’s children are dropped,
|
||||
with `unwrapDisallowed` the element itself is replaced by its children
|
||||
* `urlTransform` ([`UrlTransform`][api-url-transform], default:
|
||||
[`defaultUrlTransform`][api-default-url-transform])
|
||||
— change URLs
|
||||
|
||||
### `UrlTransform`
|
||||
|
||||
Transform URLs (TypeScript type).
|
||||
|
||||
###### Parameters
|
||||
|
||||
* `url` (`string`)
|
||||
— URL
|
||||
* `key` (`string`, example: `'href'`)
|
||||
— property name
|
||||
* `node` ([`Element` from `hast`][github-hast-element])
|
||||
— element to check
|
||||
|
||||
###### Returns
|
||||
|
||||
Transformed URL (`string`, optional).
|
||||
|
||||
## Examples
|
||||
|
||||
### Use a plugin
|
||||
|
||||
This example shows how to use a remark plugin.
|
||||
In this case, [`remark-gfm`][github-remark-gfm],
|
||||
which adds support for strikethrough, tables, tasklists and URLs directly:
|
||||
|
||||
```js
|
||||
import React from 'react'
|
||||
import {createRoot} from 'react-dom/client'
|
||||
import Markdown from 'react-markdown'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
|
||||
const markdown = `A paragraph with *emphasis* and **strong importance**.
|
||||
|
||||
> A block quote with ~strikethrough~ and a URL: https://reactjs.org.
|
||||
|
||||
* Lists
|
||||
* [ ] todo
|
||||
* [x] done
|
||||
|
||||
A table:
|
||||
|
||||
| a | b |
|
||||
| - | - |
|
||||
`
|
||||
|
||||
createRoot(document.body).render(
|
||||
<Markdown remarkPlugins={[remarkGfm]}>{markdown}</Markdown>
|
||||
)
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Show equivalent JSX</summary>
|
||||
|
||||
```js
|
||||
<>
|
||||
<p>
|
||||
A paragraph with <em>emphasis</em> and <strong>strong importance</strong>.
|
||||
</p>
|
||||
<blockquote>
|
||||
<p>
|
||||
A block quote with <del>strikethrough</del> and a URL:{' '}
|
||||
<a href="https://reactjs.org">https://reactjs.org</a>.
|
||||
</p>
|
||||
</blockquote>
|
||||
<ul className="contains-task-list">
|
||||
<li>Lists</li>
|
||||
<li className="task-list-item">
|
||||
<input type="checkbox" disabled /> todo
|
||||
</li>
|
||||
<li className="task-list-item">
|
||||
<input type="checkbox" disabled checked /> done
|
||||
</li>
|
||||
</ul>
|
||||
<p>A table:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<th>b</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Use a plugin with options
|
||||
|
||||
This example shows how to use a plugin and give it options.
|
||||
To do that, use an array with the plugin at the first place, and the options
|
||||
second.
|
||||
[`remark-gfm`][github-remark-gfm] has an option to allow only double tildes for
|
||||
strikethrough:
|
||||
|
||||
```js
|
||||
import React from 'react'
|
||||
import {createRoot} from 'react-dom/client'
|
||||
import Markdown from 'react-markdown'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
|
||||
const markdown = 'This ~is not~ strikethrough, but ~~this is~~!'
|
||||
|
||||
createRoot(document.body).render(
|
||||
<Markdown remarkPlugins={[[remarkGfm, {singleTilde: false}]]}>
|
||||
{markdown}
|
||||
</Markdown>
|
||||
)
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Show equivalent JSX</summary>
|
||||
|
||||
```js
|
||||
<p>
|
||||
This ~is not~ strikethrough, but <del>this is</del>!
|
||||
</p>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Use custom components (syntax highlight)
|
||||
|
||||
This example shows how you can overwrite the normal handling of an element by
|
||||
passing a component.
|
||||
In this case, we apply syntax highlighting with the seriously super amazing
|
||||
[`react-syntax-highlighter`][github-react-syntax-highlighter] by
|
||||
[**@conorhastings**][github-conorhastings]:
|
||||
|
||||
<!-- To do: currently broken on actual ESM; let’s find an alternative? -->
|
||||
|
||||
```js
|
||||
import React from 'react'
|
||||
import {createRoot} from 'react-dom/client'
|
||||
import Markdown from 'react-markdown'
|
||||
import {Prism as SyntaxHighlighter} from 'react-syntax-highlighter'
|
||||
import {dark} from 'react-syntax-highlighter/dist/esm/styles/prism'
|
||||
|
||||
// Did you know you can use tildes instead of backticks for code in markdown? ✨
|
||||
const markdown = `Here is some JavaScript code:
|
||||
|
||||
~~~js
|
||||
console.log('It works!')
|
||||
~~~
|
||||
`
|
||||
|
||||
createRoot(document.body).render(
|
||||
<Markdown
|
||||
children={markdown}
|
||||
components={{
|
||||
code(props) {
|
||||
const {children, className, node, ...rest} = props
|
||||
const match = /language-(\w+)/.exec(className || '')
|
||||
return match ? (
|
||||
<SyntaxHighlighter
|
||||
{...rest}
|
||||
PreTag="div"
|
||||
children={String(children).replace(/\n$/, '')}
|
||||
language={match[1]}
|
||||
style={dark}
|
||||
/>
|
||||
) : (
|
||||
<code {...rest} className={className}>
|
||||
{children}
|
||||
</code>
|
||||
)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Show equivalent JSX</summary>
|
||||
|
||||
```js
|
||||
<>
|
||||
<p>Here is some JavaScript code:</p>
|
||||
<pre>
|
||||
<SyntaxHighlighter language="js" style={dark} PreTag="div" children="console.log('It works!')" />
|
||||
</pre>
|
||||
</>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Use remark and rehype plugins (math)
|
||||
|
||||
This example shows how a syntax extension
|
||||
(through [`remark-math`][github-remark-math])
|
||||
is used to support math in markdown, and a transform plugin
|
||||
([`rehype-katex`][github-rehype-katex]) to render that math.
|
||||
|
||||
```js
|
||||
import React from 'react'
|
||||
import {createRoot} from 'react-dom/client'
|
||||
import Markdown from 'react-markdown'
|
||||
import rehypeKatex from 'rehype-katex'
|
||||
import remarkMath from 'remark-math'
|
||||
import 'katex/dist/katex.min.css' // `rehype-katex` does not import the CSS for you
|
||||
|
||||
const markdown = `The lift coefficient ($C_L$) is a dimensionless coefficient.`
|
||||
|
||||
createRoot(document.body).render(
|
||||
<Markdown remarkPlugins={[remarkMath]} rehypePlugins={[rehypeKatex]}>
|
||||
{markdown}
|
||||
</Markdown>
|
||||
)
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Show equivalent JSX</summary>
|
||||
|
||||
```js
|
||||
<p>
|
||||
The lift coefficient (
|
||||
<span className="katex">
|
||||
<span className="katex-mathml">
|
||||
<math xmlns="http://www.w3.org/1998/Math/MathML">{/* … */}</math>
|
||||
</span>
|
||||
<span className="katex-html" aria-hidden="true">
|
||||
{/* … */}
|
||||
</span>
|
||||
</span>
|
||||
) is a dimensionless coefficient.
|
||||
</p>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Plugins
|
||||
|
||||
We use [unified][github-unified],
|
||||
specifically [remark][github-remark] for markdown and
|
||||
[rehype][github-rehype] for HTML,
|
||||
which are tools to transform content with plugins.
|
||||
Here are three good ways to find plugins:
|
||||
|
||||
* [`awesome-remark`][github-awesome-remark] and
|
||||
[`awesome-rehype`][github-awesome-rehype]
|
||||
— selection of the most awesome projects
|
||||
* [List of remark plugins][github-remark-plugins] and
|
||||
[list of rehype plugins][github-rehype-plugins]
|
||||
— list of all plugins
|
||||
* [`remark-plugin`][github-topic-remark-plugin] and
|
||||
[`rehype-plugin`][github-topic-rehype-plugin] topics
|
||||
— any tagged repo on GitHub
|
||||
|
||||
## Syntax
|
||||
|
||||
`react-markdown` follows CommonMark, which standardizes the differences between
|
||||
markdown implementations, by default.
|
||||
Some syntax extensions are supported through plugins.
|
||||
|
||||
We use [`micromark`][github-micromark] under the hood for our parsing.
|
||||
See its documentation for more information on markdown, CommonMark, and
|
||||
extensions.
|
||||
|
||||
## Compatibility
|
||||
|
||||
Projects maintained by the unified collective are compatible with maintained
|
||||
versions of Node.js.
|
||||
|
||||
When we cut a new major release, we drop support for unmaintained versions of
|
||||
Node.
|
||||
This means we try to keep the current release line, `react-markdown@10`,
|
||||
compatible with Node.js 16.
|
||||
|
||||
They work in all modern browsers (essentially: everything not IE 11).
|
||||
You can use a bundler (such as esbuild, webpack, or Rollup) to use this package
|
||||
in your project, and use its options (or plugins) to add support for legacy
|
||||
browsers.
|
||||
|
||||
## Architecture
|
||||
|
||||
<pre><code> react-markdown
|
||||
+----------------------------------------------------------------------------------------------------------------+
|
||||
| |
|
||||
| +----------+ +----------------+ +---------------+ +----------------+ +------------+ |
|
||||
| | | | | | | | | | | |
|
||||
<a href="https://commonmark.org">markdown</a>-+->+ <a href="https://github.com/remarkjs/remark">remark</a> +-<a href="https://github.com/syntax-tree/mdast">mdast</a>->+ <a href="https://github.com/remarkjs/remark/blob/main/doc/plugins.md">remark plugins</a> +-<a href="https://github.com/syntax-tree/mdast">mdast</a>->+ <a href="https://github.com/remarkjs/remark-rehype">remark-rehype</a> +-<a href="https://github.com/syntax-tree/hast">hast</a>->+ <a href="https://github.com/rehypejs/rehype/blob/main/doc/plugins.md">rehype plugins</a> +-<a href="https://github.com/syntax-tree/hast">hast</a>->+ <a href="#appendix-b-components">components</a> +-+->react elements
|
||||
| | | | | | | | | | | |
|
||||
| +----------+ +----------------+ +---------------+ +----------------+ +------------+ |
|
||||
| |
|
||||
+----------------------------------------------------------------------------------------------------------------+
|
||||
</code></pre>
|
||||
|
||||
To understand what this project does, it’s important to first understand what
|
||||
unified does: please read through the [`unifiedjs/unified`][github-unified]
|
||||
readme
|
||||
(the part until you hit the API section is required reading).
|
||||
|
||||
`react-markdown` is a unified pipeline — wrapped so that most folks don’t need
|
||||
to directly interact with unified.
|
||||
The processor goes through these steps:
|
||||
|
||||
* parse markdown to mdast (markdown syntax tree)
|
||||
* transform through remark (markdown ecosystem)
|
||||
* transform mdast to hast (HTML syntax tree)
|
||||
* transform through rehype (HTML ecosystem)
|
||||
* render hast to React with components
|
||||
|
||||
## Appendix A: HTML in markdown
|
||||
|
||||
`react-markdown` typically escapes HTML (or ignores it, with `skipHtml`)
|
||||
because it is dangerous and defeats the purpose of this library.
|
||||
|
||||
However, if you are in a trusted environment (you trust the markdown), and
|
||||
can spare the bundle size (±60kb minzipped), then you can use
|
||||
[`rehype-raw`][github-rehype-raw]:
|
||||
|
||||
```js
|
||||
import React from 'react'
|
||||
import {createRoot} from 'react-dom/client'
|
||||
import Markdown from 'react-markdown'
|
||||
import rehypeRaw from 'rehype-raw'
|
||||
|
||||
const markdown = `<div class="note">
|
||||
|
||||
Some *emphasis* and <strong>strong</strong>!
|
||||
|
||||
</div>`
|
||||
|
||||
createRoot(document.body).render(
|
||||
<Markdown rehypePlugins={[rehypeRaw]}>{markdown}</Markdown>
|
||||
)
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Show equivalent JSX</summary>
|
||||
|
||||
```js
|
||||
<div className="note">
|
||||
<p>
|
||||
Some <em>emphasis</em> and <strong>strong</strong>!
|
||||
</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
**Note**: HTML in markdown is still bound by how [HTML works in
|
||||
CommonMark][commonmark-html].
|
||||
Make sure to use blank lines around block-level HTML that again contains
|
||||
markdown!
|
||||
|
||||
## Appendix B: Components
|
||||
|
||||
You can also change the things that come from markdown:
|
||||
|
||||
```js
|
||||
<Markdown
|
||||
components={{
|
||||
// Map `h1` (`# heading`) to use `h2`s.
|
||||
h1: 'h2',
|
||||
// Rewrite `em`s (`*like so*`) to `i` with a red foreground color.
|
||||
em(props) {
|
||||
const {node, ...rest} = props
|
||||
return <i style={{color: 'red'}} {...rest} />
|
||||
}
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
The keys in components are HTML equivalents for the things you write with
|
||||
markdown (such as `h1` for `# heading`).
|
||||
Normally, in markdown, those are: `a`, `blockquote`, `br`, `code`, `em`, `h1`,
|
||||
`h2`, `h3`, `h4`, `h5`, `h6`, `hr`, `img`, `li`, `ol`, `p`, `pre`, `strong`, and
|
||||
`ul`.
|
||||
With [`remark-gfm`][github-remark-gfm],
|
||||
you can also use `del`, `input`, `table`, `tbody`, `td`, `th`, `thead`, and `tr`.
|
||||
Other remark or rehype plugins that add support for new constructs will also
|
||||
work with `react-markdown`.
|
||||
|
||||
The props that are passed are what you probably would expect: an `a` (link) will
|
||||
get `href` (and `title`) props, and `img` (image) an `src`, `alt` and `title`,
|
||||
etc.
|
||||
|
||||
Every component will receive a `node`.
|
||||
This is the original [`Element` from `hast`][github-hast-element] element being
|
||||
turned into a React element.
|
||||
|
||||
## Appendix C: line endings in markdown (and JSX)
|
||||
|
||||
You might have trouble with how line endings work in markdown and JSX.
|
||||
We recommend the following, which solves all line ending problems:
|
||||
|
||||
```js
|
||||
// If you write actual markdown in your code, put your markdown in a variable;
|
||||
// **do not indent markdown**:
|
||||
const markdown = `
|
||||
# This is perfect!
|
||||
`
|
||||
|
||||
// Pass the value as an expression as an only child:
|
||||
const result = <Markdown>{markdown}</Markdown>
|
||||
```
|
||||
|
||||
👆 That works.
|
||||
Read on for what doesn’t and why that is.
|
||||
|
||||
You might try to write markdown directly in your JSX and find that it **does
|
||||
not** work:
|
||||
|
||||
```js
|
||||
<Markdown>
|
||||
# Hi
|
||||
|
||||
This is **not** a paragraph.
|
||||
</Markdown>
|
||||
```
|
||||
|
||||
The is because in JSX the whitespace (including line endings) is collapsed to
|
||||
a single space.
|
||||
So the above example is equivalent to:
|
||||
|
||||
```js
|
||||
<Markdown> # Hi This is **not** a paragraph. </Markdown>
|
||||
```
|
||||
|
||||
Instead, to pass markdown to `Markdown`, you can use an expression:
|
||||
with a template literal:
|
||||
|
||||
```js
|
||||
<Markdown>{`
|
||||
# Hi
|
||||
|
||||
This is a paragraph.
|
||||
`}</Markdown>
|
||||
```
|
||||
|
||||
Template literals have another potential problem, because they keep whitespace
|
||||
(including indentation) inside them.
|
||||
That means that the following **does not** turn into a heading:
|
||||
|
||||
```js
|
||||
<Markdown>{`
|
||||
# This is **not** a heading, it’s an indented code block
|
||||
`}</Markdown>
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
Use of `react-markdown` is secure by default.
|
||||
Overwriting `urlTransform` to something insecure will open you up to XSS
|
||||
vectors.
|
||||
Furthermore, the `remarkPlugins`, `rehypePlugins`, and `components` you use may
|
||||
be insecure.
|
||||
|
||||
To make sure the content is completely safe, even after what plugins do,
|
||||
use [`rehype-sanitize`][github-rehype-sanitize].
|
||||
It lets you define your own schema of what is and isn’t allowed.
|
||||
|
||||
## Related
|
||||
|
||||
* [`MDX`][github-mdx]
|
||||
— JSX *in* markdown
|
||||
* [`remark-gfm`][github-remark-gfm]
|
||||
— add support for GitHub flavored markdown support
|
||||
* [`react-remark`][github-react-remark]
|
||||
— hook based alternative
|
||||
* [`rehype-react`][github-rehype-react]
|
||||
— turn HTML into React elements
|
||||
|
||||
## Contribute
|
||||
|
||||
See [`contributing.md`][health-contributing] in [`remarkjs/.github`][health]
|
||||
for ways to get started.
|
||||
See [`support.md`][health-support] for ways to get help.
|
||||
|
||||
This project has a [code of conduct][health-coc].
|
||||
By interacting with this repository, organization, or community you agree to
|
||||
abide by its terms.
|
||||
|
||||
## License
|
||||
|
||||
[MIT][file-license] © [Espen Hovlandsdal][author]
|
||||
|
||||
[api-allow-element]: #allowelement
|
||||
|
||||
[api-components]: #components
|
||||
|
||||
[api-default-url-transform]: #defaulturltransformurl
|
||||
|
||||
[api-extra-props]: #extraprops
|
||||
|
||||
[api-hooks-options]: #hooksoptions
|
||||
|
||||
[api-markdown]: #markdown
|
||||
|
||||
[api-markdown-async]: #markdownasync
|
||||
|
||||
[api-markdown-hooks]: #markdownhooks
|
||||
|
||||
[api-options]: #options
|
||||
|
||||
[api-url-transform]: #urltransform
|
||||
|
||||
[author]: https://espen.codes/
|
||||
|
||||
[badge-build-image]: https://github.com/remarkjs/react-markdown/workflows/main/badge.svg
|
||||
|
||||
[badge-build-url]: https://github.com/remarkjs/react-markdown/actions
|
||||
|
||||
[badge-coverage-image]: https://img.shields.io/codecov/c/github/remarkjs/react-markdown.svg
|
||||
|
||||
[badge-coverage-url]: https://codecov.io/github/remarkjs/react-markdown
|
||||
|
||||
[badge-downloads-image]: https://img.shields.io/npm/dm/react-markdown.svg
|
||||
|
||||
[badge-downloads-url]: https://www.npmjs.com/package/react-markdown
|
||||
|
||||
[badge-size-image]: https://img.shields.io/bundlejs/size/react-markdown
|
||||
|
||||
[badge-size-url]: https://bundlejs.com/?q=react-markdown
|
||||
|
||||
[commonmark-help]: https://commonmark.org/help/
|
||||
|
||||
[commonmark-html]: https://spec.commonmark.org/0.31.2/#html-blocks
|
||||
|
||||
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
|
||||
|
||||
[esmsh]: https://esm.sh
|
||||
|
||||
[file-license]: license
|
||||
|
||||
[github-awesome-rehype]: https://github.com/rehypejs/awesome-rehype
|
||||
|
||||
[github-awesome-remark]: https://github.com/remarkjs/awesome-remark
|
||||
|
||||
[github-conorhastings]: https://github.com/conorhastings
|
||||
|
||||
[github-hast-element]: https://github.com/syntax-tree/hast#element
|
||||
|
||||
[github-hast-nodes]: https://github.com/syntax-tree/hast#nodes
|
||||
|
||||
[github-io-react-markdown]: https://remarkjs.github.io/react-markdown/
|
||||
|
||||
[github-mdx]: https://github.com/mdx-js/mdx/
|
||||
|
||||
[github-micromark]: https://github.com/micromark/micromark
|
||||
|
||||
[github-react-remark]: https://github.com/remarkjs/react-remark
|
||||
|
||||
[github-react-syntax-highlighter]: https://github.com/react-syntax-highlighter/react-syntax-highlighter
|
||||
|
||||
[github-rehype]: https://github.com/rehypejs/rehype
|
||||
|
||||
[github-rehype-katex]: https://github.com/remarkjs/remark-math/tree/main/packages/rehype-katex
|
||||
|
||||
[github-rehype-plugins]: https://github.com/rehypejs/rehype/blob/main/doc/plugins.md#list-of-plugins
|
||||
|
||||
[github-rehype-raw]: https://github.com/rehypejs/rehype-raw
|
||||
|
||||
[github-rehype-react]: https://github.com/rehypejs/rehype-react
|
||||
|
||||
[github-rehype-sanitize]: https://github.com/rehypejs/rehype-sanitize
|
||||
|
||||
[github-remark]: https://github.com/remarkjs/remark
|
||||
|
||||
[github-remark-gfm]: https://github.com/remarkjs/remark-gfm
|
||||
|
||||
[github-remark-math]: https://github.com/remarkjs/remark-math
|
||||
|
||||
[github-remark-plugins]: https://github.com/remarkjs/remark/blob/main/doc/plugins.md#list-of-plugins
|
||||
|
||||
[github-remark-rehype-options]: https://github.com/remarkjs/remark-rehype#options
|
||||
|
||||
[github-topic-rehype-plugin]: https://github.com/topics/rehype-plugin
|
||||
|
||||
[github-topic-remark-plugin]: https://github.com/topics/remark-plugin
|
||||
|
||||
[github-unified]: https://github.com/unifiedjs/unified
|
||||
|
||||
[health]: https://github.com/remarkjs/.github
|
||||
|
||||
[health-coc]: https://github.com/remarkjs/.github/blob/main/code-of-conduct.md
|
||||
|
||||
[health-contributing]: https://github.com/remarkjs/.github/blob/main/contributing.md
|
||||
|
||||
[health-support]: https://github.com/remarkjs/.github/blob/main/support.md
|
||||
|
||||
[npm-install]: https://docs.npmjs.com/cli/install
|
||||
|
||||
[react]: http://reactjs.org
|
||||
|
||||
[section-components]: #appendix-b-components
|
||||
|
||||
[section-plugins]: #plugins
|
||||
|
||||
[section-security]: #security
|
||||
|
||||
[section-syntax]: #syntax
|
||||
|
||||
[typescript]: https://www.typescriptlang.org
|
||||
Loading…
Add table
Add a link
Reference in a new issue