UNPKG

@curvenote/schema

Version:

Schema and markdown parser for @curvenote/editor

117 lines 4.39 kB
import { DOMSerializer } from 'prosemirror-model'; import { selectAll } from 'mystjs'; import { createDocument, Node } from './document'; import { markNames, nodeNames } from '../../types'; import { getSchema } from '../../schemas'; import { createId } from '../../utils'; /** * This uses the DOMSerializer from prosemirror-model to serialize to html * The document used is a fake one that is passed to react. */ function getSerializer(schema) { const nodes = Object.fromEntries(Object.entries(schema.nodes) .map(([k, nodeType]) => { const { toDOM } = nodeType.spec; if (!toDOM) return undefined; const wrapper = (node) => { const spec = toDOM(node); return [`${node.type.name} ${spec[0]}`, ...spec.slice(1)]; }; return [k, wrapper]; }) .filter((v) => v)); // We must include the text node! if (!nodes.text) nodes.text = (node) => node.text; const marks = Object.fromEntries(Object.entries(schema.marks) .map(([k, markType]) => { const { toDOM } = markType.spec; if (!toDOM) return undefined; const wrapper = (mark, inline) => { const spec = toDOM(mark, inline); return [`${mark.type.name} ${spec[0]}`, ...spec.slice(1)]; }; return [k, wrapper]; }) .filter((v) => v)); return new DOMSerializer(nodes, marks); } export function nodeToMdast(fragment, schema, opts) { if (fragment.length === 0) return []; return fragment.map((node) => { if (node instanceof Node) { const children = nodeToMdast(node.children, schema, opts); const props = Object.assign(Object.assign({ key: node.id, tag: node.tag, name: node.name }, node.attrs), { children }); if (node.name in nodeNames) { return schema.nodes[node.name].spec.toMyst(props, opts); } if (node.name in markNames) { return schema.marks[node.name].spec.toMyst(props, opts); } throw new Error(`Node for "${node.name}" is not defined.`); } const textNode = { type: 'text', value: node.text }; return textNode; }); } export function convertToMdast(node, opts) { const schema = getSchema(opts.useSchema || 'full'); const serializer = getSerializer(schema); const dom = serializer.serializeFragment(node.content, { document: createDocument(), }); const root = { type: 'root', children: nodeToMdast(dom.children, schema, opts) }; const footnotes = selectAll('inlineFootnote', root); const footnoteDefinitions = []; footnotes.forEach((footnote) => { const id = createId(); footnoteDefinitions.push({ type: 'footnoteDefinition', identifier: id, label: id, children: footnote.children, }); delete footnote.children; const ref = footnote; ref.type = 'footnoteReference'; ref.identifier = id; ref.label = id; }); root.children.push(...footnoteDefinitions); return root; } export function convertToMdastSnippet(node, opts) { var _a, _b; if (node.type.name === 'doc') { throw new Error('The requested mdast snippet is a document, use convertToMdast.'); } // If the node is not a top level, wrap it in a document and return that! const schema = getSchema(opts.useSchema || 'full'); const { doc } = schema.nodes; const wrapped = doc.create({}, [node]); return (_b = (_a = convertToMdast(wrapped, opts)) === null || _a === void 0 ? void 0 : _a.children) === null || _b === void 0 ? void 0 : _b[0]; } export function transformNumericalFootnotes(root) { const defLookup = {}; const defs = selectAll('footnoteDefinition', root); defs.forEach((def) => { defLookup[def.identifier] = def; }); const refs = selectAll('footnoteReference', root); refs.forEach((ref, index) => { const refDef = defLookup[ref.identifier]; let id = String(index + 1); if (refDef.identifier !== ref.identifier) { id = refDef.identifier; } refDef.identifier = id; refDef.label = id; ref.identifier = id; ref.label = id; }); return root; } //# sourceMappingURL=convertToMdast.js.map