feat: add config option to emit content error when content check is disabled

This commit is contained in:
Arnau Gómez Farell 2025-06-04 10:58:20 +02:00
parent f750c521a7
commit 1a5c5f866e
No known key found for this signature in database
4 changed files with 49 additions and 15 deletions

View File

@ -81,6 +81,7 @@ export class Editor extends EventEmitter<EditorEvents> {
enablePasteRules: true,
enableCoreExtensions: true,
enableContentCheck: false,
emitContentError: false,
onBeforeCreate: () => null,
onCreate: () => null,
onUpdate: () => null,

View File

@ -72,6 +72,18 @@ export const insertContentAt: RawCommands['insertContentAt'] = (position, value,
let content: Fragment | ProseMirrorNode
const emitContentError = (error: Error) => {
editor.emit('contentError', {
editor,
error,
disableCollaboration: () => {
if (editor.storage.collaboration) {
editor.storage.collaboration.isDisabled = true
}
},
})
}
try {
content = createNodeFromContent(value, editor.schema, {
parseOptions: {
@ -79,17 +91,10 @@ export const insertContentAt: RawCommands['insertContentAt'] = (position, value,
...options.parseOptions,
},
errorOnInvalidContent: options.errorOnInvalidContent ?? editor.options.enableContentCheck,
onIgnoredError: editor.options.emitContentError ? emitContentError : undefined,
})
} catch (e) {
editor.emit('contentError', {
editor,
error: e as Error,
disableCollaboration: () => {
if (editor.storage.collaboration) {
editor.storage.collaboration.isDisabled = true
}
},
})
emitContentError(e as Error)
return false
}

View File

@ -13,6 +13,13 @@ export type CreateNodeFromContentOptions = {
slice?: boolean
parseOptions?: ParseOptions
errorOnInvalidContent?: boolean
/**
* Runs if a content is invalid and an error would have been thrown, but
* `errorOnInvalidContent` is `false` so the invalid content is ignored.
*
* @param error The error that was not thrown
*/
onIgnoredError?: (error: Error) => void
}
/**
@ -56,11 +63,16 @@ export function createNodeFromContent(
return node
} catch (error) {
if (options.errorOnInvalidContent) {
throw new Error('[tiptap error]: Invalid JSON content', { cause: error as Error })
}
const thrownError = new Error('[tiptap error]: Invalid JSON content', { cause: error as Error })
console.warn('[tiptap warn]: Invalid content.', 'Passed value:', content, 'Error:', error)
if (options.errorOnInvalidContent) {
throw thrownError
}
if (options.onIgnoredError) {
options.onIgnoredError(thrownError)
} else {
console.warn('[tiptap warn]: Invalid content.', 'Passed value:', content, 'Error:', error)
}
return createNodeFromContent('', schema, options)
}
@ -105,8 +117,15 @@ export function createNodeFromContent(
DOMParser.fromSchema(contentCheckSchema).parse(elementFromString(content), options.parseOptions)
}
if (options.errorOnInvalidContent && hasInvalidContent) {
throw new Error('[tiptap error]: Invalid HTML content', { cause: new Error(`Invalid element found: ${invalidContent}`) })
if (hasInvalidContent) {
const thrownError = new Error('[tiptap error]: Invalid HTML content', { cause: new Error(`Invalid element found: ${invalidContent}`) })
if (options.errorOnInvalidContent) {
throw thrownError
} else if (options.onIgnoredError) {
options.onIgnoredError(thrownError)
}
}
}

View File

@ -127,6 +127,15 @@ export interface EditorOptions {
* @default false
*/
enableContentCheck: boolean;
/**
* If `true`, the editor will emit the `contentError` event invalid content is
* encountered but `enableContentCheck` is `false`. This lets you preserve the
* invalid editor content while still showing a warning or error message to
* the user.
*
* @default false
*/
emitContentError: boolean;
onBeforeCreate: (props: EditorEvents['beforeCreate']) => void;
onCreate: (props: EditorEvents['create']) => void;
/**