import { NodeSpec, MarkSpec, Schema } from 'prosemirror-model' import { AnyConfig, Extensions } from '../types' import { NodeConfig, MarkConfig } from '..' import splitExtensions from './splitExtensions' import getAttributesFromExtensions from './getAttributesFromExtensions' import getRenderedAttributes from './getRenderedAttributes' import isEmptyObject from '../utilities/isEmptyObject' import injectExtensionAttributesToParseRule from './injectExtensionAttributesToParseRule' import callOrReturn from '../utilities/callOrReturn' import getExtensionField from './getExtensionField' function cleanUpSchemaItem(data: T) { return Object.fromEntries(Object.entries(data).filter(([key, value]) => { if (key === 'attrs' && isEmptyObject(value)) { return false } return value !== null && value !== undefined })) as T } export default function getSchemaByResolvedExtensions(extensions: Extensions): Schema { const allAttributes = getAttributesFromExtensions(extensions) const { nodeExtensions, markExtensions } = splitExtensions(extensions) const topNode = nodeExtensions.find(extension => getExtensionField(extension, 'topNode'))?.name const nodes = Object.fromEntries(nodeExtensions.map(extension => { const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.name) const context = { name: extension.name, options: extension.options, } const extraNodeFields = extensions.reduce((fields, e) => { const extendNodeSchema = getExtensionField( e, 'extendNodeSchema', context, ) return { ...fields, ...(extendNodeSchema ? extendNodeSchema(extension) : {}), } }, {}) const schema: NodeSpec = cleanUpSchemaItem({ ...extraNodeFields, content: callOrReturn(getExtensionField(extension, 'content', context)), marks: callOrReturn(getExtensionField(extension, 'marks', context)), group: callOrReturn(getExtensionField(extension, 'group', context)), inline: callOrReturn(getExtensionField(extension, 'inline', context)), atom: callOrReturn(getExtensionField(extension, 'atom', context)), selectable: callOrReturn(getExtensionField(extension, 'selectable', context)), draggable: callOrReturn(getExtensionField(extension, 'draggable', context)), code: callOrReturn(getExtensionField(extension, 'code', context)), defining: callOrReturn(getExtensionField(extension, 'defining', context)), isolating: callOrReturn(getExtensionField(extension, 'isolating', context)), attrs: Object.fromEntries(extensionAttributes.map(extensionAttribute => { return [extensionAttribute.name, { default: extensionAttribute?.attribute?.default }] })), }) const parseHTML = callOrReturn(getExtensionField(extension, 'parseHTML', context)) if (parseHTML) { schema.parseDOM = parseHTML .map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes)) } const renderHTML = getExtensionField(extension, 'renderHTML', context) if (renderHTML) { schema.toDOM = node => renderHTML({ node, HTMLAttributes: getRenderedAttributes(node, extensionAttributes), }) } const renderText = getExtensionField(extension, 'renderText', context) if (renderText) { schema.toText = renderText } return [extension.name, schema] })) const marks = Object.fromEntries(markExtensions.map(extension => { const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.name) const context = { name: extension.name, options: extension.options, } const extraMarkFields = extensions.reduce((fields, e) => { const extendMarkSchema = getExtensionField( e, 'extendMarkSchema', context, ) return { ...fields, ...(extendMarkSchema ? extendMarkSchema(extension) : {}), } }, {}) const schema: MarkSpec = cleanUpSchemaItem({ ...extraMarkFields, inclusive: callOrReturn(getExtensionField(extension, 'inclusive', context)), excludes: callOrReturn(getExtensionField(extension, 'excludes', context)), group: callOrReturn(getExtensionField(extension, 'group', context)), spanning: callOrReturn(getExtensionField(extension, 'spanning', context)), code: callOrReturn(getExtensionField(extension, 'code', context)), attrs: Object.fromEntries(extensionAttributes.map(extensionAttribute => { return [extensionAttribute.name, { default: extensionAttribute?.attribute?.default }] })), }) const parseHTML = callOrReturn(getExtensionField(extension, 'parseHTML', context)) if (parseHTML) { schema.parseDOM = parseHTML .map(parseRule => injectExtensionAttributesToParseRule(parseRule, extensionAttributes)) } const renderHTML = getExtensionField(extension, 'renderHTML', context) if (renderHTML) { schema.toDOM = mark => renderHTML({ mark, HTMLAttributes: getRenderedAttributes(mark, extensionAttributes), }) } return [extension.name, schema] })) return new Schema({ topNode, nodes, marks, }) }