@tiptap/static-renderer 3.0.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +18 -0
  2. package/dist/index.cjs +62 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +52 -0
  5. package/dist/index.d.ts +52 -0
  6. package/dist/index.js +34 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/json/html-string/index.cjs +100 -0
  9. package/dist/json/html-string/index.cjs.map +1 -0
  10. package/dist/json/html-string/index.d.cts +205 -0
  11. package/dist/json/html-string/index.d.ts +205 -0
  12. package/dist/json/html-string/index.js +72 -0
  13. package/dist/json/html-string/index.js.map +1 -0
  14. package/dist/json/react/index.cjs +2306 -0
  15. package/dist/json/react/index.cjs.map +1 -0
  16. package/dist/json/react/index.d.cts +207 -0
  17. package/dist/json/react/index.d.ts +207 -0
  18. package/dist/json/react/index.js +2291 -0
  19. package/dist/json/react/index.js.map +1 -0
  20. package/dist/json/renderer.cjs +89 -0
  21. package/dist/json/renderer.cjs.map +1 -0
  22. package/dist/json/renderer.d.cts +182 -0
  23. package/dist/json/renderer.d.ts +182 -0
  24. package/dist/json/renderer.js +64 -0
  25. package/dist/json/renderer.js.map +1 -0
  26. package/dist/pm/html-string/index.cjs +359 -0
  27. package/dist/pm/html-string/index.cjs.map +1 -0
  28. package/dist/pm/html-string/index.d.cts +192 -0
  29. package/dist/pm/html-string/index.d.ts +192 -0
  30. package/dist/pm/html-string/index.js +332 -0
  31. package/dist/pm/html-string/index.js.map +1 -0
  32. package/dist/pm/react/index.cjs +2588 -0
  33. package/dist/pm/react/index.cjs.map +1 -0
  34. package/dist/pm/react/index.d.cts +181 -0
  35. package/dist/pm/react/index.d.ts +181 -0
  36. package/dist/pm/react/index.js +2576 -0
  37. package/dist/pm/react/index.js.map +1 -0
  38. package/package.json +82 -0
  39. package/src/helpers.example.ts +35 -0
  40. package/src/helpers.ts +65 -0
  41. package/src/index.ts +2 -0
  42. package/src/json/html-string/index.ts +2 -0
  43. package/src/json/html-string/string.example.ts +46 -0
  44. package/src/json/html-string/string.ts +22 -0
  45. package/src/json/react/index.ts +2 -0
  46. package/src/json/react/react.example.ts +45 -0
  47. package/src/json/react/react.tsx +35 -0
  48. package/src/json/renderer.ts +242 -0
  49. package/src/pm/extensionRenderer.ts +230 -0
  50. package/src/pm/html-string/html-string.example.ts +225 -0
  51. package/src/pm/html-string/html-string.ts +121 -0
  52. package/src/pm/html-string/index.ts +2 -0
  53. package/src/pm/markdown/markdown.example.ts +296 -0
  54. package/src/pm/react/index.ts +2 -0
  55. package/src/pm/react/react.example.tsx +306 -0
  56. package/src/pm/react/react.tsx +133 -0
  57. package/src/types.ts +57 -0
@@ -0,0 +1,46 @@
1
+ import { renderJSONContentToString } from './string.js'
2
+
3
+ /**
4
+ * This example demonstrates how to render a JSON representation of a node to a string
5
+ * It does so without including Prosemirror or Tiptap, it is the lightest possible way to render JSON content
6
+ * But, since it doesn't include Prosemirror or Tiptap, it cannot automatically render marks or nodes for you.
7
+ * If you need that, you should use the `renderToHTMLString` from `@tiptap/static-renderer`
8
+ *
9
+ * You have complete control over the rendering process. And can replace how each Node/Mark is rendered.
10
+ */
11
+
12
+ // eslint-disable-next-line no-console
13
+ console.log(
14
+ renderJSONContentToString({
15
+ nodeMapping: {
16
+ text({ node }) {
17
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
18
+ return node.text!
19
+ },
20
+ heading({ node, children }) {
21
+ const level = node.attrs?.level
22
+ const attrs = Object.entries(node.attrs || {})
23
+ .map(([key, value]) => `${key}=${JSON.stringify(value)}`)
24
+ .join(' ')
25
+
26
+ return `<h${level}${attrs ? ` ${attrs}` : ''}>${([] as string[])
27
+ .concat(children || '')
28
+ .filter(Boolean)
29
+ .join('\n')}</h${level}>`
30
+ },
31
+ },
32
+ markMapping: {},
33
+ })({
34
+ content: {
35
+ type: 'heading',
36
+ content: [
37
+ {
38
+ type: 'text',
39
+ text: 'hello world',
40
+ marks: [],
41
+ },
42
+ ],
43
+ attrs: { level: 2 },
44
+ },
45
+ }),
46
+ )
@@ -0,0 +1,22 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { MarkType, NodeType } from '../../types.js'
3
+ import { TiptapStaticRenderer, TiptapStaticRendererOptions } from '../renderer.js'
4
+
5
+ export function renderJSONContentToString<
6
+ /**
7
+ * A mark type is either a JSON representation of a mark or a Prosemirror mark instance
8
+ */
9
+ TMarkType extends { type: any } = MarkType,
10
+ /**
11
+ * A node type is either a JSON representation of a node or a Prosemirror node instance
12
+ */
13
+ TNodeType extends {
14
+ content?: { forEach:(cb: (node: TNodeType) => void) => void };
15
+ marks?: readonly TMarkType[];
16
+ type: string | { name: string };
17
+ } = NodeType,
18
+ >(options: TiptapStaticRendererOptions<string, TMarkType, TNodeType>) {
19
+ return TiptapStaticRenderer(ctx => {
20
+ return ctx.component(ctx.props as any)
21
+ }, options)
22
+ }
@@ -0,0 +1,2 @@
1
+ export * from '../renderer.js'
2
+ export * from './react.js'
@@ -0,0 +1,45 @@
1
+ import React from 'react'
2
+
3
+ import { NodeType } from '../../types.js'
4
+ import { NodeProps } from '../renderer.js'
5
+ import { renderJSONContentToReactElement } from './react.js'
6
+
7
+ /**
8
+ * This example demonstrates how to render a JSON representation of a node to a React element
9
+ * It does so without including Prosemirror or Tiptap, it is the lightest possible way to render JSON content
10
+ * But, since it doesn't include Prosemirror or Tiptap, it cannot automatically render marks or nodes for you.
11
+ * If you need that, you should use the `renderToReactElement` from `@tiptap/static-renderer`
12
+ *
13
+ * You have complete control over the rendering process. And can replace how each Node/Mark is rendered.
14
+ */
15
+
16
+ // eslint-disable-next-line no-console
17
+ console.log(renderJSONContentToReactElement({
18
+ nodeMapping: {
19
+ text({ node }) {
20
+ return node.text ?? null
21
+ },
22
+ heading({
23
+ node,
24
+ children,
25
+ }: NodeProps<NodeType<'heading', { level: number }>, React.ReactNode>) {
26
+ const level = node.attrs.level
27
+ const hTag = `h${level}`
28
+
29
+ return React.createElement(hTag, node.attrs, children)
30
+ },
31
+ },
32
+ markMapping: {},
33
+ })({
34
+ content: {
35
+ type: 'heading',
36
+ content: [
37
+ {
38
+ type: 'text',
39
+ text: 'hello world',
40
+ marks: [],
41
+ },
42
+ ],
43
+ attrs: { level: 2 },
44
+ },
45
+ }))
@@ -0,0 +1,35 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+
3
+ import React from 'react'
4
+
5
+ import { MarkType, NodeType } from '../../types.js'
6
+ import { TiptapStaticRenderer, TiptapStaticRendererOptions } from '../renderer.js'
7
+
8
+ export function renderJSONContentToReactElement<
9
+ /**
10
+ * A mark type is either a JSON representation of a mark or a Prosemirror mark instance
11
+ */
12
+ TMarkType extends { type: any } = MarkType,
13
+ /**
14
+ * A node type is either a JSON representation of a node or a Prosemirror node instance
15
+ */
16
+ TNodeType extends {
17
+ content?: { forEach:(cb: (node: TNodeType) => void) => void };
18
+ marks?: readonly TMarkType[];
19
+ type: string | { name: string };
20
+ } = NodeType,
21
+ >(options: TiptapStaticRendererOptions<React.ReactNode, TMarkType, TNodeType>) {
22
+ let key = 0
23
+
24
+ return TiptapStaticRenderer<React.ReactNode, TMarkType, TNodeType>(
25
+ ({ component, props: { children, ...props } }) => {
26
+ return React.createElement(
27
+ component as React.FC<typeof props>,
28
+ // eslint-disable-next-line no-plusplus
29
+ Object.assign(props, { key: key++ }),
30
+ ([] as React.ReactNode[]).concat(children),
31
+ )
32
+ },
33
+ options,
34
+ )
35
+ }
@@ -0,0 +1,242 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import type { MarkType, NodeType } from '../types'
3
+
4
+ /**
5
+ * Props for a node renderer
6
+ */
7
+ export type NodeProps<TNodeType = any, TChildren = any> = {
8
+ /**
9
+ * The current node to render
10
+ */
11
+ node: TNodeType;
12
+ /**
13
+ * Unless the node is the root node, this will always be defined
14
+ */
15
+ parent?: TNodeType;
16
+ /**
17
+ * The children of the current node
18
+ */
19
+ children?: TChildren;
20
+ /**
21
+ * Render a child element
22
+ */
23
+ renderElement: (props: {
24
+ /**
25
+ * Tiptap JSON content to render
26
+ */
27
+ content: TNodeType;
28
+ /**
29
+ * The parent node of the current node
30
+ */
31
+ parent?: TNodeType;
32
+ }) => TChildren;
33
+ };
34
+
35
+ /**
36
+ * Props for a mark renderer
37
+ */
38
+ export type MarkProps<TMarkType = any, TChildren = any, TNodeType = any> = {
39
+ /**
40
+ * The current mark to render
41
+ */
42
+ mark: TMarkType;
43
+ /**
44
+ * The children of the current mark
45
+ */
46
+ children?: TChildren;
47
+ /**
48
+ * The node the current mark is applied to
49
+ */
50
+ node: TNodeType;
51
+ /**
52
+ * The node the current mark is applied to
53
+ */
54
+ parent?: TNodeType;
55
+ };
56
+
57
+ export type TiptapStaticRendererOptions<
58
+ /**
59
+ * The return type of the render function (e.g. React.ReactNode, string)
60
+ */
61
+ TReturnType,
62
+ /**
63
+ * A mark type is either a JSON representation of a mark or a Prosemirror mark instance
64
+ */
65
+ TMarkType extends { type: any } = MarkType,
66
+ /**
67
+ * A node type is either a JSON representation of a node or a Prosemirror node instance
68
+ */
69
+ TNodeType extends {
70
+ content?: { forEach: (cb: (node: TNodeType) => void) => void };
71
+ marks?: readonly TMarkType[];
72
+ type: string | { name: string };
73
+ } = NodeType,
74
+ /**
75
+ * A node renderer is a function that takes a node and its children and returns the rendered output
76
+ */
77
+ TNodeRender extends (ctx: NodeProps<TNodeType, TReturnType | TReturnType[]>) => TReturnType = (
78
+ ctx: NodeProps<TNodeType, TReturnType | TReturnType[]>
79
+ ) => TReturnType,
80
+ /**
81
+ * A mark renderer is a function that takes a mark and its children and returns the rendered output
82
+ */
83
+ TMarkRender extends (ctx: MarkProps<TMarkType, TReturnType | TReturnType[], TNodeType>) => TReturnType = (
84
+ ctx: MarkProps<TMarkType, TReturnType | TReturnType[], TNodeType>
85
+ ) => TReturnType,
86
+ > = {
87
+ /**
88
+ * Mapping of node types to react components
89
+ */
90
+ nodeMapping: Record<string, TNodeRender>;
91
+ /**
92
+ * Mapping of mark types to react components
93
+ */
94
+ markMapping: Record<string, TMarkRender>;
95
+ /**
96
+ * Component to render if a node type is not handled
97
+ */
98
+ unhandledNode?: TNodeRender;
99
+ /**
100
+ * Component to render if a mark type is not handled
101
+ */
102
+ unhandledMark?: TMarkRender;
103
+ };
104
+
105
+ /**
106
+ * Tiptap Static Renderer
107
+ * ----------------------
108
+ *
109
+ * This function is a basis to allow for different renderers to be created.
110
+ * Generic enough to be able to statically render Prosemirror JSON or Prosemirror Nodes.
111
+ *
112
+ * Using this function, you can create a renderer that takes a JSON representation of a Prosemirror document
113
+ * and renders it using a mapping of node types to React components or even to a string.
114
+ * This function is used as the basis to create the `reactRenderer` and `stringRenderer` functions.
115
+ */
116
+ export function TiptapStaticRenderer<
117
+ /**
118
+ * The return type of the render function (e.g. React.ReactNode, string)
119
+ */
120
+ TReturnType,
121
+ /**
122
+ * A mark type is either a JSON representation of a mark or a Prosemirror mark instance
123
+ */
124
+ TMarkType extends { type: string | { name: string } } = MarkType,
125
+ /**
126
+ * A node type is either a JSON representation of a node or a Prosemirror node instance
127
+ */
128
+ TNodeType extends {
129
+ content?: { forEach:(
130
+ cb: (node: TNodeType) => void) => void };
131
+ marks?: readonly TMarkType[];
132
+ type: string | { name: string };
133
+ } = NodeType,
134
+ /**
135
+ * A node renderer is a function that takes a node and its children and returns the rendered output
136
+ */
137
+ TNodeRender extends (ctx: NodeProps<TNodeType, TReturnType | TReturnType[]>) => TReturnType = (
138
+ ctx: NodeProps<TNodeType, TReturnType | TReturnType[]>
139
+ ) => TReturnType,
140
+ /**
141
+ * A mark renderer is a function that takes a mark and its children and returns the rendered output
142
+ */
143
+ TMarkRender extends (ctx: MarkProps<TMarkType, TReturnType | TReturnType[], TNodeType>) => TReturnType = (
144
+ ctx: MarkProps<TMarkType, TReturnType | TReturnType[], TNodeType>
145
+ ) => TReturnType,
146
+ >(
147
+ /**
148
+ * The function that actually renders the component
149
+ */
150
+ renderComponent: (
151
+ ctx:
152
+ | {
153
+ component: TNodeRender;
154
+ props: NodeProps<TNodeType, TReturnType | TReturnType[]>;
155
+ }
156
+ | {
157
+ component: TMarkRender;
158
+ props: MarkProps<TMarkType, TReturnType | TReturnType[], TNodeType>;
159
+ }
160
+ ) => TReturnType,
161
+ {
162
+ nodeMapping,
163
+ markMapping,
164
+ unhandledNode,
165
+ unhandledMark,
166
+ }: TiptapStaticRendererOptions<TReturnType, TMarkType, TNodeType, TNodeRender, TMarkRender>,
167
+ ) {
168
+ /**
169
+ * Render Tiptap JSON and all its children using the provided node and mark mappings.
170
+ */
171
+ return function renderContent({
172
+ content,
173
+ parent,
174
+ }: {
175
+ /**
176
+ * Tiptap JSON content to render
177
+ */
178
+ content: TNodeType;
179
+ /**
180
+ * The parent node of the current node
181
+ */
182
+ parent?: TNodeType;
183
+ }): TReturnType {
184
+ const nodeType = typeof content.type === 'string' ? content.type : content.type.name
185
+ const NodeHandler = nodeMapping[nodeType] ?? unhandledNode
186
+
187
+ if (!NodeHandler) {
188
+ throw new Error(`missing handler for node type ${nodeType}`)
189
+ }
190
+
191
+ const nodeContent = renderComponent({
192
+ component: NodeHandler,
193
+ props: {
194
+ node: content,
195
+ parent,
196
+ renderElement: renderContent,
197
+ // Lazily compute the children to avoid unnecessary recursion
198
+ get children() {
199
+ // recursively render child content nodes
200
+ const children: TReturnType[] = []
201
+
202
+ if (content.content) {
203
+ content.content.forEach(child => {
204
+ children.push(
205
+ renderContent({
206
+ content: child,
207
+ parent: content,
208
+ }),
209
+ )
210
+ })
211
+ }
212
+
213
+ return children
214
+ },
215
+ },
216
+ })
217
+
218
+ // apply marks to the content
219
+ const markedContent = content.marks
220
+ ? content.marks.reduce((acc, mark) => {
221
+ const markType = typeof mark.type === 'string' ? mark.type : mark.type.name
222
+ const MarkHandler = markMapping[markType] ?? unhandledMark
223
+
224
+ if (!MarkHandler) {
225
+ throw new Error(`missing handler for mark type ${markType}`)
226
+ }
227
+
228
+ return renderComponent({
229
+ component: MarkHandler,
230
+ props: {
231
+ mark,
232
+ parent,
233
+ node: content,
234
+ children: acc,
235
+ },
236
+ })
237
+ }, nodeContent)
238
+ : nodeContent
239
+
240
+ return markedContent
241
+ }
242
+ }
@@ -0,0 +1,230 @@
1
+ /* eslint-disable no-plusplus */
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+
4
+ import {
5
+ ExtensionAttribute,
6
+ Extensions,
7
+ getAttributesFromExtensions,
8
+ getExtensionField,
9
+ getSchemaByResolvedExtensions,
10
+ JSONContent,
11
+ Mark as MarkExtension,
12
+ MarkConfig,
13
+ Node as NodeExtension,
14
+ NodeConfig,
15
+ resolveExtensions,
16
+ splitExtensions,
17
+ } from '@tiptap/core'
18
+ import { DOMOutputSpec, Mark, Node } from '@tiptap/pm/model'
19
+
20
+ import { getHTMLAttributes } from '../helpers.js'
21
+ import { MarkProps, NodeProps, TiptapStaticRendererOptions } from '../json/renderer.js'
22
+
23
+ export type DomOutputSpecToElement<T> = (content: DOMOutputSpec) => (children?: T | T[]) => T;
24
+
25
+ /**
26
+ * This takes a NodeExtension and maps it to a React component
27
+ * @param extension The node extension to map to a React component
28
+ * @param extensionAttributes All available extension attributes
29
+ * @returns A tuple with the name of the extension and a React component that renders the extension
30
+ */
31
+ export function mapNodeExtensionToReactNode<T>(
32
+ domOutputSpecToElement: DomOutputSpecToElement<T>,
33
+ extension: NodeExtension,
34
+ extensionAttributes: ExtensionAttribute[],
35
+ options?: Partial<Pick<TiptapStaticRendererOptions<T, Mark, Node>, 'unhandledNode'>>,
36
+ ): [string, (props: NodeProps<Node, T | T[]>) => T] {
37
+ const context = {
38
+ name: extension.name,
39
+ options: extension.options,
40
+ storage: extension.storage,
41
+ parent: extension.parent,
42
+ }
43
+
44
+ const renderToHTML = getExtensionField<NodeConfig['renderHTML']>(
45
+ extension,
46
+ 'renderHTML',
47
+ context,
48
+ )
49
+
50
+ if (!renderToHTML) {
51
+ if (options?.unhandledNode) {
52
+ return [extension.name, options.unhandledNode]
53
+ }
54
+ return [
55
+ extension.name,
56
+ () => {
57
+ throw new Error(
58
+ `[tiptap error]: Node ${extension.name} cannot be rendered, it is missing a "renderToHTML" method, please implement it or override the corresponding "nodeMapping" method to have a custom rendering`,
59
+ )
60
+ },
61
+ ]
62
+ }
63
+
64
+ return [
65
+ extension.name,
66
+ ({ node, children }) => {
67
+ try {
68
+ return domOutputSpecToElement(
69
+ renderToHTML({
70
+ node,
71
+ HTMLAttributes: getHTMLAttributes(node, extensionAttributes),
72
+ }),
73
+ )(children)
74
+ } catch (e) {
75
+ throw new Error(
76
+ `[tiptap error]: Node ${
77
+ extension.name
78
+ } cannot be rendered, it's "renderToHTML" method threw an error: ${(e as Error).message}`,
79
+ { cause: e },
80
+ )
81
+ }
82
+ },
83
+ ]
84
+ }
85
+
86
+ /**
87
+ * This takes a MarkExtension and maps it to a React component
88
+ * @param extension The mark extension to map to a React component
89
+ * @param extensionAttributes All available extension attributes
90
+ * @returns A tuple with the name of the extension and a React component that renders the extension
91
+ */
92
+ export function mapMarkExtensionToReactNode<T>(
93
+ domOutputSpecToElement: DomOutputSpecToElement<T>,
94
+ extension: MarkExtension,
95
+ extensionAttributes: ExtensionAttribute[],
96
+ options?: Partial<Pick<TiptapStaticRendererOptions<T, Mark, Node>, 'unhandledMark'>>,
97
+ ): [string, (props: MarkProps<Mark, T | T[]>) => T] {
98
+ const context = {
99
+ name: extension.name,
100
+ options: extension.options,
101
+ storage: extension.storage,
102
+ parent: extension.parent,
103
+ }
104
+
105
+ const renderToHTML = getExtensionField<MarkConfig['renderHTML']>(
106
+ extension,
107
+ 'renderHTML',
108
+ context,
109
+ )
110
+
111
+ if (!renderToHTML) {
112
+ if (options?.unhandledMark) {
113
+ return [extension.name, options.unhandledMark]
114
+ }
115
+ return [
116
+ extension.name,
117
+ () => {
118
+ throw new Error(
119
+ `Node ${extension.name} cannot be rendered, it is missing a "renderToHTML" method`,
120
+ )
121
+ },
122
+ ]
123
+ }
124
+
125
+ return [
126
+ extension.name,
127
+ ({ mark, children }) => {
128
+ try {
129
+ return domOutputSpecToElement(
130
+ renderToHTML({
131
+ mark,
132
+ HTMLAttributes: getHTMLAttributes(mark, extensionAttributes),
133
+ }),
134
+ )(children)
135
+ } catch (e) {
136
+ throw new Error(
137
+ `[tiptap error]: Mark ${
138
+ extension.name
139
+ } cannot be rendered, it's "renderToHTML" method threw an error: ${(e as Error).message}`,
140
+ { cause: e },
141
+ )
142
+ }
143
+ },
144
+ ]
145
+ }
146
+
147
+ /**
148
+ * This function will statically render a Prosemirror Node to a target element type using the given extensions
149
+ * @param renderer The renderer to use to render the Prosemirror Node to the target element type
150
+ * @param domOutputSpecToElement A function that takes a Prosemirror DOMOutputSpec and returns a function that takes children and returns the target element type
151
+ * @param mapDefinedTypes An object with functions to map the doc and text types to the target element type
152
+ * @param content The Prosemirror Node to render
153
+ * @param extensions The extensions to use to render the Prosemirror Node
154
+ * @param options Additional options to pass to the renderer that can override the default behavior
155
+ * @returns The rendered target element type
156
+ */
157
+ export function renderToElement<T>({
158
+ renderer,
159
+ domOutputSpecToElement,
160
+ mapDefinedTypes,
161
+ content,
162
+ extensions,
163
+ options,
164
+ }: {
165
+ renderer: (options: TiptapStaticRendererOptions<T, Mark, Node>) => (ctx: { content: Node }) => T;
166
+ domOutputSpecToElement: DomOutputSpecToElement<T>;
167
+ mapDefinedTypes: {
168
+ doc: (props: NodeProps<Node, T | T[]>) => T;
169
+ text: (props: NodeProps<Node, T | T[]>) => T;
170
+ };
171
+ content: Node | JSONContent;
172
+ extensions: Extensions;
173
+ options?: Partial<TiptapStaticRendererOptions<T, Mark, Node>>;
174
+ }): T {
175
+ // get all extensions in order & split them into nodes and marks
176
+ extensions = resolveExtensions(extensions)
177
+ const extensionAttributes = getAttributesFromExtensions(extensions)
178
+ const { nodeExtensions, markExtensions } = splitExtensions(extensions)
179
+
180
+ if (!(content instanceof Node)) {
181
+ content = Node.fromJSON(getSchemaByResolvedExtensions(extensions), content)
182
+ }
183
+
184
+ return renderer({
185
+ ...options,
186
+ nodeMapping: {
187
+ ...Object.fromEntries(
188
+ nodeExtensions
189
+ .filter(e => {
190
+ if (e.name in mapDefinedTypes) {
191
+ // These are predefined types that we don't need to map
192
+ return false
193
+ }
194
+ // No need to generate mappings for nodes that are already mapped
195
+ if (options?.nodeMapping) {
196
+ return !(e.name in options.nodeMapping)
197
+ }
198
+ return true
199
+ })
200
+ .map(nodeExtension => mapNodeExtensionToReactNode<T>(
201
+ domOutputSpecToElement,
202
+ nodeExtension,
203
+ extensionAttributes,
204
+ options,
205
+ )),
206
+ ),
207
+ ...mapDefinedTypes,
208
+ ...options?.nodeMapping,
209
+ },
210
+ markMapping: {
211
+ ...Object.fromEntries(
212
+ markExtensions
213
+ .filter(e => {
214
+ // No need to generate mappings for marks that are already mapped
215
+ if (options?.markMapping) {
216
+ return !(e.name in options.markMapping)
217
+ }
218
+ return true
219
+ })
220
+ .map(mark => mapMarkExtensionToReactNode<T>(
221
+ domOutputSpecToElement,
222
+ mark,
223
+ extensionAttributes,
224
+ options,
225
+ )),
226
+ ),
227
+ ...options?.markMapping,
228
+ },
229
+ })({ content })
230
+ }