mirror of
https://github.com/ueberdosis/tiptap.git
synced 2025-06-11 03:33:12 +08:00
fix: do not modify createNodeFromContent
This commit is contained in:
parent
3f8efb9e64
commit
42f719468b
@ -84,14 +84,28 @@ export const insertContentAt: RawCommands['insertContentAt'] = (position, value,
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
const parseOptions: ParseOptions = {
|
||||||
content = createNodeFromContent(value, editor.schema, {
|
|
||||||
parseOptions: {
|
|
||||||
preserveWhitespace: 'full',
|
preserveWhitespace: 'full',
|
||||||
...options.parseOptions,
|
...options.parseOptions,
|
||||||
},
|
}
|
||||||
|
|
||||||
|
// If `emitContentError` is enabled, we want to check the content for errors
|
||||||
|
// but ignore them (do not remove the invalid content from the document)
|
||||||
|
if (!options.errorOnInvalidContent && editor.options.enableContentCheck && editor.options.emitContentError) {
|
||||||
|
try {
|
||||||
|
createNodeFromContent(value, editor.schema, {
|
||||||
|
parseOptions,
|
||||||
|
errorOnInvalidContent: true,
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
emitContentError(e as Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
content = createNodeFromContent(value, editor.schema, {
|
||||||
|
parseOptions,
|
||||||
errorOnInvalidContent: options.errorOnInvalidContent ?? editor.options.enableContentCheck,
|
errorOnInvalidContent: options.errorOnInvalidContent ?? editor.options.enableContentCheck,
|
||||||
onIgnoredError: editor.options.emitContentError ? emitContentError : undefined,
|
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emitContentError(e as Error)
|
emitContentError(e as Error)
|
||||||
|
@ -13,13 +13,6 @@ export type CreateNodeFromContentOptions = {
|
|||||||
slice?: boolean
|
slice?: boolean
|
||||||
parseOptions?: ParseOptions
|
parseOptions?: ParseOptions
|
||||||
errorOnInvalidContent?: boolean
|
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,16 +56,11 @@ export function createNodeFromContent(
|
|||||||
|
|
||||||
return node
|
return node
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const thrownError = new Error('[tiptap error]: Invalid JSON content', { cause: error as Error })
|
|
||||||
|
|
||||||
if (options.errorOnInvalidContent) {
|
if (options.errorOnInvalidContent) {
|
||||||
throw thrownError
|
throw new Error('[tiptap error]: Invalid JSON content', { cause: error as Error })
|
||||||
}
|
}
|
||||||
if (options.onIgnoredError) {
|
|
||||||
options.onIgnoredError(thrownError)
|
|
||||||
} else {
|
|
||||||
console.warn('[tiptap warn]: Invalid content.', 'Passed value:', content, 'Error:', error)
|
console.warn('[tiptap warn]: Invalid content.', 'Passed value:', content, 'Error:', error)
|
||||||
}
|
|
||||||
|
|
||||||
return createNodeFromContent('', schema, options)
|
return createNodeFromContent('', schema, options)
|
||||||
}
|
}
|
||||||
@ -117,15 +105,8 @@ export function createNodeFromContent(
|
|||||||
DOMParser.fromSchema(contentCheckSchema).parse(elementFromString(content), options.parseOptions)
|
DOMParser.fromSchema(contentCheckSchema).parse(elementFromString(content), options.parseOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasInvalidContent) {
|
if (options.errorOnInvalidContent && hasInvalidContent) {
|
||||||
const thrownError = new Error('[tiptap error]: Invalid HTML content', { cause: new Error(`Invalid element found: ${invalidContent}`) })
|
throw 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ export interface EditorOptions {
|
|||||||
*/
|
*/
|
||||||
enableContentCheck: boolean;
|
enableContentCheck: boolean;
|
||||||
/**
|
/**
|
||||||
* If `true`, the editor will emit the `contentError` event invalid content is
|
* If `true`, the editor will emit the `contentError` event if invalid content is
|
||||||
* encountered but `enableContentCheck` is `false`. This lets you preserve the
|
* encountered but `enableContentCheck` is `false`. This lets you preserve the
|
||||||
* invalid editor content while still showing a warning or error message to
|
* invalid editor content while still showing a warning or error message to
|
||||||
* the user.
|
* the user.
|
||||||
|
@ -263,121 +263,4 @@ describe('createNodeFromContent', () => {
|
|||||||
]), { errorOnInvalidContent: true })
|
]), { errorOnInvalidContent: true })
|
||||||
}).to.throw('[tiptap error]: Invalid JSON content')
|
}).to.throw('[tiptap error]: Invalid JSON content')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('calls onIgnoredError when a schema does not have matching node types for JSON content', () => {
|
|
||||||
const content = {
|
|
||||||
type: 'non-existing-node-type',
|
|
||||||
content: [{
|
|
||||||
type: 'text',
|
|
||||||
text: 'Example Text',
|
|
||||||
}],
|
|
||||||
}
|
|
||||||
|
|
||||||
let errorCalled = false
|
|
||||||
let errorMessage = ''
|
|
||||||
|
|
||||||
const fragment = createNodeFromContent(content, getSchemaByResolvedExtensions([
|
|
||||||
Document,
|
|
||||||
Paragraph,
|
|
||||||
Text,
|
|
||||||
]), {
|
|
||||||
errorOnInvalidContent: false,
|
|
||||||
onIgnoredError: error => {
|
|
||||||
errorCalled = true
|
|
||||||
errorMessage = error.message
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(errorCalled).to.eq(true)
|
|
||||||
expect(errorMessage).to.eq('[tiptap error]: Invalid JSON content')
|
|
||||||
expect(fragment.toJSON()).to.deep.eq(null)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('calls onIgnoredError when a schema does not have matching node types for HTML content', () => {
|
|
||||||
const content = '<non-existing-node-type>Example Text</non-existing-node-type>'
|
|
||||||
|
|
||||||
let errorCalled = false
|
|
||||||
let errorMessage = ''
|
|
||||||
|
|
||||||
const fragment = createNodeFromContent(content, getSchemaByResolvedExtensions([
|
|
||||||
Document,
|
|
||||||
Paragraph,
|
|
||||||
Text,
|
|
||||||
]), {
|
|
||||||
errorOnInvalidContent: false,
|
|
||||||
onIgnoredError: error => {
|
|
||||||
errorCalled = true
|
|
||||||
errorMessage = error.message
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(errorCalled).to.eq(true)
|
|
||||||
expect(errorMessage).to.eq('[tiptap error]: Invalid HTML content')
|
|
||||||
expect(fragment.toJSON()).to.deep.eq([{ type: 'text', text: 'Example Text' }])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('calls onIgnoredError when a schema does not have matching mark types for JSON content', () => {
|
|
||||||
const content = {
|
|
||||||
type: 'paragraph',
|
|
||||||
content: [{
|
|
||||||
type: 'text',
|
|
||||||
text: 'Example Text',
|
|
||||||
marks: [{
|
|
||||||
type: 'non-existing-mark-type',
|
|
||||||
}],
|
|
||||||
}],
|
|
||||||
}
|
|
||||||
|
|
||||||
let errorCalled = false
|
|
||||||
let errorMessage = ''
|
|
||||||
|
|
||||||
const fragment = createNodeFromContent(content, getSchemaByResolvedExtensions([
|
|
||||||
Document,
|
|
||||||
Paragraph,
|
|
||||||
Text,
|
|
||||||
]), {
|
|
||||||
errorOnInvalidContent: false,
|
|
||||||
onIgnoredError: error => {
|
|
||||||
errorCalled = true
|
|
||||||
errorMessage = error.message
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(errorCalled).to.eq(true)
|
|
||||||
expect(errorMessage).to.eq('[tiptap error]: Invalid JSON content')
|
|
||||||
expect(fragment.toJSON()).to.deep.eq(null)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('calls onIgnoredError when the JSON content does not follow the nesting rules of the schema', () => {
|
|
||||||
const content = {
|
|
||||||
type: 'paragraph',
|
|
||||||
content: [{
|
|
||||||
type: 'paragraph',
|
|
||||||
content: [{
|
|
||||||
type: 'text',
|
|
||||||
text: 'Example Text',
|
|
||||||
}],
|
|
||||||
}],
|
|
||||||
}
|
|
||||||
|
|
||||||
let errorCalled = false
|
|
||||||
let errorMessage = ''
|
|
||||||
|
|
||||||
const fragment = createNodeFromContent(content, getSchemaByResolvedExtensions([
|
|
||||||
Document,
|
|
||||||
Paragraph,
|
|
||||||
Text,
|
|
||||||
]), {
|
|
||||||
errorOnInvalidContent: false,
|
|
||||||
onIgnoredError: error => {
|
|
||||||
errorCalled = true
|
|
||||||
errorMessage = error.message
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(errorCalled).to.eq(true)
|
|
||||||
expect(errorMessage).to.eq('[tiptap error]: Invalid JSON content')
|
|
||||||
expect(fragment.toJSON()).to.deep.eq(null)
|
|
||||||
})
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user