@curvenote/schema
Version:
Schema and markdown parser for @curvenote/editor
117 lines • 4.39 kB
JavaScript
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