This commit is contained in:
Iliyan Angelov
2025-09-14 23:24:25 +03:00
commit c67067a2a4
71311 changed files with 6800714 additions and 0 deletions

4
frontend/node_modules/react-markdown/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
export {uriTransformer} from './lib/uri-transformer.js'
export {ReactMarkdown as default} from './lib/react-markdown.js'
export type Options = import('./lib/react-markdown.js').ReactMarkdownOptions
export type Components = import('./lib/ast-to-react.js').Components

8
frontend/node_modules/react-markdown/index.js generated vendored Normal file
View File

@@ -0,0 +1,8 @@
/**
* @typedef {import('./lib/react-markdown.js').ReactMarkdownOptions} Options
* @typedef {import('./lib/ast-to-react.js').Components} Components
*/
export {uriTransformer} from './lib/uri-transformer.js'
export {ReactMarkdown as default} from './lib/react-markdown.js'

View File

@@ -0,0 +1,134 @@
/**
* @param {Context} context
* @param {Element|Root} node
*/
export function childrenToReact(
context: Context,
node: Element | Root
): React.ReactNode[]
/**
* <T>
*/
export type ComponentType<T> = import('react').ComponentType<T>
/**
* <T>
*/
export type ComponentPropsWithoutRef<T extends React.ElementType<any>> =
import('react').ComponentPropsWithoutRef<T>
export type ReactNode = import('react').ReactNode
export type Position = import('unist').Position
export type Element = import('hast').Element
export type ElementContent = import('hast').ElementContent
export type Root = import('hast').Root
export type Text = import('hast').Text
export type Comment = import('hast').Comment
export type Doctype = import('hast').DocType
export type Info = import('property-information').Info
export type Schema = import('property-information').Schema
export type ReactMarkdownProps = import('./complex-types.js').ReactMarkdownProps
export type Raw = {
type: 'raw'
value: string
}
export type Context = {
options: Options
schema: Schema
listDepth: number
}
export type TransformLink = (
href: string,
children: Array<ElementContent>,
title: string | null
) => string
export type TransformImage = (
src: string,
alt: string,
title: string | null
) => string
export type TransformLinkTargetType = import('react').HTMLAttributeAnchorTarget
export type TransformLinkTarget = (
href: string,
children: Array<ElementContent>,
title: string | null
) => TransformLinkTargetType | undefined
/**
* To do: is `data-sourcepos` typeable?
*/
export type ReactMarkdownNames = keyof JSX.IntrinsicElements
export type CodeProps = ComponentPropsWithoutRef<'code'> &
ReactMarkdownProps & {
inline?: boolean
}
export type HeadingProps = ComponentPropsWithoutRef<'h1'> &
ReactMarkdownProps & {
level: number
}
export type LiProps = ComponentPropsWithoutRef<'li'> &
ReactMarkdownProps & {
checked: boolean | null
index: number
ordered: boolean
}
export type OrderedListProps = ComponentPropsWithoutRef<'ol'> &
ReactMarkdownProps & {
depth: number
ordered: true
}
export type TableDataCellProps = ComponentPropsWithoutRef<'td'> &
ReactMarkdownProps & {
style?: Record<string, unknown>
isHeader: false
}
export type TableHeaderCellProps = ComponentPropsWithoutRef<'th'> &
ReactMarkdownProps & {
style?: Record<string, unknown>
isHeader: true
}
export type TableRowProps = ComponentPropsWithoutRef<'tr'> &
ReactMarkdownProps & {
isHeader: boolean
}
export type UnorderedListProps = ComponentPropsWithoutRef<'ul'> &
ReactMarkdownProps & {
depth: number
ordered: false
}
export type CodeComponent = ComponentType<CodeProps>
export type HeadingComponent = ComponentType<HeadingProps>
export type LiComponent = ComponentType<LiProps>
export type OrderedListComponent = ComponentType<OrderedListProps>
export type TableDataCellComponent = ComponentType<TableDataCellProps>
export type TableHeaderCellComponent = ComponentType<TableHeaderCellProps>
export type TableRowComponent = ComponentType<TableRowProps>
export type UnorderedListComponent = ComponentType<UnorderedListProps>
export type SpecialComponents = {
code: CodeComponent | ReactMarkdownNames
h1: HeadingComponent | ReactMarkdownNames
h2: HeadingComponent | ReactMarkdownNames
h3: HeadingComponent | ReactMarkdownNames
h4: HeadingComponent | ReactMarkdownNames
h5: HeadingComponent | ReactMarkdownNames
h6: HeadingComponent | ReactMarkdownNames
li: LiComponent | ReactMarkdownNames
ol: OrderedListComponent | ReactMarkdownNames
td: TableDataCellComponent | ReactMarkdownNames
th: TableHeaderCellComponent | ReactMarkdownNames
tr: TableRowComponent | ReactMarkdownNames
ul: UnorderedListComponent | ReactMarkdownNames
}
export type Components = Partial<
Omit<import('./complex-types.js').NormalComponents, keyof SpecialComponents> &
SpecialComponents
>
export type Options = {
sourcePos?: boolean
rawSourcePos?: boolean
skipHtml?: boolean
includeElementIndex?: boolean
transformLinkUri?: null | false | TransformLink
transformImageUri?: TransformImage
linkTarget?: TransformLinkTargetType | TransformLinkTarget
components?: Components
}
import React from 'react'
import style from 'style-to-object'

View File

@@ -0,0 +1,451 @@
/**
* @template T
* @typedef {import('react').ComponentType<T>} ComponentType<T>
*/
/**
* @template {import('react').ElementType} T
* @typedef {import('react').ComponentPropsWithoutRef<T>} ComponentPropsWithoutRef<T>
*/
/**
* @typedef {import('react').ReactNode} ReactNode
* @typedef {import('unist').Position} Position
* @typedef {import('hast').Element} Element
* @typedef {import('hast').ElementContent} ElementContent
* @typedef {import('hast').Root} Root
* @typedef {import('hast').Text} Text
* @typedef {import('hast').Comment} Comment
* @typedef {import('hast').DocType} Doctype
* @typedef {import('property-information').Info} Info
* @typedef {import('property-information').Schema} Schema
* @typedef {import('./complex-types.js').ReactMarkdownProps} ReactMarkdownProps
*
* @typedef Raw
* @property {'raw'} type
* @property {string} value
*
* @typedef Context
* @property {Options} options
* @property {Schema} schema
* @property {number} listDepth
*
* @callback TransformLink
* @param {string} href
* @param {Array<ElementContent>} children
* @param {string?} title
* @returns {string}
*
* @callback TransformImage
* @param {string} src
* @param {string} alt
* @param {string?} title
* @returns {string}
*
* @typedef {import('react').HTMLAttributeAnchorTarget} TransformLinkTargetType
*
* @callback TransformLinkTarget
* @param {string} href
* @param {Array<ElementContent>} children
* @param {string?} title
* @returns {TransformLinkTargetType|undefined}
*
* @typedef {keyof JSX.IntrinsicElements} ReactMarkdownNames
*
* To do: is `data-sourcepos` typeable?
*
* @typedef {ComponentPropsWithoutRef<'code'> & ReactMarkdownProps & {inline?: boolean}} CodeProps
* @typedef {ComponentPropsWithoutRef<'h1'> & ReactMarkdownProps & {level: number}} HeadingProps
* @typedef {ComponentPropsWithoutRef<'li'> & ReactMarkdownProps & {checked: boolean|null, index: number, ordered: boolean}} LiProps
* @typedef {ComponentPropsWithoutRef<'ol'> & ReactMarkdownProps & {depth: number, ordered: true}} OrderedListProps
* @typedef {ComponentPropsWithoutRef<'td'> & ReactMarkdownProps & {style?: Record<string, unknown>, isHeader: false}} TableDataCellProps
* @typedef {ComponentPropsWithoutRef<'th'> & ReactMarkdownProps & {style?: Record<string, unknown>, isHeader: true}} TableHeaderCellProps
* @typedef {ComponentPropsWithoutRef<'tr'> & ReactMarkdownProps & {isHeader: boolean}} TableRowProps
* @typedef {ComponentPropsWithoutRef<'ul'> & ReactMarkdownProps & {depth: number, ordered: false}} UnorderedListProps
*
* @typedef {ComponentType<CodeProps>} CodeComponent
* @typedef {ComponentType<HeadingProps>} HeadingComponent
* @typedef {ComponentType<LiProps>} LiComponent
* @typedef {ComponentType<OrderedListProps>} OrderedListComponent
* @typedef {ComponentType<TableDataCellProps>} TableDataCellComponent
* @typedef {ComponentType<TableHeaderCellProps>} TableHeaderCellComponent
* @typedef {ComponentType<TableRowProps>} TableRowComponent
* @typedef {ComponentType<UnorderedListProps>} UnorderedListComponent
*
* @typedef SpecialComponents
* @property {CodeComponent|ReactMarkdownNames} code
* @property {HeadingComponent|ReactMarkdownNames} h1
* @property {HeadingComponent|ReactMarkdownNames} h2
* @property {HeadingComponent|ReactMarkdownNames} h3
* @property {HeadingComponent|ReactMarkdownNames} h4
* @property {HeadingComponent|ReactMarkdownNames} h5
* @property {HeadingComponent|ReactMarkdownNames} h6
* @property {LiComponent|ReactMarkdownNames} li
* @property {OrderedListComponent|ReactMarkdownNames} ol
* @property {TableDataCellComponent|ReactMarkdownNames} td
* @property {TableHeaderCellComponent|ReactMarkdownNames} th
* @property {TableRowComponent|ReactMarkdownNames} tr
* @property {UnorderedListComponent|ReactMarkdownNames} ul
*
* @typedef {Partial<Omit<import('./complex-types.js').NormalComponents, keyof SpecialComponents> & SpecialComponents>} Components
*
* @typedef Options
* @property {boolean} [sourcePos=false]
* @property {boolean} [rawSourcePos=false]
* @property {boolean} [skipHtml=false]
* @property {boolean} [includeElementIndex=false]
* @property {null|false|TransformLink} [transformLinkUri]
* @property {TransformImage} [transformImageUri]
* @property {TransformLinkTargetType|TransformLinkTarget} [linkTarget]
* @property {Components} [components]
*/
import React from 'react'
import ReactIs from 'react-is'
import {whitespace} from 'hast-util-whitespace'
import {svg, find, hastToReact} from 'property-information'
import {stringify as spaces} from 'space-separated-tokens'
import {stringify as commas} from 'comma-separated-tokens'
import style from 'style-to-object'
import {uriTransformer} from './uri-transformer.js'
const own = {}.hasOwnProperty
// The table-related elements that must not contain whitespace text according
// to React.
const tableElements = new Set(['table', 'thead', 'tbody', 'tfoot', 'tr'])
/**
* @param {Context} context
* @param {Element|Root} node
*/
export function childrenToReact(context, node) {
/** @type {Array<ReactNode>} */
const children = []
let childIndex = -1
/** @type {Comment|Doctype|Element|Raw|Text} */
let child
while (++childIndex < node.children.length) {
child = node.children[childIndex]
if (child.type === 'element') {
children.push(toReact(context, child, childIndex, node))
} else if (child.type === 'text') {
// Currently, a warning is triggered by react for *any* white space in
// tables.
// So we drop it.
// See: <https://github.com/facebook/react/pull/7081>.
// See: <https://github.com/facebook/react/pull/7515>.
// See: <https://github.com/remarkjs/remark-react/issues/64>.
// See: <https://github.com/remarkjs/react-markdown/issues/576>.
if (
node.type !== 'element' ||
!tableElements.has(node.tagName) ||
!whitespace(child)
) {
children.push(child.value)
}
} else if (child.type === 'raw' && !context.options.skipHtml) {
// Default behavior is to show (encoded) HTML.
children.push(child.value)
}
}
return children
}
/**
* @param {Context} context
* @param {Element} node
* @param {number} index
* @param {Element|Root} parent
*/
function toReact(context, node, index, parent) {
const options = context.options
const transform =
options.transformLinkUri === undefined
? uriTransformer
: options.transformLinkUri
const parentSchema = context.schema
/** @type {ReactMarkdownNames} */
// @ts-expect-error assume a known HTML/SVG element.
const name = node.tagName
/** @type {Record<string, unknown>} */
const properties = {}
let schema = parentSchema
/** @type {string} */
let property
if (parentSchema.space === 'html' && name === 'svg') {
schema = svg
context.schema = schema
}
if (node.properties) {
for (property in node.properties) {
if (own.call(node.properties, property)) {
addProperty(properties, property, node.properties[property], context)
}
}
}
if (name === 'ol' || name === 'ul') {
context.listDepth++
}
const children = childrenToReact(context, node)
if (name === 'ol' || name === 'ul') {
context.listDepth--
}
// Restore parent schema.
context.schema = parentSchema
// Nodes created by plugins do not have positional info, in which case we use
// an object that matches the position interface.
const position = node.position || {
start: {line: null, column: null, offset: null},
end: {line: null, column: null, offset: null}
}
const component =
options.components && own.call(options.components, name)
? options.components[name]
: name
const basic = typeof component === 'string' || component === React.Fragment
if (!ReactIs.isValidElementType(component)) {
throw new TypeError(
`Component for name \`${name}\` not defined or is not renderable`
)
}
properties.key = index
if (name === 'a' && options.linkTarget) {
properties.target =
typeof options.linkTarget === 'function'
? options.linkTarget(
String(properties.href || ''),
node.children,
typeof properties.title === 'string' ? properties.title : null
)
: options.linkTarget
}
if (name === 'a' && transform) {
properties.href = transform(
String(properties.href || ''),
node.children,
typeof properties.title === 'string' ? properties.title : null
)
}
if (
!basic &&
name === 'code' &&
parent.type === 'element' &&
parent.tagName !== 'pre'
) {
properties.inline = true
}
if (
!basic &&
(name === 'h1' ||
name === 'h2' ||
name === 'h3' ||
name === 'h4' ||
name === 'h5' ||
name === 'h6')
) {
properties.level = Number.parseInt(name.charAt(1), 10)
}
if (name === 'img' && options.transformImageUri) {
properties.src = options.transformImageUri(
String(properties.src || ''),
String(properties.alt || ''),
typeof properties.title === 'string' ? properties.title : null
)
}
if (!basic && name === 'li' && parent.type === 'element') {
const input = getInputElement(node)
properties.checked =
input && input.properties ? Boolean(input.properties.checked) : null
properties.index = getElementsBeforeCount(parent, node)
properties.ordered = parent.tagName === 'ol'
}
if (!basic && (name === 'ol' || name === 'ul')) {
properties.ordered = name === 'ol'
properties.depth = context.listDepth
}
if (name === 'td' || name === 'th') {
if (properties.align) {
if (!properties.style) properties.style = {}
// @ts-expect-error assume `style` is an object
properties.style.textAlign = properties.align
delete properties.align
}
if (!basic) {
properties.isHeader = name === 'th'
}
}
if (!basic && name === 'tr' && parent.type === 'element') {
properties.isHeader = Boolean(parent.tagName === 'thead')
}
// If `sourcePos` is given, pass source information (line/column info from markdown source).
if (options.sourcePos) {
properties['data-sourcepos'] = flattenPosition(position)
}
if (!basic && options.rawSourcePos) {
properties.sourcePosition = node.position
}
// If `includeElementIndex` is given, pass node index info to components.
if (!basic && options.includeElementIndex) {
properties.index = getElementsBeforeCount(parent, node)
properties.siblingCount = getElementsBeforeCount(parent)
}
if (!basic) {
properties.node = node
}
// Ensure no React warnings are emitted for void elements w/ children.
return children.length > 0
? React.createElement(component, properties, children)
: React.createElement(component, properties)
}
/**
* @param {Element|Root} node
* @returns {Element?}
*/
function getInputElement(node) {
let index = -1
while (++index < node.children.length) {
const child = node.children[index]
if (child.type === 'element' && child.tagName === 'input') {
return child
}
}
return null
}
/**
* @param {Element|Root} parent
* @param {Element} [node]
* @returns {number}
*/
function getElementsBeforeCount(parent, node) {
let index = -1
let count = 0
while (++index < parent.children.length) {
if (parent.children[index] === node) break
if (parent.children[index].type === 'element') count++
}
return count
}
/**
* @param {Record<string, unknown>} props
* @param {string} prop
* @param {unknown} value
* @param {Context} ctx
*/
function addProperty(props, prop, value, ctx) {
const info = find(ctx.schema, prop)
let result = value
// Ignore nullish and `NaN` values.
// eslint-disable-next-line no-self-compare
if (result === null || result === undefined || result !== result) {
return
}
// Accept `array`.
// Most props are space-separated.
if (Array.isArray(result)) {
result = info.commaSeparated ? commas(result) : spaces(result)
}
if (info.property === 'style' && typeof result === 'string') {
result = parseStyle(result)
}
if (info.space && info.property) {
props[
own.call(hastToReact, info.property)
? hastToReact[info.property]
: info.property
] = result
} else if (info.attribute) {
props[info.attribute] = result
}
}
/**
* @param {string} value
* @returns {Record<string, string>}
*/
function parseStyle(value) {
/** @type {Record<string, string>} */
const result = {}
try {
style(value, iterator)
} catch {
// Silent.
}
return result
/**
* @param {string} name
* @param {string} v
*/
function iterator(name, v) {
const k = name.slice(0, 4) === '-ms-' ? `ms-${name.slice(4)}` : name
result[k.replace(/-([a-z])/g, styleReplacer)] = v
}
}
/**
* @param {unknown} _
* @param {string} $1
*/
function styleReplacer(_, $1) {
return $1.toUpperCase()
}
/**
* @param {Position|{start: {line: null, column: null, offset: null}, end: {line: null, column: null, offset: null}}} pos
* @returns {string}
*/
function flattenPosition(pos) {
return [
pos.start.line,
':',
pos.start.column,
'-',
pos.end.line,
':',
pos.end.column
]
.map(String)
.join('')
}

View File

@@ -0,0 +1,24 @@
import type {ReactNode, ComponentType, ComponentPropsWithoutRef} from 'react'
import type {Position} from 'unist'
import type {Element} from 'hast'
export type ReactMarkdownProps = {
node: Element
children: ReactNode[]
/**
* Passed when `options.rawSourcePos` is given
*/
sourcePosition?: Position
/**
* Passed when `options.includeElementIndex` is given
*/
index?: number
/**
* Passed when `options.includeElementIndex` is given
*/
siblingCount?: number
}
export type NormalComponents = {
[TagName in keyof JSX.IntrinsicElements]:
| keyof JSX.IntrinsicElements
| ComponentType<ComponentPropsWithoutRef<TagName> & ReactMarkdownProps>
}

View File

@@ -0,0 +1,28 @@
import type {ReactNode, ComponentType, ComponentPropsWithoutRef} from 'react'
import type {Position} from 'unist'
import type {Element} from 'hast'
/* File for types which are not handled correctly in JSDoc mode */
export type ReactMarkdownProps = {
node: Element
children: ReactNode[]
/**
* Passed when `options.rawSourcePos` is given
*/
sourcePosition?: Position
/**
* Passed when `options.includeElementIndex` is given
*/
index?: number
/**
* Passed when `options.includeElementIndex` is given
*/
siblingCount?: number
}
export type NormalComponents = {
[TagName in keyof JSX.IntrinsicElements]:
| keyof JSX.IntrinsicElements
| ComponentType<ComponentPropsWithoutRef<TagName> & ReactMarkdownProps>
}

View File

@@ -0,0 +1,60 @@
/**
* React component to render markdown.
*
* @param {ReactMarkdownOptions} options
* @returns {ReactElement}
*/
export function ReactMarkdown(options: ReactMarkdownOptions): ReactElement
export namespace ReactMarkdown {
namespace propTypes {
const children: PropTypes.Requireable<string>
const className: PropTypes.Requireable<string>
const allowElement: PropTypes.Requireable<(...args: any[]) => any>
const allowedElements: PropTypes.Requireable<(string | null | undefined)[]>
const disallowedElements: PropTypes.Requireable<
(string | null | undefined)[]
>
const unwrapDisallowed: PropTypes.Requireable<boolean>
const remarkPlugins: PropTypes.Requireable<(object | null | undefined)[]>
const rehypePlugins: PropTypes.Requireable<(object | null | undefined)[]>
const sourcePos: PropTypes.Requireable<boolean>
const rawSourcePos: PropTypes.Requireable<boolean>
const skipHtml: PropTypes.Requireable<boolean>
const includeElementIndex: PropTypes.Requireable<boolean>
const transformLinkUri: PropTypes.Requireable<
NonNullable<boolean | ((...args: any[]) => any) | null | undefined>
>
const linkTarget: PropTypes.Requireable<
NonNullable<string | ((...args: any[]) => any) | null | undefined>
>
const transformImageUri: PropTypes.Requireable<(...args: any[]) => any>
const components: PropTypes.Requireable<object>
}
}
export type ReactNode = import('react').ReactNode
export type ReactElement = import('react').ReactElement<{}>
export type PluggableList = import('unified').PluggableList
export type Root = import('hast').Root
export type FilterOptions = import('./rehype-filter.js').Options
export type TransformOptions = import('./ast-to-react.js').Options
export type CoreOptions = {
children: string
}
export type PluginOptions = {
remarkPlugins?: import('unified').PluggableList
rehypePlugins?: import('unified').PluggableList
remarkRehypeOptions?: import('remark-rehype').Options | undefined
}
export type LayoutOptions = {
className?: string
}
export type ReactMarkdownOptions = CoreOptions &
PluginOptions &
LayoutOptions &
FilterOptions &
TransformOptions
export type Deprecation = {
id: string
to?: string
}
import PropTypes from 'prop-types'

View File

@@ -0,0 +1,184 @@
/**
* @typedef {import('react').ReactNode} ReactNode
* @typedef {import('react').ReactElement<{}>} ReactElement
* @typedef {import('unified').PluggableList} PluggableList
* @typedef {import('hast').Root} Root
* @typedef {import('./rehype-filter.js').Options} FilterOptions
* @typedef {import('./ast-to-react.js').Options} TransformOptions
*
* @typedef CoreOptions
* @property {string} children
*
* @typedef PluginOptions
* @property {PluggableList} [remarkPlugins=[]]
* @property {PluggableList} [rehypePlugins=[]]
* @property {import('remark-rehype').Options | undefined} [remarkRehypeOptions={}]
*
* @typedef LayoutOptions
* @property {string} [className]
*
* @typedef {CoreOptions & PluginOptions & LayoutOptions & FilterOptions & TransformOptions} ReactMarkdownOptions
*
* @typedef Deprecation
* @property {string} id
* @property {string} [to]
*/
import React from 'react'
import {VFile} from 'vfile'
import {unified} from 'unified'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import PropTypes from 'prop-types'
import {html} from 'property-information'
import rehypeFilter from './rehype-filter.js'
import {childrenToReact} from './ast-to-react.js'
const own = {}.hasOwnProperty
const changelog =
'https://github.com/remarkjs/react-markdown/blob/main/changelog.md'
/** @type {Record<string, Deprecation>} */
const deprecated = {
plugins: {to: 'remarkPlugins', id: 'change-plugins-to-remarkplugins'},
renderers: {to: 'components', id: 'change-renderers-to-components'},
astPlugins: {id: 'remove-buggy-html-in-markdown-parser'},
allowDangerousHtml: {id: 'remove-buggy-html-in-markdown-parser'},
escapeHtml: {id: 'remove-buggy-html-in-markdown-parser'},
source: {to: 'children', id: 'change-source-to-children'},
allowNode: {
to: 'allowElement',
id: 'replace-allownode-allowedtypes-and-disallowedtypes'
},
allowedTypes: {
to: 'allowedElements',
id: 'replace-allownode-allowedtypes-and-disallowedtypes'
},
disallowedTypes: {
to: 'disallowedElements',
id: 'replace-allownode-allowedtypes-and-disallowedtypes'
},
includeNodeIndex: {
to: 'includeElementIndex',
id: 'change-includenodeindex-to-includeelementindex'
}
}
/**
* React component to render markdown.
*
* @param {ReactMarkdownOptions} options
* @returns {ReactElement}
*/
export function ReactMarkdown(options) {
for (const key in deprecated) {
if (own.call(deprecated, key) && own.call(options, key)) {
const deprecation = deprecated[key]
console.warn(
`[react-markdown] Warning: please ${
deprecation.to ? `use \`${deprecation.to}\` instead of` : 'remove'
} \`${key}\` (see <${changelog}#${deprecation.id}> for more info)`
)
delete deprecated[key]
}
}
const processor = unified()
.use(remarkParse)
.use(options.remarkPlugins || [])
.use(remarkRehype, {
...options.remarkRehypeOptions,
allowDangerousHtml: true
})
.use(options.rehypePlugins || [])
.use(rehypeFilter, options)
const file = new VFile()
if (typeof options.children === 'string') {
file.value = options.children
} else if (options.children !== undefined && options.children !== null) {
console.warn(
`[react-markdown] Warning: please pass a string as \`children\` (not: \`${options.children}\`)`
)
}
const hastNode = processor.runSync(processor.parse(file), file)
if (hastNode.type !== 'root') {
throw new TypeError('Expected a `root` node')
}
/** @type {ReactElement} */
let result = React.createElement(
React.Fragment,
{},
childrenToReact({options, schema: html, listDepth: 0}, hastNode)
)
if (options.className) {
result = React.createElement('div', {className: options.className}, result)
}
return result
}
ReactMarkdown.propTypes = {
// Core options:
children: PropTypes.string,
// Layout options:
className: PropTypes.string,
// Filter options:
allowElement: PropTypes.func,
allowedElements: PropTypes.arrayOf(PropTypes.string),
disallowedElements: PropTypes.arrayOf(PropTypes.string),
unwrapDisallowed: PropTypes.bool,
// Plugin options:
remarkPlugins: PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.object,
PropTypes.func,
PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.bool,
PropTypes.string,
PropTypes.object,
PropTypes.func,
PropTypes.arrayOf(
// prettier-ignore
// type-coverage:ignore-next-line
PropTypes.any
)
])
)
])
),
rehypePlugins: PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.object,
PropTypes.func,
PropTypes.arrayOf(
PropTypes.oneOfType([
PropTypes.bool,
PropTypes.string,
PropTypes.object,
PropTypes.func,
PropTypes.arrayOf(
// prettier-ignore
// type-coverage:ignore-next-line
PropTypes.any
)
])
)
])
),
// Transform options:
sourcePos: PropTypes.bool,
rawSourcePos: PropTypes.bool,
skipHtml: PropTypes.bool,
includeElementIndex: PropTypes.bool,
transformLinkUri: PropTypes.oneOfType([PropTypes.func, PropTypes.bool]),
linkTarget: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
transformImageUri: PropTypes.func,
components: PropTypes.object
}

View File

@@ -0,0 +1,20 @@
export default function rehypeFilter(
this: import('unified').Processor<void, import('hast').Root, void, void>,
settings_0: Options
):
| void
| import('unified').Transformer<import('hast').Root, import('hast').Root>
export type Node = import('unist').Node
export type Root = import('hast').Root
export type Element = import('hast').Element
export type AllowElement = (
element: Element,
index: number,
parent: Element | Root
) => boolean | undefined
export type Options = {
allowedElements?: Array<string>
disallowedElements?: Array<string>
allowElement?: AllowElement
unwrapDisallowed?: boolean
}

View File

@@ -0,0 +1,66 @@
import {visit} from 'unist-util-visit'
/**
* @typedef {import('unist').Node} Node
* @typedef {import('hast').Root} Root
* @typedef {import('hast').Element} Element
*
* @callback AllowElement
* @param {Element} element
* @param {number} index
* @param {Element|Root} parent
* @returns {boolean|undefined}
*
* @typedef Options
* @property {Array<string>} [allowedElements]
* @property {Array<string>} [disallowedElements=[]]
* @property {AllowElement} [allowElement]
* @property {boolean} [unwrapDisallowed=false]
*/
/**
* @type {import('unified').Plugin<[Options], Root>}
*/
export default function rehypeFilter(options) {
if (options.allowedElements && options.disallowedElements) {
throw new TypeError(
'Only one of `allowedElements` and `disallowedElements` should be defined'
)
}
if (
options.allowedElements ||
options.disallowedElements ||
options.allowElement
) {
return (tree) => {
visit(tree, 'element', (node, index, parent_) => {
const parent = /** @type {Element|Root} */ (parent_)
/** @type {boolean|undefined} */
let remove
if (options.allowedElements) {
remove = !options.allowedElements.includes(node.tagName)
} else if (options.disallowedElements) {
remove = options.disallowedElements.includes(node.tagName)
}
if (!remove && options.allowElement && typeof index === 'number') {
remove = !options.allowElement(node, index, parent)
}
if (remove && typeof index === 'number') {
if (options.unwrapDisallowed && node.children) {
parent.children.splice(index, 1, ...node.children)
} else {
parent.children.splice(index, 1)
}
return index
}
return undefined
})
}
}
}

View File

@@ -0,0 +1,5 @@
/**
* @param {string} uri
* @returns {string}
*/
export function uriTransformer(uri: string): string

View File

@@ -0,0 +1,45 @@
const protocols = ['http', 'https', 'mailto', 'tel']
/**
* @param {string} uri
* @returns {string}
*/
export function uriTransformer(uri) {
const url = (uri || '').trim()
const first = url.charAt(0)
if (first === '#' || first === '/') {
return url
}
const colon = url.indexOf(':')
if (colon === -1) {
return url
}
let index = -1
while (++index < protocols.length) {
const protocol = protocols[index]
if (
colon === protocol.length &&
url.slice(0, protocol.length).toLowerCase() === protocol
) {
return url
}
}
index = url.indexOf('?')
if (index !== -1 && colon > index) {
return url
}
index = url.indexOf('#')
if (index !== -1 && colon > index) {
return url
}
// eslint-disable-next-line no-script-url
return 'javascript:void(0)'
}

21
frontend/node_modules/react-markdown/license generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 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.

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) Facebook, Inc. and its affiliates.
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.

View File

@@ -0,0 +1,104 @@
# `react-is`
This package allows you to test arbitrary values and see if they're a particular React element type.
## Installation
```sh
# Yarn
yarn add react-is
# NPM
npm install react-is
```
## Usage
### Determining if a Component is Valid
```js
import React from "react";
import * as ReactIs from "react-is";
class ClassComponent extends React.Component {
render() {
return React.createElement("div");
}
}
const FunctionComponent = () => React.createElement("div");
const ForwardRefComponent = React.forwardRef((props, ref) =>
React.createElement(Component, { forwardedRef: ref, ...props })
);
const Context = React.createContext(false);
ReactIs.isValidElementType("div"); // true
ReactIs.isValidElementType(ClassComponent); // true
ReactIs.isValidElementType(FunctionComponent); // true
ReactIs.isValidElementType(ForwardRefComponent); // true
ReactIs.isValidElementType(Context.Provider); // true
ReactIs.isValidElementType(Context.Consumer); // true
ReactIs.isValidElementType(React.createFactory("div")); // true
```
### Determining an Element's Type
#### Context
```js
import React from "react";
import * as ReactIs from 'react-is';
const ThemeContext = React.createContext("blue");
ReactIs.isContextConsumer(<ThemeContext.Consumer />); // true
ReactIs.isContextProvider(<ThemeContext.Provider />); // true
ReactIs.typeOf(<ThemeContext.Provider />) === ReactIs.ContextProvider; // true
ReactIs.typeOf(<ThemeContext.Consumer />) === ReactIs.ContextConsumer; // true
```
#### Element
```js
import React from "react";
import * as ReactIs from 'react-is';
ReactIs.isElement(<div />); // true
ReactIs.typeOf(<div />) === ReactIs.Element; // true
```
#### Fragment
```js
import React from "react";
import * as ReactIs from 'react-is';
ReactIs.isFragment(<></>); // true
ReactIs.typeOf(<></>) === ReactIs.Fragment; // true
```
#### Portal
```js
import React from "react";
import ReactDOM from "react-dom";
import * as ReactIs from 'react-is';
const div = document.createElement("div");
const portal = ReactDOM.createPortal(<div />, div);
ReactIs.isPortal(portal); // true
ReactIs.typeOf(portal) === ReactIs.Portal; // true
```
#### StrictMode
```js
import React from "react";
import * as ReactIs from 'react-is';
ReactIs.isStrictMode(<React.StrictMode />); // true
ReactIs.typeOf(<React.StrictMode />) === ReactIs.StrictMode; // true
```

View File

@@ -0,0 +1,221 @@
/**
* @license React
* react-is.development.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
if (process.env.NODE_ENV !== "production") {
(function() {
'use strict';
// ATTENTION
// When adding new symbols to this file,
// Please consider also adding to 'react-devtools-shared/src/backend/ReactSymbols'
// The Symbol used to tag the ReactElement-like types.
var REACT_ELEMENT_TYPE = Symbol.for('react.element');
var REACT_PORTAL_TYPE = Symbol.for('react.portal');
var REACT_FRAGMENT_TYPE = Symbol.for('react.fragment');
var REACT_STRICT_MODE_TYPE = Symbol.for('react.strict_mode');
var REACT_PROFILER_TYPE = Symbol.for('react.profiler');
var REACT_PROVIDER_TYPE = Symbol.for('react.provider');
var REACT_CONTEXT_TYPE = Symbol.for('react.context');
var REACT_SERVER_CONTEXT_TYPE = Symbol.for('react.server_context');
var REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref');
var REACT_SUSPENSE_TYPE = Symbol.for('react.suspense');
var REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list');
var REACT_MEMO_TYPE = Symbol.for('react.memo');
var REACT_LAZY_TYPE = Symbol.for('react.lazy');
var REACT_OFFSCREEN_TYPE = Symbol.for('react.offscreen');
// -----------------------------------------------------------------------------
var enableScopeAPI = false; // Experimental Create Event Handle API.
var enableCacheElement = false;
var enableTransitionTracing = false; // No known bugs, but needs performance testing
var enableLegacyHidden = false; // Enables unstable_avoidThisFallback feature in Fiber
// stuff. Intended to enable React core members to more easily debug scheduling
// issues in DEV builds.
var enableDebugTracing = false; // Track which Fiber(s) schedule render work.
var REACT_MODULE_REFERENCE;
{
REACT_MODULE_REFERENCE = Symbol.for('react.module.reference');
}
function isValidElementType(type) {
if (typeof type === 'string' || typeof type === 'function') {
return true;
} // Note: typeof might be other than 'symbol' or 'number' (e.g. if it's a polyfill).
if (type === REACT_FRAGMENT_TYPE || type === REACT_PROFILER_TYPE || enableDebugTracing || type === REACT_STRICT_MODE_TYPE || type === REACT_SUSPENSE_TYPE || type === REACT_SUSPENSE_LIST_TYPE || enableLegacyHidden || type === REACT_OFFSCREEN_TYPE || enableScopeAPI || enableCacheElement || enableTransitionTracing ) {
return true;
}
if (typeof type === 'object' && type !== null) {
if (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE || // This needs to include all possible module reference object
// types supported by any Flight configuration anywhere since
// we don't know which Flight build this will end up being used
// with.
type.$$typeof === REACT_MODULE_REFERENCE || type.getModuleId !== undefined) {
return true;
}
}
return false;
}
function typeOf(object) {
if (typeof object === 'object' && object !== null) {
var $$typeof = object.$$typeof;
switch ($$typeof) {
case REACT_ELEMENT_TYPE:
var type = object.type;
switch (type) {
case REACT_FRAGMENT_TYPE:
case REACT_PROFILER_TYPE:
case REACT_STRICT_MODE_TYPE:
case REACT_SUSPENSE_TYPE:
case REACT_SUSPENSE_LIST_TYPE:
return type;
default:
var $$typeofType = type && type.$$typeof;
switch ($$typeofType) {
case REACT_SERVER_CONTEXT_TYPE:
case REACT_CONTEXT_TYPE:
case REACT_FORWARD_REF_TYPE:
case REACT_LAZY_TYPE:
case REACT_MEMO_TYPE:
case REACT_PROVIDER_TYPE:
return $$typeofType;
default:
return $$typeof;
}
}
case REACT_PORTAL_TYPE:
return $$typeof;
}
}
return undefined;
}
var ContextConsumer = REACT_CONTEXT_TYPE;
var ContextProvider = REACT_PROVIDER_TYPE;
var Element = REACT_ELEMENT_TYPE;
var ForwardRef = REACT_FORWARD_REF_TYPE;
var Fragment = REACT_FRAGMENT_TYPE;
var Lazy = REACT_LAZY_TYPE;
var Memo = REACT_MEMO_TYPE;
var Portal = REACT_PORTAL_TYPE;
var Profiler = REACT_PROFILER_TYPE;
var StrictMode = REACT_STRICT_MODE_TYPE;
var Suspense = REACT_SUSPENSE_TYPE;
var SuspenseList = REACT_SUSPENSE_LIST_TYPE;
var hasWarnedAboutDeprecatedIsAsyncMode = false;
var hasWarnedAboutDeprecatedIsConcurrentMode = false; // AsyncMode should be deprecated
function isAsyncMode(object) {
{
if (!hasWarnedAboutDeprecatedIsAsyncMode) {
hasWarnedAboutDeprecatedIsAsyncMode = true; // Using console['warn'] to evade Babel and ESLint
console['warn']('The ReactIs.isAsyncMode() alias has been deprecated, ' + 'and will be removed in React 18+.');
}
}
return false;
}
function isConcurrentMode(object) {
{
if (!hasWarnedAboutDeprecatedIsConcurrentMode) {
hasWarnedAboutDeprecatedIsConcurrentMode = true; // Using console['warn'] to evade Babel and ESLint
console['warn']('The ReactIs.isConcurrentMode() alias has been deprecated, ' + 'and will be removed in React 18+.');
}
}
return false;
}
function isContextConsumer(object) {
return typeOf(object) === REACT_CONTEXT_TYPE;
}
function isContextProvider(object) {
return typeOf(object) === REACT_PROVIDER_TYPE;
}
function isElement(object) {
return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
}
function isForwardRef(object) {
return typeOf(object) === REACT_FORWARD_REF_TYPE;
}
function isFragment(object) {
return typeOf(object) === REACT_FRAGMENT_TYPE;
}
function isLazy(object) {
return typeOf(object) === REACT_LAZY_TYPE;
}
function isMemo(object) {
return typeOf(object) === REACT_MEMO_TYPE;
}
function isPortal(object) {
return typeOf(object) === REACT_PORTAL_TYPE;
}
function isProfiler(object) {
return typeOf(object) === REACT_PROFILER_TYPE;
}
function isStrictMode(object) {
return typeOf(object) === REACT_STRICT_MODE_TYPE;
}
function isSuspense(object) {
return typeOf(object) === REACT_SUSPENSE_TYPE;
}
function isSuspenseList(object) {
return typeOf(object) === REACT_SUSPENSE_LIST_TYPE;
}
exports.ContextConsumer = ContextConsumer;
exports.ContextProvider = ContextProvider;
exports.Element = Element;
exports.ForwardRef = ForwardRef;
exports.Fragment = Fragment;
exports.Lazy = Lazy;
exports.Memo = Memo;
exports.Portal = Portal;
exports.Profiler = Profiler;
exports.StrictMode = StrictMode;
exports.Suspense = Suspense;
exports.SuspenseList = SuspenseList;
exports.isAsyncMode = isAsyncMode;
exports.isConcurrentMode = isConcurrentMode;
exports.isContextConsumer = isContextConsumer;
exports.isContextProvider = isContextProvider;
exports.isElement = isElement;
exports.isForwardRef = isForwardRef;
exports.isFragment = isFragment;
exports.isLazy = isLazy;
exports.isMemo = isMemo;
exports.isPortal = isPortal;
exports.isProfiler = isProfiler;
exports.isStrictMode = isStrictMode;
exports.isSuspense = isSuspense;
exports.isSuspenseList = isSuspenseList;
exports.isValidElementType = isValidElementType;
exports.typeOf = typeOf;
})();
}

View File

@@ -0,0 +1,14 @@
/**
* @license React
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';var b=Symbol.for("react.element"),c=Symbol.for("react.portal"),d=Symbol.for("react.fragment"),e=Symbol.for("react.strict_mode"),f=Symbol.for("react.profiler"),g=Symbol.for("react.provider"),h=Symbol.for("react.context"),k=Symbol.for("react.server_context"),l=Symbol.for("react.forward_ref"),m=Symbol.for("react.suspense"),n=Symbol.for("react.suspense_list"),p=Symbol.for("react.memo"),q=Symbol.for("react.lazy"),t=Symbol.for("react.offscreen"),u;u=Symbol.for("react.module.reference");
function v(a){if("object"===typeof a&&null!==a){var r=a.$$typeof;switch(r){case b:switch(a=a.type,a){case d:case f:case e:case m:case n:return a;default:switch(a=a&&a.$$typeof,a){case k:case h:case l:case q:case p:case g:return a;default:return r}}case c:return r}}}exports.ContextConsumer=h;exports.ContextProvider=g;exports.Element=b;exports.ForwardRef=l;exports.Fragment=d;exports.Lazy=q;exports.Memo=p;exports.Portal=c;exports.Profiler=f;exports.StrictMode=e;exports.Suspense=m;
exports.SuspenseList=n;exports.isAsyncMode=function(){return!1};exports.isConcurrentMode=function(){return!1};exports.isContextConsumer=function(a){return v(a)===h};exports.isContextProvider=function(a){return v(a)===g};exports.isElement=function(a){return"object"===typeof a&&null!==a&&a.$$typeof===b};exports.isForwardRef=function(a){return v(a)===l};exports.isFragment=function(a){return v(a)===d};exports.isLazy=function(a){return v(a)===q};exports.isMemo=function(a){return v(a)===p};
exports.isPortal=function(a){return v(a)===c};exports.isProfiler=function(a){return v(a)===f};exports.isStrictMode=function(a){return v(a)===e};exports.isSuspense=function(a){return v(a)===m};exports.isSuspenseList=function(a){return v(a)===n};
exports.isValidElementType=function(a){return"string"===typeof a||"function"===typeof a||a===d||a===f||a===e||a===m||a===n||a===t||"object"===typeof a&&null!==a&&(a.$$typeof===q||a.$$typeof===p||a.$$typeof===g||a.$$typeof===h||a.$$typeof===l||a.$$typeof===u||void 0!==a.getModuleId)?!0:!1};exports.typeOf=v;

View File

@@ -0,0 +1,7 @@
'use strict';
if (process.env.NODE_ENV === 'production') {
module.exports = require('./cjs/react-is.production.min.js');
} else {
module.exports = require('./cjs/react-is.development.js');
}

View File

@@ -0,0 +1,26 @@
{
"name": "react-is",
"version": "18.3.1",
"description": "Brand checking of React Elements.",
"main": "index.js",
"repository": {
"type": "git",
"url": "https://github.com/facebook/react.git",
"directory": "packages/react-is"
},
"keywords": [
"react"
],
"license": "MIT",
"bugs": {
"url": "https://github.com/facebook/react/issues"
},
"homepage": "https://reactjs.org/",
"files": [
"LICENSE",
"README.md",
"index.js",
"cjs/",
"umd/"
]
}

View File

@@ -0,0 +1,220 @@
/**
* @license React
* react-is.development.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = global || self, factory(global.ReactIs = {}));
}(this, (function (exports) { 'use strict';
// ATTENTION
// When adding new symbols to this file,
// Please consider also adding to 'react-devtools-shared/src/backend/ReactSymbols'
// The Symbol used to tag the ReactElement-like types.
var REACT_ELEMENT_TYPE = Symbol.for('react.element');
var REACT_PORTAL_TYPE = Symbol.for('react.portal');
var REACT_FRAGMENT_TYPE = Symbol.for('react.fragment');
var REACT_STRICT_MODE_TYPE = Symbol.for('react.strict_mode');
var REACT_PROFILER_TYPE = Symbol.for('react.profiler');
var REACT_PROVIDER_TYPE = Symbol.for('react.provider');
var REACT_CONTEXT_TYPE = Symbol.for('react.context');
var REACT_SERVER_CONTEXT_TYPE = Symbol.for('react.server_context');
var REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref');
var REACT_SUSPENSE_TYPE = Symbol.for('react.suspense');
var REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list');
var REACT_MEMO_TYPE = Symbol.for('react.memo');
var REACT_LAZY_TYPE = Symbol.for('react.lazy');
var REACT_OFFSCREEN_TYPE = Symbol.for('react.offscreen');
// -----------------------------------------------------------------------------
var enableScopeAPI = false; // Experimental Create Event Handle API.
var enableCacheElement = false;
var enableTransitionTracing = false; // No known bugs, but needs performance testing
var enableLegacyHidden = false; // Enables unstable_avoidThisFallback feature in Fiber
// stuff. Intended to enable React core members to more easily debug scheduling
// issues in DEV builds.
var enableDebugTracing = false; // Track which Fiber(s) schedule render work.
var REACT_MODULE_REFERENCE;
{
REACT_MODULE_REFERENCE = Symbol.for('react.module.reference');
}
function isValidElementType(type) {
if (typeof type === 'string' || typeof type === 'function') {
return true;
} // Note: typeof might be other than 'symbol' or 'number' (e.g. if it's a polyfill).
if (type === REACT_FRAGMENT_TYPE || type === REACT_PROFILER_TYPE || enableDebugTracing || type === REACT_STRICT_MODE_TYPE || type === REACT_SUSPENSE_TYPE || type === REACT_SUSPENSE_LIST_TYPE || enableLegacyHidden || type === REACT_OFFSCREEN_TYPE || enableScopeAPI || enableCacheElement || enableTransitionTracing ) {
return true;
}
if (typeof type === 'object' && type !== null) {
if (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE || // This needs to include all possible module reference object
// types supported by any Flight configuration anywhere since
// we don't know which Flight build this will end up being used
// with.
type.$$typeof === REACT_MODULE_REFERENCE || type.getModuleId !== undefined) {
return true;
}
}
return false;
}
function typeOf(object) {
if (typeof object === 'object' && object !== null) {
var $$typeof = object.$$typeof;
switch ($$typeof) {
case REACT_ELEMENT_TYPE:
var type = object.type;
switch (type) {
case REACT_FRAGMENT_TYPE:
case REACT_PROFILER_TYPE:
case REACT_STRICT_MODE_TYPE:
case REACT_SUSPENSE_TYPE:
case REACT_SUSPENSE_LIST_TYPE:
return type;
default:
var $$typeofType = type && type.$$typeof;
switch ($$typeofType) {
case REACT_SERVER_CONTEXT_TYPE:
case REACT_CONTEXT_TYPE:
case REACT_FORWARD_REF_TYPE:
case REACT_LAZY_TYPE:
case REACT_MEMO_TYPE:
case REACT_PROVIDER_TYPE:
return $$typeofType;
default:
return $$typeof;
}
}
case REACT_PORTAL_TYPE:
return $$typeof;
}
}
return undefined;
}
var ContextConsumer = REACT_CONTEXT_TYPE;
var ContextProvider = REACT_PROVIDER_TYPE;
var Element = REACT_ELEMENT_TYPE;
var ForwardRef = REACT_FORWARD_REF_TYPE;
var Fragment = REACT_FRAGMENT_TYPE;
var Lazy = REACT_LAZY_TYPE;
var Memo = REACT_MEMO_TYPE;
var Portal = REACT_PORTAL_TYPE;
var Profiler = REACT_PROFILER_TYPE;
var StrictMode = REACT_STRICT_MODE_TYPE;
var Suspense = REACT_SUSPENSE_TYPE;
var SuspenseList = REACT_SUSPENSE_LIST_TYPE;
var hasWarnedAboutDeprecatedIsAsyncMode = false;
var hasWarnedAboutDeprecatedIsConcurrentMode = false; // AsyncMode should be deprecated
function isAsyncMode(object) {
{
if (!hasWarnedAboutDeprecatedIsAsyncMode) {
hasWarnedAboutDeprecatedIsAsyncMode = true; // Using console['warn'] to evade Babel and ESLint
console['warn']('The ReactIs.isAsyncMode() alias has been deprecated, ' + 'and will be removed in React 18+.');
}
}
return false;
}
function isConcurrentMode(object) {
{
if (!hasWarnedAboutDeprecatedIsConcurrentMode) {
hasWarnedAboutDeprecatedIsConcurrentMode = true; // Using console['warn'] to evade Babel and ESLint
console['warn']('The ReactIs.isConcurrentMode() alias has been deprecated, ' + 'and will be removed in React 18+.');
}
}
return false;
}
function isContextConsumer(object) {
return typeOf(object) === REACT_CONTEXT_TYPE;
}
function isContextProvider(object) {
return typeOf(object) === REACT_PROVIDER_TYPE;
}
function isElement(object) {
return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
}
function isForwardRef(object) {
return typeOf(object) === REACT_FORWARD_REF_TYPE;
}
function isFragment(object) {
return typeOf(object) === REACT_FRAGMENT_TYPE;
}
function isLazy(object) {
return typeOf(object) === REACT_LAZY_TYPE;
}
function isMemo(object) {
return typeOf(object) === REACT_MEMO_TYPE;
}
function isPortal(object) {
return typeOf(object) === REACT_PORTAL_TYPE;
}
function isProfiler(object) {
return typeOf(object) === REACT_PROFILER_TYPE;
}
function isStrictMode(object) {
return typeOf(object) === REACT_STRICT_MODE_TYPE;
}
function isSuspense(object) {
return typeOf(object) === REACT_SUSPENSE_TYPE;
}
function isSuspenseList(object) {
return typeOf(object) === REACT_SUSPENSE_LIST_TYPE;
}
exports.ContextConsumer = ContextConsumer;
exports.ContextProvider = ContextProvider;
exports.Element = Element;
exports.ForwardRef = ForwardRef;
exports.Fragment = Fragment;
exports.Lazy = Lazy;
exports.Memo = Memo;
exports.Portal = Portal;
exports.Profiler = Profiler;
exports.StrictMode = StrictMode;
exports.Suspense = Suspense;
exports.SuspenseList = SuspenseList;
exports.isAsyncMode = isAsyncMode;
exports.isConcurrentMode = isConcurrentMode;
exports.isContextConsumer = isContextConsumer;
exports.isContextProvider = isContextProvider;
exports.isElement = isElement;
exports.isForwardRef = isForwardRef;
exports.isFragment = isFragment;
exports.isLazy = isLazy;
exports.isMemo = isMemo;
exports.isPortal = isPortal;
exports.isProfiler = isProfiler;
exports.isStrictMode = isStrictMode;
exports.isSuspense = isSuspense;
exports.isSuspenseList = isSuspenseList;
exports.isValidElementType = isValidElementType;
exports.typeOf = typeOf;
})));

View File

@@ -0,0 +1,15 @@
/**
* @license React
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
(function(){'use strict';(function(b,c){"object"===typeof exports&&"undefined"!==typeof module?c(exports):"function"===typeof define&&define.amd?define(["exports"],c):(b=b||self,c(b.ReactIs={}))})(this,function(b){function c(a){if("object"===typeof a&&null!==a){var b=a.$$typeof;switch(b){case q:switch(a=a.type,a){case d:case e:case f:case g:case h:return a;default:switch(a=a&&a.$$typeof,a){case t:case k:case l:case m:case n:case p:return a;default:return b}}case r:return b}}}var q=Symbol.for("react.element"),
r=Symbol.for("react.portal"),d=Symbol.for("react.fragment"),f=Symbol.for("react.strict_mode"),e=Symbol.for("react.profiler"),p=Symbol.for("react.provider"),k=Symbol.for("react.context"),t=Symbol.for("react.server_context"),l=Symbol.for("react.forward_ref"),g=Symbol.for("react.suspense"),h=Symbol.for("react.suspense_list"),n=Symbol.for("react.memo"),m=Symbol.for("react.lazy"),u=Symbol.for("react.offscreen");var v=Symbol.for("react.module.reference");b.ContextConsumer=k;b.ContextProvider=p;b.Element=
q;b.ForwardRef=l;b.Fragment=d;b.Lazy=m;b.Memo=n;b.Portal=r;b.Profiler=e;b.StrictMode=f;b.Suspense=g;b.SuspenseList=h;b.isAsyncMode=function(a){return!1};b.isConcurrentMode=function(a){return!1};b.isContextConsumer=function(a){return c(a)===k};b.isContextProvider=function(a){return c(a)===p};b.isElement=function(a){return"object"===typeof a&&null!==a&&a.$$typeof===q};b.isForwardRef=function(a){return c(a)===l};b.isFragment=function(a){return c(a)===d};b.isLazy=function(a){return c(a)===m};b.isMemo=
function(a){return c(a)===n};b.isPortal=function(a){return c(a)===r};b.isProfiler=function(a){return c(a)===e};b.isStrictMode=function(a){return c(a)===f};b.isSuspense=function(a){return c(a)===g};b.isSuspenseList=function(a){return c(a)===h};b.isValidElementType=function(a){return"string"===typeof a||"function"===typeof a||a===d||a===e||a===f||a===g||a===h||a===u||"object"===typeof a&&null!==a&&(a.$$typeof===m||a.$$typeof===n||a.$$typeof===p||a.$$typeof===k||a.$$typeof===l||a.$$typeof===v||void 0!==
a.getModuleId)?!0:!1};b.typeOf=c});
})();

204
frontend/node_modules/react-markdown/package.json generated vendored Normal file
View File

@@ -0,0 +1,204 @@
{
"name": "react-markdown",
"version": "8.0.7",
"description": "React component to render markdown",
"license": "MIT",
"keywords": [
"remark",
"unified",
"markdown",
"commonmark",
"gfm",
"ast",
"react",
"react-component",
"component"
],
"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>",
"contributors": [
"Espen Hovlandsdal <espen@hovlandsdal.com>",
"Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)",
"Thomas Lindstrøm <t@hom.as>",
"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>",
"Kelvin Chan <kchan@securitycompass.com>",
"Kohei Asai <me@axross.io>",
"Marshall Smith <marshall@radialdevgroup.com>",
"Nathan Bierema <nbierema@gmail.com>",
"Petr Gazarov <petrgazarov@gmail.com>",
"Phil Rajchgot <tophil@outlook.com>",
"Rasmus Eneman <rasmus@eneman.eu>",
"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>",
"Tiago Roldão <focus5.6@gmail.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",
"main": "index.js",
"types": "index.d.ts",
"unpkg": "react-markdown.min.js",
"files": [
"lib/",
"index.d.ts",
"index.js",
"react-markdown.min.js"
],
"dependencies": {
"@types/hast": "^2.0.0",
"@types/prop-types": "^15.0.0",
"@types/unist": "^2.0.0",
"comma-separated-tokens": "^2.0.0",
"hast-util-whitespace": "^2.0.0",
"prop-types": "^15.0.0",
"property-information": "^6.0.0",
"react-is": "^18.0.0",
"remark-parse": "^10.0.0",
"remark-rehype": "^10.0.0",
"space-separated-tokens": "^2.0.0",
"style-to-object": "^0.4.0",
"unified": "^10.0.0",
"unist-util-visit": "^4.0.0",
"vfile": "^5.0.0"
},
"peerDependencies": {
"@types/react": ">=16",
"react": ">=16"
},
"devDependencies": {
"@types/node": "^18.0.0",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@types/react-is": "^17.0.0",
"c8": "^7.0.0",
"esbuild": "^0.17.0",
"eslint-config-xo-react": "^0.27.0",
"eslint-plugin-es": "^4.0.0",
"eslint-plugin-react": "^7.0.0",
"eslint-plugin-react-hooks": "^4.0.0",
"eslint-plugin-security": "^1.0.0",
"prettier": "^2.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"rehype-raw": "^6.0.0",
"remark-cli": "^11.0.0",
"remark-gfm": "^3.0.0",
"remark-preset-wooorm": "^9.0.0",
"remark-toc": "^8.0.0",
"type-coverage": "^2.0.0",
"typescript": "^5.0.0",
"xo": "^0.54.0"
},
"scripts": {
"prepack": "npm run build && npm run format",
"build": "tsc --build --clean && tsc --build && type-coverage && esbuild index.js --bundle --minify --target=es2015 --outfile=react-markdown.min.js --global-name=ReactMarkdown --banner:js=\"(function (g, f) {typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = f() : typeof define === 'function' && define.amd ? define([], f) : (g = typeof globalThis !== 'undefined' ? globalThis : g || self, g.ReactMarkdown = f()); }(this, (function () { 'use strict';\" --footer:js=\"return ReactMarkdown;})));\"",
"format": "remark . -qfo --ignore-pattern test/ && prettier . -w --loglevel warn && xo --fix",
"test-api": "node --no-warnings --experimental-loader=./test/loader.js --conditions development test/test.jsx",
"test-coverage": "c8 --check-coverage --100 --reporter lcov npm run test-api",
"test": "npm run build && npm run format && npm run test-coverage"
},
"remarkConfig": {
"plugins": [
"remark-preset-wooorm",
[
"remark-gfm",
{
"tablePipeAlign": false
}
],
[
"remark-lint-table-pipe-alignment",
false
],
[
"remark-lint-no-html",
false
]
]
},
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true,
"ignoreCatch": true,
"#": "below is ignored because some proptypes will `any`",
"ignoreFiles": [
"lib/react-markdown.d.ts",
"index.d.ts"
]
},
"prettier": {
"tabWidth": 2,
"useTabs": false,
"singleQuote": true,
"bracketSpacing": false,
"semi": false,
"trailingComma": "none"
},
"xo": {
"prettier": true,
"extends": "xo-react",
"envs": [
"shared-node-browser"
],
"overrides": [
{
"files": [
"lib/**/*.js"
],
"extends": [
"plugin:es/restrict-to-es2019",
"plugin:security/recommended"
],
"rules": {
"complexity": "off",
"security/detect-object-injection": "off"
}
},
{
"files": [
"test/**/*.jsx"
],
"rules": {
"n/file-extension-in-import": "off",
"react/no-children-prop": "off",
"react/prop-types": "off"
}
}
]
}
}

File diff suppressed because one or more lines are too long

783
frontend/node_modules/react-markdown/readme.md generated vendored Normal file
View File

@@ -0,0 +1,783 @@
<!--
Notes for maintaining this document:
* Update the link for `cm-html` once in a while
-->
# react-markdown
[![Build][build-badge]][build]
[![Coverage][coverage-badge]][coverage]
[![Downloads][downloads-badge]][downloads]
[![Size][size-badge]][size]
[![Sponsors][sponsors-badge]][collective]
[![Backers][backers-badge]][collective]
[![Chat][chat-badge]][chat]
React component to render markdown.
## Feature highlights
* [x] **[safe][security] by default**
(no `dangerouslySetInnerHTML` or XSS attacks)
* [x] **[components][]**
(pass your own component to use instead of `<h2>` for `## hi`)
* [x] **[plugins][]**
(many plugins you can pick and choose from)
* [x] **[compliant][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)
* [`props`](#props)
* [`uriTransformer`](#uritransformer)
* [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)
* [Types](#types)
* [Compatibility](#compatibility)
* [Architecture](#architecture)
* [Appendix A: HTML in markdown](#appendix-a-html-in-markdown)
* [Appendix B: Components](#appendix-b-components)
* [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 itll safely render to React elements.
You can pass plugins to change how markdown is transformed to React elements and
pass components that will be used instead of normal HTML elements.
* to learn markdown, see this [cheatsheet and tutorial][cheat]
* to try out `react-markdown`, see [our demo][demo]
## When should I use this?
There are other ways to use markdown in React out there so why use this one?
The two main reasons are that they often rely on `dangerouslySetInnerHTML` or
have bugs with how they handle markdown.
`react-markdown` uses a syntax tree to build the virtual dom which allows for
updating only the changing DOM instead of completely overwriting.
`react-markdown` is 100% CommonMark compliant and has plugins to support other
syntax extensions (such as GFM).
These features are supported because we use [unified][], specifically [remark][]
for markdown and [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 youre familiar with unified, you can use a modern hooks based alternative
[`react-remark`][react-remark] or [`rehype-react`][rehype-react] manually.
If you instead want to use JavaScript and JSX *inside* markdown files, use
[MDX][].
## Install
This package is [ESM only][esm].
In Node.js (version 12.20+, 14.14+, or 16.0+), install with [npm][]:
```sh
npm install react-markdown
```
In Deno with [`esm.sh`][esmsh]:
```js
import ReactMarkdown from 'https://esm.sh/react-markdown@7'
```
In browsers with [`esm.sh`][esmsh]:
```html
<script type="module">
import ReactMarkdown from 'https://esm.sh/react-markdown@7?bundle'
</script>
```
## Use
A basic hello world:
```jsx
import React from 'react'
import ReactMarkdown from 'react-markdown'
import ReactDom from 'react-dom'
ReactDom.render(<ReactMarkdown># Hello, *world*!</ReactMarkdown>, document.body)
```
<details>
<summary>Show equivalent JSX</summary>
```jsx
<h1>
Hello, <em>world</em>!
</h1>
```
</details>
Here is an example that shows passing the markdown as a string and how
to use a plugin ([`remark-gfm`][gfm], which adds support for strikethrough,
tables, tasklists and URLs directly):
```jsx
import React from 'react'
import ReactDom from 'react-dom'
import ReactMarkdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
const markdown = `Just a link: https://reactjs.com.`
ReactDom.render(
<ReactMarkdown children={markdown} remarkPlugins={[remarkGfm]} />,
document.body
)
```
<details>
<summary>Show equivalent JSX</summary>
```jsx
<p>
Just a link: <a href="https://reactjs.com">https://reactjs.com</a>.
</p>
```
</details>
## API
This package exports the following identifier:
[`uriTransformer`][uri-transformer].
The default export is `ReactMarkdown`.
### `props`
* `children` (`string`, default: `''`)\
markdown to parse
* `components` (`Record<string, Component>`, default: `{}`)\
object mapping tag names to React components
* `remarkPlugins` (`Array<Plugin>`, default: `[]`)\
list of [remark plugins][remark-plugins] to use
* `rehypePlugins` (`Array<Plugin>`, default: `[]`)\
list of [rehype plugins][rehype-plugins] to use
* `remarkRehypeOptions` (`Object?`, default: `undefined`)\
options to pass through to [`remark-rehype`][remark-rehype]
* `className` (`string?`)\
wrap the markdown in a `div` with this class name
* `skipHtml` (`boolean`, default: `false`)\
ignore HTML in markdown completely
* `sourcePos` (`boolean`, default: `false`)\
pass a prop to all components with a serialized position
(`data-sourcepos="3:1-3:13"`)
* `rawSourcePos` (`boolean`, default: `false`)\
pass a prop to all components with their [position][]
(`sourcePosition: {start: {line: 3, column: 1}, end:…}`)
* `includeElementIndex` (`boolean`, default: `false`)\
pass the `index` (number of elements before it) and `siblingCount` (number
of elements in parent) as props to all components
* `allowedElements` (`Array<string>`, default: `undefined`)\
tag names to allow (cant combine w/ `disallowedElements`), all tag names
are allowed by default
* `disallowedElements` (`Array<string>`, default: `undefined`)\
tag names to disallow (cant combine w/ `allowedElements`), all tag names
are allowed by default
* `allowElement` (`(element, index, parent) => boolean?`, optional)\
function called to check if an element is allowed (when truthy) or not,
`allowedElements` or `disallowedElements` is used first!
* `unwrapDisallowed` (`boolean`, default: `false`)\
extract (unwrap) the children of not allowed elements, by default, when
`strong` is disallowed, it and its children are dropped, but with
`unwrapDisallowed` the element itself is replaced by its children
* `linkTarget` (`string` or `(href, children, title) => string`, optional)\
target to use on links (such as `_blank` for `<a target="_blank"…`)
* `transformLinkUri` (`(href, children, title) => string`, default:
[`uriTransformer`][uri-transformer], optional)\
change URLs on links, pass `null` to allow all URLs, see [security][]
* `transformImageUri` (`(src, alt, title) => string`, default:
[`uriTransformer`][uri-transformer], optional)\
change URLs on images, pass `null` to allow all URLs, see [security][]
### `uriTransformer`
Our default URL transform, which you can overwrite (see props above).
Its given a URL and cleans it, by allowing only `http:`, `https:`, `mailto:`,
and `tel:` URLs, absolute paths (`/example.png`), and hashes (`#some-place`).
See the [source code here][uri].
## Examples
### Use a plugin
This example shows how to use a remark plugin.
In this case, [`remark-gfm`][gfm], which adds support for strikethrough, tables,
tasklists and URLs directly:
```jsx
import React from 'react'
import ReactMarkdown from 'react-markdown'
import ReactDom from 'react-dom'
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 |
| - | - |
`
ReactDom.render(
<ReactMarkdown children={markdown} remarkPlugins={[remarkGfm]} />,
document.body
)
```
<details>
<summary>Show equivalent JSX</summary>
```jsx
<>
<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>
<li>Lists</li>
<li>
<input checked={false} readOnly={true} type="checkbox" /> todo
</li>
<li>
<input checked={true} readOnly={true} type="checkbox" /> done
</li>
</ul>
<p>A table:</p>
<table>
<thead>
<tr>
<td>a</td>
<td>b</td>
</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`][gfm] has an option to allow only double tildes for strikethrough:
```jsx
import React from 'react'
import ReactMarkdown from 'react-markdown'
import ReactDom from 'react-dom'
import remarkGfm from 'remark-gfm'
ReactDom.render(
<ReactMarkdown remarkPlugins={[[remarkGfm, {singleTilde: false}]]}>
This ~is not~ strikethrough, but ~~this is~~!
</ReactMarkdown>,
document.body
)
```
<details>
<summary>Show equivalent JSX</summary>
```jsx
<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`][react-syntax-highlighter] by
[**@conorhastings**][conor]:
```jsx
import React from 'react'
import ReactDom from 'react-dom'
import ReactMarkdown 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!')
~~~
`
ReactDom.render(
<ReactMarkdown
children={markdown}
components={{
code({node, inline, className, children, ...props}) {
const match = /language-(\w+)/.exec(className || '')
return !inline && match ? (
<SyntaxHighlighter
{...props}
children={String(children).replace(/\n$/, '')}
style={dark}
language={match[1]}
PreTag="div"
/>
) : (
<code {...props} className={className}>
{children}
</code>
)
}
}}
/>,
document.body
)
```
<details>
<summary>Show equivalent JSX</summary>
```jsx
<>
<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`][math])
is used to support math in markdown, and a transform plugin
([`rehype-katex`][katex]) to render that math.
```jsx
import React from 'react'
import ReactDom from 'react-dom'
import ReactMarkdown from 'react-markdown'
import remarkMath from 'remark-math'
import rehypeKatex from 'rehype-katex'
import 'katex/dist/katex.min.css' // `rehype-katex` does not import the CSS for you
ReactDom.render(
<ReactMarkdown
children={`The lift coefficient ($C_L$) is a dimensionless coefficient.`}
remarkPlugins={[remarkMath]}
rehypePlugins={[rehypeKatex]}
/>,
document.body
)
```
<details>
<summary>Show equivalent JSX</summary>
```jsx
<p>
The lift coefficient (
<span className="math math-inline">
<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>
</span>
) is a dimensionless coefficient.
</p>
```
</details>
## Plugins
We use [unified][], specifically [remark][] for markdown and [rehype][] for
HTML, which are tools to transform content with plugins.
Here are three good ways to find plugins:
* [`awesome-remark`][awesome-remark] and [`awesome-rehype`][awesome-rehype]
— selection of the most awesome projects
* [List of remark plugins][remark-plugins] and
[list of rehype plugins][rehype-plugins]
— list of all plugins
* [`remark-plugin`][remark-plugin] and [`rehype-plugin`][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`][micromark] under the hood for our parsing.
See its documentation for more information on markdown, CommonMark, and
extensions.
## Types
This package is fully typed with [TypeScript][].
It exports `Options` and `Components` types, which specify the interface of the
accepted props and components.
## Compatibility
Projects maintained by the unified collective are compatible with all maintained
versions of Node.js.
As of now, that is Node.js 12.20+, 14.14+, and 16.0+.
Our projects sometimes work with older versions, but this is not guaranteed.
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, its important to first understand what
unified does: please read through the [`unifiedjs/unified`][unified] readme (the
part until you hit the API section is required reading).
`react-markdown` is a unified pipeline — wrapped so that most folks dont 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`][raw]:
```jsx
import React from 'react'
import ReactDom from 'react-dom'
import ReactMarkdown from 'react-markdown'
import rehypeRaw from 'rehype-raw'
const input = `<div class="note">
Some *emphasis* and <strong>strong</strong>!
</div>`
ReactDom.render(
<ReactMarkdown rehypePlugins={[rehypeRaw]} children={input} />,
document.body
)
```
<details>
<summary>Show equivalent JSX</summary>
```jsx
<div class="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][cm-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:
```jsx
<ReactMarkdown
components={{
// Map `h1` (`# heading`) to use `h2`s.
h1: 'h2',
// Rewrite `em`s (`*like so*`) to `i` with a red foreground color.
em: ({node, ...props}) => <i style={{color: 'red'}} {...props} />
}}
/>
```
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`][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.
There are some extra props passed.
* `code`
* `inline` (`boolean?`)
— set to `true` for inline code
* `className` (`string?`)
— set to `language-js` or so when using ` ```js `
* `h1`, `h2`, `h3`, `h4`, `h5`, `h6`
* `level` (`number` between 1 and 6)
— heading rank
* `input` (when using [`remark-gfm`][gfm])
* `checked` (`boolean`)
— whether the item is checked
* `disabled` (`true`)
* `type` (`'checkbox'`)
* `li`
* `index` (`number`)
— number of preceding items (so first gets `0`, etc.)
* `ordered` (`boolean`)
— whether the parent is an `ol` or not
* `checked` (`boolean?`)
`null` normally, `boolean` when using [`remark-gfm`][gfm]s tasklists
* `className` (`string?`)
— set to `task-list-item` when using [`remark-gfm`][gfm] and the
item1 is a tasklist
* `ol`, `ul`
* `depth` (`number`)
— number of ancestral lists (so first gets `0`, etc.)
* `ordered` (`boolean`)
— whether its an `ol` or not
* `className` (`string?`)
— set to `contains-task-list` when using [`remark-gfm`][gfm] and the
list contains one or more tasklists
* `td`, `th` (when using [`remark-gfm`][gfm])
* `style` (`Object?`)
— something like `{textAlign: 'left'}` depending on how the cell is
aligned
* `isHeader` (`boolean`)
— whether its a `th` or not
* `tr` (when using [`remark-gfm`][gfm])
* `isHeader` (`boolean`)
— whether its in the `thead` or not
Every component will receive a `node` (`Object`).
This is the original [hast](https://github.com/syntax-tree/hast) element being
turned into a React element.
Every element will receive a `key` (`string`).
See [Reacts docs](https://reactjs.org/docs/lists-and-keys.html#keys) for more
info.
Optionally, components will also receive:
* `data-sourcepos` (`string`)
— see `sourcePos` option
* `sourcePosition` (`Object`)
— see `rawSourcePos` option
* `index` and `siblingCount` (`number`)
— see `includeElementIndex` option
* `target` on `a` (`string`)
— see `linkTarget` option
## Security
Use of `react-markdown` is secure by default.
Overwriting `transformLinkUri` or `transformImageUri` 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`][sanitize].
It lets you define your own schema of what is and isnt allowed.
## Related
* [`MDX`](https://github.com/mdx-js/mdx)
— JSX *in* markdown
* [`remark-gfm`](https://github.com/remarkjs/remark-gfm)
— add support for GitHub flavored markdown support
* [`react-remark`][react-remark]
— modern hook based alternative
* [`rehype-react`][rehype-react]
— turn HTML into React elements
## Contribute
See [`contributing.md`][contributing] in [`remarkjs/.github`][health] for ways
to get started.
See [`support.md`][support] for ways to get help.
This project has a [code of conduct][coc].
By interacting with this repository, organization, or community you agree to
abide by its terms.
## License
[MIT][license] © [Espen Hovlandsdal][author]
[build-badge]: https://github.com/remarkjs/react-markdown/workflows/main/badge.svg
[build]: https://github.com/remarkjs/react-markdown/actions
[coverage-badge]: https://img.shields.io/codecov/c/github/remarkjs/react-markdown.svg
[coverage]: https://codecov.io/github/remarkjs/react-markdown
[downloads-badge]: https://img.shields.io/npm/dm/react-markdown.svg
[downloads]: https://www.npmjs.com/package/react-markdown
[size-badge]: https://img.shields.io/bundlephobia/minzip/react-markdown.svg
[size]: https://bundlephobia.com/result?p=react-markdown
[sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg
[backers-badge]: https://opencollective.com/unified/backers/badge.svg
[collective]: https://opencollective.com/unified
[chat-badge]: https://img.shields.io/badge/chat-discussions-success.svg
[chat]: https://github.com/remarkjs/remark/discussions
[npm]: https://docs.npmjs.com/cli/install
[esmsh]: https://esm.sh
[health]: https://github.com/remarkjs/.github
[contributing]: https://github.com/remarkjs/.github/blob/HEAD/contributing.md
[support]: https://github.com/remarkjs/.github/blob/HEAD/support.md
[coc]: https://github.com/remarkjs/.github/blob/HEAD/code-of-conduct.md
[license]: license
[author]: https://espen.codes/
[micromark]: https://github.com/micromark/micromark
[remark]: https://github.com/remarkjs/remark
[demo]: https://remarkjs.github.io/react-markdown/
[position]: https://github.com/syntax-tree/unist#position
[gfm]: https://github.com/remarkjs/remark-gfm
[math]: https://github.com/remarkjs/remark-math
[katex]: https://github.com/remarkjs/remark-math/tree/main/packages/rehype-katex
[raw]: https://github.com/rehypejs/rehype-raw
[sanitize]: https://github.com/rehypejs/rehype-sanitize
[remark-plugins]: https://github.com/remarkjs/remark/blob/main/doc/plugins.md#list-of-plugins
[rehype-plugins]: https://github.com/rehypejs/rehype/blob/main/doc/plugins.md#list-of-plugins
[remark-rehype]: https://github.com/remarkjs/remark-rehype
[awesome-remark]: https://github.com/remarkjs/awesome-remark
[awesome-rehype]: https://github.com/rehypejs/awesome-rehype
[remark-plugin]: https://github.com/topics/remark-plugin
[rehype-plugin]: https://github.com/topics/rehype-plugin
[cm-html]: https://spec.commonmark.org/0.30/#html-blocks
[uri]: https://github.com/remarkjs/react-markdown/blob/main/lib/uri-transformer.js
[uri-transformer]: #uritransformer
[react]: http://reactjs.org
[cheat]: https://commonmark.org/help/
[unified]: https://github.com/unifiedjs/unified
[rehype]: https://github.com/rehypejs/rehype
[react-remark]: https://github.com/remarkjs/react-remark
[rehype-react]: https://github.com/rehypejs/rehype-react
[mdx]: https://github.com/mdx-js/mdx/
[typescript]: https://www.typescriptlang.org
[security]: #security
[components]: #appendix-b-components
[plugins]: #plugins
[syntax]: #syntax
[react-syntax-highlighter]: https://github.com/react-syntax-highlighter/react-syntax-highlighter
[conor]: https://github.com/conorhastings
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c