mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-25 03:06:31 -05:00
Add markdown-extension-collection.ts
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
ed6ab1b1fe
commit
f2958a77fe
2 changed files with 67 additions and 31 deletions
|
@ -6,17 +6,15 @@
|
||||||
|
|
||||||
import MarkdownIt from 'markdown-it/lib'
|
import MarkdownIt from 'markdown-it/lib'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import type { ComponentReplacer, ValidReactDomElement } from '../replace-components/component-replacer'
|
import type { ValidReactDomElement } from '../replace-components/component-replacer'
|
||||||
import convertHtmlToReact from '@hedgedoc/html-to-react'
|
import convertHtmlToReact from '@hedgedoc/html-to-react'
|
||||||
import { NodeToReactTransformer } from '../utils/node-to-react-transformer'
|
import { NodeToReactTransformer } from '../utils/node-to-react-transformer'
|
||||||
import { LineIdMapper } from '../utils/line-id-mapper'
|
import { LineIdMapper } from '../utils/line-id-mapper'
|
||||||
import type { MarkdownExtension } from '../markdown-extension/markdown-extension'
|
import type { MarkdownExtension } from '../markdown-extension/markdown-extension'
|
||||||
import type { NodeProcessor } from '../node-preprocessors/node-processor'
|
import { MarkdownExtensionCollection } from '../markdown-extension/markdown-extension-collection'
|
||||||
import type { Document } from 'domhandler'
|
|
||||||
import { SanitizerMarkdownExtension } from '../markdown-extension/sanitizer/sanitizer-markdown-extension'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders markdown code into react elements
|
* Renders Markdown-Code into react elements
|
||||||
*
|
*
|
||||||
* @param markdownContentLines The markdown code lines that should be rendered
|
* @param markdownContentLines The markdown code lines that should be rendered
|
||||||
* @param additionalMarkdownExtensions A list of {@link MarkdownExtension markdown extensions} that should be used
|
* @param additionalMarkdownExtensions A list of {@link MarkdownExtension markdown extensions} that should be used
|
||||||
|
@ -32,13 +30,10 @@ export const useConvertMarkdownToReactDom = (
|
||||||
): ValidReactDomElement[] => {
|
): ValidReactDomElement[] => {
|
||||||
const lineNumberMapper = useMemo(() => new LineIdMapper(), [])
|
const lineNumberMapper = useMemo(() => new LineIdMapper(), [])
|
||||||
const htmlToReactTransformer = useMemo(() => new NodeToReactTransformer(), [])
|
const htmlToReactTransformer = useMemo(() => new NodeToReactTransformer(), [])
|
||||||
const markdownExtensions = useMemo(() => {
|
const markdownExtensions = useMemo(
|
||||||
const tagNameWhiteList = additionalMarkdownExtensions.reduce(
|
() => new MarkdownExtensionCollection(additionalMarkdownExtensions),
|
||||||
(state, extension) => [...state, ...extension.buildTagNameWhitelist()],
|
[additionalMarkdownExtensions]
|
||||||
[] as string[]
|
)
|
||||||
)
|
|
||||||
return [...additionalMarkdownExtensions, new SanitizerMarkdownExtension(tagNameWhiteList)]
|
|
||||||
}, [additionalMarkdownExtensions])
|
|
||||||
|
|
||||||
const markdownIt = useMemo(() => {
|
const markdownIt = useMemo(() => {
|
||||||
const newMarkdownIt = new MarkdownIt('default', {
|
const newMarkdownIt = new MarkdownIt('default', {
|
||||||
|
@ -47,35 +42,19 @@ export const useConvertMarkdownToReactDom = (
|
||||||
langPrefix: '',
|
langPrefix: '',
|
||||||
typographer: true
|
typographer: true
|
||||||
})
|
})
|
||||||
markdownExtensions.forEach((extension) =>
|
markdownExtensions.configureMarkdownIt(newMarkdownIt)
|
||||||
newMarkdownIt.use((markdownIt) => extension.configureMarkdownIt(markdownIt))
|
|
||||||
)
|
|
||||||
markdownExtensions.forEach((extension) =>
|
|
||||||
newMarkdownIt.use((markdownIt) => extension.configureMarkdownItPost(markdownIt))
|
|
||||||
)
|
|
||||||
return newMarkdownIt
|
return newMarkdownIt
|
||||||
}, [allowHtml, markdownExtensions, newlinesAreBreaks])
|
}, [allowHtml, markdownExtensions, newlinesAreBreaks])
|
||||||
|
|
||||||
useMemo(() => {
|
useMemo(() => {
|
||||||
const replacers = markdownExtensions.reduce(
|
htmlToReactTransformer.setReplacers(markdownExtensions.buildReplacers())
|
||||||
(state, extension) => [...state, ...extension.buildReplacers()],
|
|
||||||
[] as ComponentReplacer[]
|
|
||||||
)
|
|
||||||
htmlToReactTransformer.setReplacers(replacers)
|
|
||||||
}, [htmlToReactTransformer, markdownExtensions])
|
}, [htmlToReactTransformer, markdownExtensions])
|
||||||
|
|
||||||
useMemo(() => {
|
useMemo(() => {
|
||||||
htmlToReactTransformer.setLineIds(lineNumberMapper.updateLineMapping(markdownContentLines))
|
htmlToReactTransformer.setLineIds(lineNumberMapper.updateLineMapping(markdownContentLines))
|
||||||
}, [htmlToReactTransformer, lineNumberMapper, markdownContentLines])
|
}, [htmlToReactTransformer, lineNumberMapper, markdownContentLines])
|
||||||
|
|
||||||
const nodePreProcessor = useMemo(() => {
|
const nodePreProcessor = useMemo(() => markdownExtensions.buildFlatNodeProcessor(), [markdownExtensions])
|
||||||
return markdownExtensions
|
|
||||||
.reduce((state, extension) => [...state, ...extension.buildNodeProcessors()], [] as NodeProcessor[])
|
|
||||||
.reduce(
|
|
||||||
(state, processor) => (document: Document) => state(processor.process(document)),
|
|
||||||
(document: Document) => document
|
|
||||||
)
|
|
||||||
}, [markdownExtensions])
|
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const html = markdownIt.render(markdownContentLines.join('\n'))
|
const html = markdownIt.render(markdownContentLines.join('\n'))
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { MarkdownExtension } from './markdown-extension'
|
||||||
|
import type { ComponentReplacer } from '../replace-components/component-replacer'
|
||||||
|
import type { Document } from 'domhandler'
|
||||||
|
import type MarkdownIt from 'markdown-it'
|
||||||
|
import { SanitizerMarkdownExtension } from './sanitizer/sanitizer-markdown-extension'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains multiple {@link MarkdownExtension} and uses them to configure parts of the renderer.
|
||||||
|
*/
|
||||||
|
export class MarkdownExtensionCollection {
|
||||||
|
private extensions: MarkdownExtension[]
|
||||||
|
|
||||||
|
public constructor(additionalExtensions: MarkdownExtension[]) {
|
||||||
|
const tagWhiteLists = additionalExtensions.flatMap((extension) => extension.buildTagNameWhitelist())
|
||||||
|
|
||||||
|
this.extensions = [...additionalExtensions, new SanitizerMarkdownExtension(tagWhiteLists)]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the given {@link MarkdownIt markdown-it instance} using every saved {@link MarkdownExtension extension}.
|
||||||
|
*
|
||||||
|
* @param markdownIt The markdown-it instance to configure.
|
||||||
|
*/
|
||||||
|
public configureMarkdownIt(markdownIt: MarkdownIt): void {
|
||||||
|
this.extensions.forEach((extension) => markdownIt.use((markdownIt) => extension.configureMarkdownIt(markdownIt)))
|
||||||
|
this.extensions.forEach((extension) =>
|
||||||
|
markdownIt.use((markdownIt) => extension.configureMarkdownItPost(markdownIt))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a node processor that applies the node processor of every saved {@link MarkdownExtension extension}.
|
||||||
|
*
|
||||||
|
* @return the created node processor function
|
||||||
|
*/
|
||||||
|
public buildFlatNodeProcessor(): (document: Document) => Document {
|
||||||
|
return this.extensions
|
||||||
|
.flatMap((extension) => extension.buildNodeProcessors())
|
||||||
|
.reduce(
|
||||||
|
(state, processor) => (document: Document) => state(processor.process(document)),
|
||||||
|
(document: Document) => document
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects all {@link ComponentReplacer component replacers} from all saved {@link MarkdownExtension extension}.
|
||||||
|
*/
|
||||||
|
public buildReplacers(): ComponentReplacer[] {
|
||||||
|
return this.extensions.flatMap((extension) => extension.buildReplacers())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue