snapshot of https://github.com/ueberdosis/tiptap.git (main, 2026-09-15; blobs > 131072B omitted)
Some checks failed
Verify / Install node dependencies (push) Failing after 2s
Verify / Build packages (push) Has been skipped
Verify / Check linting & formatting (push) Has been skipped
Verify / Run unit tests (push) Has been skipped
Verify / Run e2e tests (shard 1/4) (push) Has been skipped
Verify / Run e2e tests (shard 2/4) (push) Has been skipped
Verify / Run e2e tests (shard 3/4) (push) Has been skipped
Verify / Run e2e tests (shard 4/4) (push) Has been skipped
Publish / Resolve publish config (push) Failing after 2s
Publish / Send notification to Slack (push) Has been skipped
Publish / Send failure notification to Slack (push) Failing after 8s
Verify / Merge e2e reports (push) Has been skipped
Publish / Build (push) Has been skipped
Publish / Bump Versions OR Release to Registry (push) Has been skipped
Some checks failed
Verify / Install node dependencies (push) Failing after 2s
Verify / Build packages (push) Has been skipped
Verify / Check linting & formatting (push) Has been skipped
Verify / Run unit tests (push) Has been skipped
Verify / Run e2e tests (shard 1/4) (push) Has been skipped
Verify / Run e2e tests (shard 2/4) (push) Has been skipped
Verify / Run e2e tests (shard 3/4) (push) Has been skipped
Verify / Run e2e tests (shard 4/4) (push) Has been skipped
Publish / Resolve publish config (push) Failing after 2s
Publish / Send notification to Slack (push) Has been skipped
Publish / Send failure notification to Slack (push) Failing after 8s
Verify / Merge e2e reports (push) Has been skipped
Publish / Build (push) Has been skipped
Publish / Bump Versions OR Release to Registry (push) Has been skipped
This commit is contained in:
145
packages/static-renderer/src/pm/html-string/html-string.ts
Normal file
145
packages/static-renderer/src/pm/html-string/html-string.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
/* oslint-disableno-explicit-any */
|
||||
import type { DOMOutputSpecArray, Extensions, JSONContent } from '@tiptap/core'
|
||||
import type { DOMOutputSpec, Mark, Node as PMNode } from '@tiptap/pm/model'
|
||||
|
||||
import {
|
||||
escapeHTML,
|
||||
renderJSONContentToString,
|
||||
serializeAttrsToHTMLString,
|
||||
serializeChildrenToHTMLString,
|
||||
} from '../../json/html-string/string.js'
|
||||
import type { TiptapStaticRendererOptions } from '../../json/renderer.js'
|
||||
import type { StaticEditorOptions } from '../extensionRenderer.js'
|
||||
import { applyStaticEditorOptionsToExtensions, renderToElement } from '../extensionRenderer.js'
|
||||
|
||||
export {
|
||||
serializeAttrsToHTMLString,
|
||||
serializeChildrenToHTMLString,
|
||||
} from '../../json/html-string/string.js'
|
||||
|
||||
/**
|
||||
* HTML elements that cannot be self-closing and must always have a closing tag.
|
||||
* These elements must be rendered as <tag></tag> even when empty, not <tag />.
|
||||
*/
|
||||
const NON_SELF_CLOSING_TAGS = new Set([
|
||||
'iframe',
|
||||
'script',
|
||||
'style',
|
||||
'title',
|
||||
'textarea',
|
||||
'div',
|
||||
'span',
|
||||
'a',
|
||||
'button',
|
||||
])
|
||||
|
||||
/**
|
||||
* Take a DOMOutputSpec and return a function that can render it to a string
|
||||
* @param content The DOMOutputSpec to convert to a string
|
||||
* @returns A function that can render the DOMOutputSpec to a string
|
||||
*/
|
||||
export function domOutputSpecToHTMLString(
|
||||
content: DOMOutputSpec,
|
||||
): (children?: string | string[]) => string {
|
||||
if (typeof content === 'string') {
|
||||
return () => escapeHTML(content)
|
||||
}
|
||||
if (typeof content === 'object' && 'length' in content) {
|
||||
const [_tag, attrs, children, ...rest] = content as DOMOutputSpecArray
|
||||
let tag = _tag
|
||||
const parts = tag.split(' ')
|
||||
|
||||
if (parts.length > 1) {
|
||||
tag = `${parts[1]} xmlns="${parts[0]}"`
|
||||
}
|
||||
|
||||
if (attrs === undefined) {
|
||||
return () => `<${tag}/>`
|
||||
}
|
||||
if (attrs === 0) {
|
||||
return child => `<${tag}>${serializeChildrenToHTMLString(child)}</${tag}>`
|
||||
}
|
||||
if (typeof attrs === 'object') {
|
||||
if (Array.isArray(attrs)) {
|
||||
if (children === undefined) {
|
||||
return child =>
|
||||
`<${tag}>${domOutputSpecToHTMLString(attrs as DOMOutputSpecArray)(child)}</${tag}>`
|
||||
}
|
||||
if (children === 0) {
|
||||
return child =>
|
||||
`<${tag}>${domOutputSpecToHTMLString(attrs as DOMOutputSpecArray)(child)}</${tag}>`
|
||||
}
|
||||
return child =>
|
||||
`<${tag}>${domOutputSpecToHTMLString(attrs as DOMOutputSpecArray)(child)}${[children]
|
||||
.concat(rest)
|
||||
.map(a => domOutputSpecToHTMLString(a)(child))}</${tag}>`
|
||||
}
|
||||
if (children === undefined) {
|
||||
if (NON_SELF_CLOSING_TAGS.has(tag)) {
|
||||
return () => `<${tag}${serializeAttrsToHTMLString(attrs)}></${tag}>`
|
||||
}
|
||||
return () => `<${tag}${serializeAttrsToHTMLString(attrs)}/>`
|
||||
}
|
||||
if (children === 0) {
|
||||
return child =>
|
||||
`<${tag}${serializeAttrsToHTMLString(attrs)}>${serializeChildrenToHTMLString(child)}</${tag}>`
|
||||
}
|
||||
|
||||
return child =>
|
||||
`<${tag}${serializeAttrsToHTMLString(attrs)}>${[children]
|
||||
.concat(rest)
|
||||
.map(a => domOutputSpecToHTMLString(a)(child))
|
||||
.join('')}</${tag}>`
|
||||
}
|
||||
}
|
||||
|
||||
// TODO support DOM elements? How to handle them?
|
||||
throw new Error(
|
||||
'[tiptap error]: Unsupported DomOutputSpec type, check the `renderHTML` method output or implement a node mapping',
|
||||
{
|
||||
cause: content,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will statically render a Prosemirror Node to HTML using the provided extensions and options.
|
||||
*
|
||||
* Limitations: this function builds the schema and runs each extension's
|
||||
* `renderHTML`, but does not instantiate an `Editor`. Extensions that mutate
|
||||
* the document inside `addProseMirrorPlugins`, `onCreate`, or transaction
|
||||
* hooks will not run. For UniqueID, pre-process the JSON with
|
||||
* `generateUniqueIds` from `@tiptap/extension-unique-id`; for TableOfContents,
|
||||
* pre-process with `generateTocIds` from `@tiptap/extension-table-of-contents`.
|
||||
*
|
||||
* @param content The content to render to HTML
|
||||
* @param extensions The extensions to use for rendering
|
||||
* @param staticEditorOptions Optional editor-level options that affect rendered output, currently `{ textDirection }`. Mirrors a subset of `EditorOptions`.
|
||||
* @param options The options to use for rendering
|
||||
* @returns The rendered HTML string
|
||||
*/
|
||||
export function renderToHTMLString({
|
||||
content,
|
||||
extensions,
|
||||
staticEditorOptions,
|
||||
options,
|
||||
}: {
|
||||
content: PMNode | JSONContent
|
||||
extensions: Extensions
|
||||
staticEditorOptions?: StaticEditorOptions
|
||||
options?: Partial<TiptapStaticRendererOptions<string, Mark, PMNode>>
|
||||
}): string {
|
||||
return renderToElement<string>({
|
||||
renderer: renderJSONContentToString,
|
||||
domOutputSpecToElement: domOutputSpecToHTMLString,
|
||||
mapDefinedTypes: {
|
||||
// Map a doc node to concatenated children
|
||||
doc: ({ children }) => serializeChildrenToHTMLString(children),
|
||||
// Map a text node to its text content
|
||||
text: ({ node }) => escapeHTML(node.text ?? ''),
|
||||
},
|
||||
content,
|
||||
extensions: applyStaticEditorOptionsToExtensions(extensions, staticEditorOptions),
|
||||
options,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user