diff --git a/src/components/markdown-renderer/markdown-extension/blockquote/blockquote-color-extra-tag-replacer.tsx b/src/components/markdown-renderer/markdown-extension/blockquote/blockquote-color-extra-tag-replacer.tsx
index 904c8c96d..289578883 100644
--- a/src/components/markdown-renderer/markdown-extension/blockquote/blockquote-color-extra-tag-replacer.tsx
+++ b/src/components/markdown-renderer/markdown-extension/blockquote/blockquote-color-extra-tag-replacer.tsx
@@ -22,22 +22,20 @@ import { Optional } from '@mrdrogdrog/optional'
*/
export class BlockquoteColorExtraTagReplacer extends ComponentReplacer {
replace(element: Element): NodeReplacement {
- if (
- element.tagName === BlockquoteExtraTagMarkdownExtension.tagName &&
- element.attribs?.['data-label'] === 'color' &&
- element.children !== undefined
- ) {
- let index = 0
- return Optional.ofNullable(element.children[0])
- .filter(isText)
- .map((child) => (child as Text).data)
- .filter((content) => cssColor.test(content))
- .map((color) => (
-
-
-
- ))
- .orElse(DO_NOT_REPLACE)
- }
+ return Optional.of(element)
+ .filter(
+ (element) =>
+ element.tagName === BlockquoteExtraTagMarkdownExtension.tagName && element.attribs?.['data-label'] === 'color'
+ )
+ .map((element) => element.children[0])
+ .filter(isText)
+ .map((child) => (child as Text).data)
+ .filter((content) => cssColor.test(content))
+ .map((color) => (
+
+
+
+ ))
+ .orElse(DO_NOT_REPLACE)
}
}
diff --git a/src/components/markdown-renderer/markdown-extension/blockquote/blockquote-extra-tag-replacer.tsx b/src/components/markdown-renderer/markdown-extension/blockquote/blockquote-extra-tag-replacer.tsx
index 9fe297cce..888e31ea0 100644
--- a/src/components/markdown-renderer/markdown-extension/blockquote/blockquote-extra-tag-replacer.tsx
+++ b/src/components/markdown-renderer/markdown-extension/blockquote/blockquote-extra-tag-replacer.tsx
@@ -23,16 +23,17 @@ import { BlockquoteExtraTagMarkdownExtension } from './blockquote-extra-tag-mark
*/
export class BlockquoteExtraTagReplacer extends ComponentReplacer {
replace(element: Element, subNodeTransform: SubNodeTransform): NodeReplacement {
- if (element.tagName !== BlockquoteExtraTagMarkdownExtension.tagName || !element.attribs) {
- return DO_NOT_REPLACE
- }
-
- return (
-
- {this.buildIconElement(element)}
- {BlockquoteExtraTagReplacer.transformChildren(element, subNodeTransform)}
-
- )
+ return Optional.of(element)
+ .filter(
+ (element) => element.tagName === BlockquoteExtraTagMarkdownExtension.tagName && element.attribs !== undefined
+ )
+ .map((element) => (
+
+ {this.buildIconElement(element)}
+ {BlockquoteExtraTagReplacer.transformChildren(element, subNodeTransform)}
+
+ ))
+ .orElse(DO_NOT_REPLACE)
}
/**
diff --git a/src/components/markdown-renderer/markdown-extension/csv/csv-replacer.tsx b/src/components/markdown-renderer/markdown-extension/csv/csv-replacer.tsx
index 12f9bb78a..233fbf287 100644
--- a/src/components/markdown-renderer/markdown-extension/csv/csv-replacer.tsx
+++ b/src/components/markdown-renderer/markdown-extension/csv/csv-replacer.tsx
@@ -1,12 +1,13 @@
/*
- * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { Element } from 'domhandler'
import React from 'react'
-import { ComponentReplacer } from '../../replace-components/component-replacer'
+import type { NodeReplacement } from '../../replace-components/component-replacer'
+import { ComponentReplacer, DO_NOT_REPLACE } from '../../replace-components/component-replacer'
import { CsvTable } from './csv-table'
import { CodeBlockComponentReplacer } from '../../replace-components/code-block-component-replacer'
@@ -14,10 +15,10 @@ import { CodeBlockComponentReplacer } from '../../replace-components/code-block-
* Detects code blocks with "csv" as language and renders them as table.
*/
export class CsvReplacer extends ComponentReplacer {
- public replace(codeNode: Element): React.ReactElement | undefined {
+ public replace(codeNode: Element): NodeReplacement {
const code = CodeBlockComponentReplacer.extractTextFromCodeNode(codeNode, 'csv')
if (!code) {
- return
+ return DO_NOT_REPLACE
}
const extraData = codeNode.attribs['data-extra']
diff --git a/src/components/markdown-renderer/markdown-extension/highlighted-fence/highlighted-code-replacer.tsx b/src/components/markdown-renderer/markdown-extension/highlighted-fence/highlighted-code-replacer.tsx
index ffe67eab7..cf63d2b88 100644
--- a/src/components/markdown-renderer/markdown-extension/highlighted-fence/highlighted-code-replacer.tsx
+++ b/src/components/markdown-renderer/markdown-extension/highlighted-fence/highlighted-code-replacer.tsx
@@ -1,12 +1,13 @@
/*
- * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { Element } from 'domhandler'
import React from 'react'
-import { ComponentReplacer } from '../../replace-components/component-replacer'
+import type { NodeReplacement } from '../../replace-components/component-replacer'
+import { ComponentReplacer, DO_NOT_REPLACE } from '../../replace-components/component-replacer'
import { HighlightedCode } from './highlighted-code'
/**
@@ -21,10 +22,10 @@ export class HighlightedCodeReplacer extends ComponentReplacer {
: undefined
}
- public replace(codeNode: Element): React.ReactElement | undefined {
+ public replace(codeNode: Element): NodeReplacement {
const code = HighlightedCodeReplacer.extractCode(codeNode)
if (!code) {
- return
+ return DO_NOT_REPLACE
}
const language = codeNode.attribs['data-highlight-language']
diff --git a/src/components/markdown-renderer/markdown-extension/iframe-capsule/iframe-capsule-replacer.tsx b/src/components/markdown-renderer/markdown-extension/iframe-capsule/iframe-capsule-replacer.tsx
index 994423a92..855c6a2c0 100644
--- a/src/components/markdown-renderer/markdown-extension/iframe-capsule/iframe-capsule-replacer.tsx
+++ b/src/components/markdown-renderer/markdown-extension/iframe-capsule/iframe-capsule-replacer.tsx
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@@ -16,17 +16,15 @@ import { ClickShield } from '../../replace-components/click-shield/click-shield'
*/
export class IframeCapsuleReplacer extends ComponentReplacer {
replace(node: Element, subNodeTransform: SubNodeTransform, nativeRenderer: NativeRenderer): NodeReplacement {
- if (node.name === 'iframe') {
- return (
-
- {nativeRenderer()}
-
- )
- } else {
- return DO_NOT_REPLACE
- }
+ return node.name !== 'iframe' ? (
+ DO_NOT_REPLACE
+ ) : (
+
+ {nativeRenderer()}
+
+ )
}
}
diff --git a/src/components/markdown-renderer/markdown-extension/image/proxy-image-replacer.tsx b/src/components/markdown-renderer/markdown-extension/image/proxy-image-replacer.tsx
index 7d155fb1f..e37c8c717 100644
--- a/src/components/markdown-renderer/markdown-extension/image/proxy-image-replacer.tsx
+++ b/src/components/markdown-renderer/markdown-extension/image/proxy-image-replacer.tsx
@@ -1,12 +1,13 @@
/*
- * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { Element } from 'domhandler'
import React from 'react'
-import { ComponentReplacer } from '../../replace-components/component-replacer'
+import type { NodeReplacement } from '../../replace-components/component-replacer'
+import { ComponentReplacer, DO_NOT_REPLACE } from '../../replace-components/component-replacer'
import { ProxyImageFrame } from './proxy-image-frame'
export type ImageClickHandler = (event: React.MouseEvent) => void
@@ -22,20 +23,20 @@ export class ProxyImageReplacer extends ComponentReplacer {
this.clickHandler = clickHandler
}
- public replace(node: Element): React.ReactElement | undefined {
- if (node.name === 'img') {
- return (
-
- )
- }
+ public replace(node: Element): NodeReplacement {
+ return node.name !== 'img' ? (
+ DO_NOT_REPLACE
+ ) : (
+
+ )
}
}
diff --git a/src/components/markdown-renderer/markdown-extension/katex/katex-replacer.tsx b/src/components/markdown-renderer/markdown-extension/katex/katex-replacer.tsx
index 933065021..9f7f00d30 100644
--- a/src/components/markdown-renderer/markdown-extension/katex/katex-replacer.tsx
+++ b/src/components/markdown-renderer/markdown-extension/katex/katex-replacer.tsx
@@ -7,6 +7,7 @@
import type { Element } from 'domhandler'
import { isTag } from 'domhandler'
import React from 'react'
+import type { NodeReplacement } from '../../replace-components/component-replacer'
import { ComponentReplacer, DO_NOT_REPLACE } from '../../replace-components/component-replacer'
import { KatexMarkdownExtension } from './katex-markdown-extension'
import { Optional } from '@mrdrogdrog/optional'
@@ -17,7 +18,7 @@ const KaTeX = React.lazy(() => import(/* webpackChunkName: "katex" */ './katex-f
* Detects LaTeX syntax and renders it with KaTeX.
*/
export class KatexReplacer extends ComponentReplacer {
- public replace(node: Element): React.ReactElement | undefined {
+ public replace(node: Element): NodeReplacement {
return this.extractKatexContent(node)
.map((latexContent) => {
const isInline = !!node.attribs?.['data-inline']
diff --git a/src/components/markdown-renderer/markdown-extension/linemarker/linemarker-replacer.tsx b/src/components/markdown-renderer/markdown-extension/linemarker/linemarker-replacer.tsx
index 9a1c03748..99a8b6315 100644
--- a/src/components/markdown-renderer/markdown-extension/linemarker/linemarker-replacer.tsx
+++ b/src/components/markdown-renderer/markdown-extension/linemarker/linemarker-replacer.tsx
@@ -1,18 +1,19 @@
/*
- * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { Element } from 'domhandler'
-import { ComponentReplacer } from '../../replace-components/component-replacer'
+import type { NodeReplacement } from '../../replace-components/component-replacer'
+import { ComponentReplacer, DO_NOT_REPLACE } from '../../replace-components/component-replacer'
import { LinemarkerMarkdownExtension } from './linemarker-markdown-extension'
/**
* Detects line markers and suppresses them in the resulting DOM.
*/
export class LinemarkerReplacer extends ComponentReplacer {
- public replace(codeNode: Element): null | undefined {
- return codeNode.name === LinemarkerMarkdownExtension.tagName ? null : undefined
+ public replace(codeNode: Element): NodeReplacement {
+ return codeNode.name === LinemarkerMarkdownExtension.tagName ? null : DO_NOT_REPLACE
}
}
diff --git a/src/components/markdown-renderer/markdown-extension/link-replacer/jump-anchor-replacer.tsx b/src/components/markdown-renderer/markdown-extension/link-replacer/jump-anchor-replacer.tsx
index 23cd31058..676115eac 100644
--- a/src/components/markdown-renderer/markdown-extension/link-replacer/jump-anchor-replacer.tsx
+++ b/src/components/markdown-renderer/markdown-extension/link-replacer/jump-anchor-replacer.tsx
@@ -1,5 +1,5 @@
/*
- * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@@ -22,10 +22,10 @@ export class JumpAnchorReplacer extends ComponentReplacer {
const jumpId = node.attribs['data-jump-target-id']
delete node.attribs['data-jump-target-id']
const replacement = nativeRenderer()
- if (replacement === null || typeof replacement === 'string') {
- return replacement
- } else {
- return )} jumpTargetId={jumpId} />
- }
+ return replacement === null || typeof replacement === 'string' ? (
+ replacement
+ ) : (
+ )} jumpTargetId={jumpId} />
+ )
}
}
diff --git a/src/components/markdown-renderer/markdown-extension/task-list/task-list-replacer.tsx b/src/components/markdown-renderer/markdown-extension/task-list/task-list-replacer.tsx
index b96aa22df..26a4f894a 100644
--- a/src/components/markdown-renderer/markdown-extension/task-list/task-list-replacer.tsx
+++ b/src/components/markdown-renderer/markdown-extension/task-list/task-list-replacer.tsx
@@ -1,13 +1,13 @@
/*
- * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { Element } from 'domhandler'
-import type { ReactElement } from 'react'
import React from 'react'
-import { ComponentReplacer } from '../../replace-components/component-replacer'
+import type { NodeReplacement } from '../../replace-components/component-replacer'
+import { ComponentReplacer, DO_NOT_REPLACE } from '../../replace-components/component-replacer'
import { TaskListCheckbox } from './task-list-checkbox'
export type TaskCheckedChangeHandler = (lineInMarkdown: number, checked: boolean) => void
@@ -20,23 +20,17 @@ export class TaskListReplacer extends ComponentReplacer {
constructor(onTaskCheckedChange?: TaskCheckedChangeHandler) {
super()
- this.onTaskCheckedChange = (lineInMarkdown, checked) => {
- if (onTaskCheckedChange === undefined) {
- return
- }
- onTaskCheckedChange(lineInMarkdown, checked)
- }
+ this.onTaskCheckedChange = (lineInMarkdown, checked) => onTaskCheckedChange?.(lineInMarkdown, checked)
}
- public replace(node: Element): ReactElement | undefined {
+ public replace(node: Element): NodeReplacement {
if (node.attribs?.class !== 'task-list-item-checkbox') {
- return
+ return DO_NOT_REPLACE
}
const lineInMarkdown = Number(node.attribs['data-line'])
- if (isNaN(lineInMarkdown)) {
- return undefined
- }
- return (
+ return isNaN(lineInMarkdown) ? (
+ DO_NOT_REPLACE
+ ) : (
- }
+ return node.name !== 'img' || !uploadIdRegex.test(node.attribs.src) ? (
+ DO_NOT_REPLACE
+ ) : (
+
+ )
}
}
diff --git a/src/components/markdown-renderer/replace-components/code-block-component-replacer.ts b/src/components/markdown-renderer/replace-components/code-block-component-replacer.ts
index bd294a710..6b6f84ecd 100644
--- a/src/components/markdown-renderer/replace-components/code-block-component-replacer.ts
+++ b/src/components/markdown-renderer/replace-components/code-block-component-replacer.ts
@@ -1,11 +1,11 @@
/*
- * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import type { ValidReactDomElement } from './component-replacer'
-import { ComponentReplacer } from './component-replacer'
+import type { NodeReplacement } from './component-replacer'
+import { ComponentReplacer, DO_NOT_REPLACE } from './component-replacer'
import type { FunctionComponent } from 'react'
import React from 'react'
import type { Element } from 'domhandler'
@@ -22,9 +22,9 @@ export class CodeBlockComponentReplacer extends ComponentReplacer {
super()
}
- replace(node: Element): ValidReactDomElement | undefined {
+ replace(node: Element): NodeReplacement {
const code = CodeBlockComponentReplacer.extractTextFromCodeNode(node, this.language)
- return code ? React.createElement(this.component, { code: code }) : undefined
+ return code ? React.createElement(this.component, { code: code }) : DO_NOT_REPLACE
}
/**
diff --git a/src/components/markdown-renderer/replace-components/component-replacer.ts b/src/components/markdown-renderer/replace-components/component-replacer.ts
index aeb811e04..63c8b1cb1 100644
--- a/src/components/markdown-renderer/replace-components/component-replacer.ts
+++ b/src/components/markdown-renderer/replace-components/component-replacer.ts
@@ -10,13 +10,13 @@ import type { ReactElement } from 'react'
export type ValidReactDomElement = ReactElement | string | null
-export type SubNodeTransform = (node: Node, subKey: number | string) => NodeReplacement
+export type SubNodeTransform = (node: Node, subKey: number | string) => ValidReactDomElement
export type NativeRenderer = () => ValidReactDomElement
-export const REPLACE_WITH_NOTHING = null
-export const DO_NOT_REPLACE = undefined
-export type NodeReplacement = ValidReactDomElement | typeof REPLACE_WITH_NOTHING | typeof DO_NOT_REPLACE
+export const DO_NOT_REPLACE = Symbol()
+
+export type NodeReplacement = ValidReactDomElement | typeof DO_NOT_REPLACE
/**
* Base class for all component replacers.
@@ -42,7 +42,7 @@ export abstract class ComponentReplacer {
* @param subNodeTransform The transformer that should be used.
* @return The children as react elements.
*/
- protected static transformChildren(node: Element, subNodeTransform: SubNodeTransform): NodeReplacement[] {
+ protected static transformChildren(node: Element, subNodeTransform: SubNodeTransform): ValidReactDomElement[] {
return node.children.map((value, index) => subNodeTransform(value, index))
}
diff --git a/src/components/markdown-renderer/replace-components/custom-tag-with-id-component-replacer.ts b/src/components/markdown-renderer/replace-components/custom-tag-with-id-component-replacer.ts
index ed18bb998..e539ed3ec 100644
--- a/src/components/markdown-renderer/replace-components/custom-tag-with-id-component-replacer.ts
+++ b/src/components/markdown-renderer/replace-components/custom-tag-with-id-component-replacer.ts
@@ -1,11 +1,11 @@
/*
- * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
+ * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { NodeReplacement } from './component-replacer'
-import { ComponentReplacer } from './component-replacer'
+import { ComponentReplacer, DO_NOT_REPLACE } from './component-replacer'
import type { FunctionComponent } from 'react'
import React from 'react'
import type { Element } from 'domhandler'
@@ -24,7 +24,7 @@ export class CustomTagWithIdComponentReplacer extends ComponentReplacer {
public replace(node: Element): NodeReplacement {
const id = this.extractId(node)
- return id ? React.createElement(this.component, { id: id }) : undefined
+ return id ? React.createElement(this.component, { id: id }) : DO_NOT_REPLACE
}
/**
diff --git a/src/components/markdown-renderer/utils/node-to-react-transformer.test.tsx b/src/components/markdown-renderer/utils/node-to-react-transformer.test.tsx
index 48f25e18a..19a7b193d 100644
--- a/src/components/markdown-renderer/utils/node-to-react-transformer.test.tsx
+++ b/src/components/markdown-renderer/utils/node-to-react-transformer.test.tsx
@@ -8,7 +8,7 @@ import { NodeToReactTransformer } from './node-to-react-transformer'
import { Element } from 'domhandler'
import type { ReactElement, ReactHTMLElement } from 'react'
import type { NodeReplacement } from '../replace-components/component-replacer'
-import { ComponentReplacer, DO_NOT_REPLACE, REPLACE_WITH_NOTHING } from '../replace-components/component-replacer'
+import { ComponentReplacer, DO_NOT_REPLACE } from '../replace-components/component-replacer'
describe('node to react transformer', () => {
let nodeToReactTransformer: NodeToReactTransformer
@@ -30,7 +30,7 @@ describe('node to react transformer', () => {
nodeToReactTransformer.setReplacers([
new (class extends ComponentReplacer {
replace(): NodeReplacement {
- return REPLACE_WITH_NOTHING
+ return null
}
})()
])
diff --git a/src/components/markdown-renderer/utils/node-to-react-transformer.tsx b/src/components/markdown-renderer/utils/node-to-react-transformer.tsx
index 1fce0bffa..b3cd749e2 100644
--- a/src/components/markdown-renderer/utils/node-to-react-transformer.tsx
+++ b/src/components/markdown-renderer/utils/node-to-react-transformer.tsx
@@ -8,7 +8,7 @@ import type { Element, Node } from 'domhandler'
import { isTag } from 'domhandler'
import { convertNodeToReactElement } from '@hedgedoc/html-to-react/dist/convertNodeToReactElement'
import type { ComponentReplacer, NodeReplacement, ValidReactDomElement } from '../replace-components/component-replacer'
-import { DO_NOT_REPLACE, REPLACE_WITH_NOTHING } from '../replace-components/component-replacer'
+import { DO_NOT_REPLACE } from '../replace-components/component-replacer'
import React from 'react'
import type { LineWithId } from '../markdown-extension/linemarker/types'
import { Optional } from '@mrdrogdrog/optional'
@@ -45,7 +45,7 @@ export class NodeToReactTransformer {
* @param index The index of the node within its parents child list.
* @return the created react element
*/
- public translateNodeToReactElement(node: Node, index: number | string): ValidReactDomElement | undefined {
+ public translateNodeToReactElement(node: Node, index: number | string): ValidReactDomElement {
return isTag(node)
? this.translateElementToReactElement(node, index)
: convertNodeToReactElement(node, index, this.translateNodeToReactElement.bind(this))
@@ -58,10 +58,10 @@ export class NodeToReactTransformer {
* @param index The index of the element within its parents child list.
* @return the created react element
*/
- private translateElementToReactElement(element: Element, index: number | string): ValidReactDomElement | undefined {
+ private translateElementToReactElement(element: Element, index: number | string): ValidReactDomElement {
const elementKey = this.calculateUniqueKey(element).orElseGet(() => (-index).toString())
const replacement = this.findElementReplacement(element, elementKey)
- if (replacement === REPLACE_WITH_NOTHING) {
+ if (replacement === null) {
return null
} else if (replacement === DO_NOT_REPLACE) {
return this.renderNativeNode(element, elementKey)
@@ -101,7 +101,7 @@ export class NodeToReactTransformer {
*
* @param element The {@link Element} that should be checked.
* @param elementKey The unique key for the element
- * @return The replacement or {@link DO_NOT_REPLACE} if the element shouldn't be replaced or {@link REPLACE_WITH_NOTHING} if the node shouldn't be rendered at all.
+ * @return The replacement or {@link DO_NOT_REPLACE} if the element shouldn't be replaced with a custom component.
*/
private findElementReplacement(element: Element, elementKey: string): NodeReplacement {
const transformer = this.translateNodeToReactElement.bind(this)