/* * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) * * SPDX-License-Identifier: AGPL-3.0-only */ import { HtmlToReact } from '../../common/html-to-react/html-to-react' import type { MarkdownRendererExtension } from '../extensions/base/markdown-renderer-extension' import { useCombinedNodePreprocessor } from './hooks/use-combined-node-preprocessor' import { useConfiguredMarkdownIt } from './hooks/use-configured-markdown-it' import { LineContentToLineIdMapper } from './utils/line-content-to-line-id-mapper' import { NodeToReactTransformer } from './utils/node-to-react-transformer' import type { ParserOptions } from '@hedgedoc/html-to-react' import type DOMPurify from 'dompurify' import React, { useMemo } from 'react' export interface MarkdownToReactProps { markdownContentLines: string[] markdownRenderExtensions: MarkdownRendererExtension[] newlinesAreBreaks?: boolean allowHtml: boolean } /** * Renders Markdown code. * * @param markdownContentLines The Markdown code lines that should be rendered * @param additionalMarkdownExtensions A list of {@link MarkdownRendererExtension markdown extensions} that should be used * @param newlinesAreBreaks Defines if the alternative break mode of markdown it should be used * @param allowHtml Defines if html is allowed in markdown */ export const MarkdownToReact: React.FC = ({ markdownContentLines, markdownRenderExtensions, newlinesAreBreaks, allowHtml }) => { const lineNumberMapper = useMemo(() => new LineContentToLineIdMapper(), []) const nodeToReactTransformer = useMemo(() => new NodeToReactTransformer(), []) useMemo(() => { nodeToReactTransformer.setReplacers(markdownRenderExtensions.flatMap((extension) => extension.buildReplacers())) }, [nodeToReactTransformer, markdownRenderExtensions]) useMemo(() => { nodeToReactTransformer.setLineIds(lineNumberMapper.updateLineMapping(markdownContentLines)) }, [nodeToReactTransformer, lineNumberMapper, markdownContentLines]) const nodePreProcessor = useCombinedNodePreprocessor(markdownRenderExtensions) const markdownIt = useConfiguredMarkdownIt(markdownRenderExtensions, allowHtml, newlinesAreBreaks ?? true) const parserOptions: ParserOptions = useMemo( () => ({ transform: (node, index) => nodeToReactTransformer.translateNodeToReactElement(node, index), preprocessNodes: (document) => { nodeToReactTransformer.resetReplacers() return nodePreProcessor(document) } }), [nodeToReactTransformer, nodePreProcessor] ) const html = useMemo(() => markdownIt.render(markdownContentLines.join('\n')), [markdownContentLines, markdownIt]) const domPurifyConfig: DOMPurify.Config = useMemo( () => ({ ADD_TAGS: markdownRenderExtensions.flatMap((extension) => extension.buildTagNameAllowList()) }), [markdownRenderExtensions] ) return }