prosemirror-remark
Version:
ProseMirror Remark integration
1,826 lines • 96.8 kB
JavaScript
import { NodeExtension, createProseMirrorNode, MarkExtension, MarkInputRule, Extension } from "prosemirror-unified";
import remarkParse from "remark-parse";
import remarkStringify from "remark-stringify";
import { wrapIn, toggleMark, chainCommands, exitCode, setBlockType } from "prosemirror-commands";
import { wrappingInputRule, textblockTypeInputRule, InputRule } from "prosemirror-inputrules";
import { Selection } from "prosemirror-state";
import { sinkListItem, liftListItem, splitListItem, wrapInList } from "prosemirror-schema-list";
class BlockquoteExtension extends NodeExtension {
proseMirrorInputRules(proseMirrorSchema) {
return [
wrappingInputRule(
/^\s{0,3}>\s$/u,
proseMirrorSchema.nodes[this.proseMirrorNodeName()]
)
];
}
proseMirrorKeymap(proseMirrorSchema) {
return {
"Mod->": wrapIn(proseMirrorSchema.nodes[this.proseMirrorNodeName()])
};
}
proseMirrorNodeName() {
return "blockquote";
}
proseMirrorNodeSpec() {
return {
content: "block+",
group: "block",
parseDOM: [{ tag: "blockquote" }],
toDOM() {
return ["blockquote", 0];
}
};
}
proseMirrorNodeToUnistNodes(_node, convertedChildren) {
return [
{
children: convertedChildren,
type: this.unistNodeName()
}
];
}
unistNodeName() {
return "blockquote";
}
unistNodeToProseMirrorNodes(_node, proseMirrorSchema, convertedChildren) {
return createProseMirrorNode(
this.proseMirrorNodeName(),
proseMirrorSchema,
convertedChildren
);
}
}
class BoldExtension extends MarkExtension {
processConvertedUnistNode(convertedNode) {
return { children: [convertedNode], type: this.unistNodeName() };
}
proseMirrorInputRules(proseMirrorSchema) {
return [
new MarkInputRule(
/\*\*([^\s](?:.*[^\s])?)\*\*([\s\S])$/u,
proseMirrorSchema.marks[this.proseMirrorMarkName()]
),
new MarkInputRule(
/__([^\s](?:.*[^\s])?)__([\s\S])$/u,
proseMirrorSchema.marks[this.proseMirrorMarkName()]
)
];
}
proseMirrorKeymap(proseMirrorSchema) {
const markType = proseMirrorSchema.marks[this.proseMirrorMarkName()];
return {
"Mod-b": toggleMark(markType),
"Mod-B": toggleMark(markType)
};
}
proseMirrorMarkName() {
return "strong";
}
proseMirrorMarkSpec() {
return {
parseDOM: [
{ tag: "b" },
{ tag: "strong" },
{
getAttrs: (value) => /^(bold(er)?|[5-9]\d{2,})$/u.test(value) && null,
style: "font-weight"
}
],
toDOM() {
return ["strong", 0];
}
};
}
unistNodeName() {
return "strong";
}
unistNodeToProseMirrorNodes(_node, proseMirrorSchema, convertedChildren) {
return convertedChildren.map(
(child) => child.mark(
child.marks.concat([
proseMirrorSchema.marks[this.proseMirrorMarkName()].create()
])
)
);
}
}
class BreakExtension extends NodeExtension {
proseMirrorKeymap(proseMirrorSchema) {
const command = chainCommands(exitCode, (state, dispatch) => {
if (dispatch) {
dispatch(
state.tr.replaceSelectionWith(
proseMirrorSchema.nodes[this.proseMirrorNodeName()].create()
).scrollIntoView()
);
}
return true;
});
const isMac = typeof navigator !== "undefined" ? /Mac|iP(hone|[oa]d)/u.test(navigator.platform) : false;
return {
"Mod-Enter": command,
"Shift-Enter": command,
...isMac && { "Ctrl-Enter": command }
};
}
proseMirrorNodeName() {
return "hard_break";
}
proseMirrorNodeSpec() {
return {
group: "inline",
inline: true,
parseDOM: [{ tag: "br" }],
selectable: false,
toDOM() {
return ["br"];
}
};
}
proseMirrorNodeToUnistNodes() {
return [{ type: this.unistNodeName() }];
}
unistNodeName() {
return "break";
}
unistNodeToProseMirrorNodes(_node, proseMirrorSchema, convertedChildren) {
return createProseMirrorNode(
this.proseMirrorNodeName(),
proseMirrorSchema,
convertedChildren
);
}
}
class TextExtension extends NodeExtension {
proseMirrorNodeName() {
return "text";
}
proseMirrorNodeSpec() {
return {
group: "inline"
};
}
proseMirrorNodeToUnistNodes(node2) {
return [{ type: this.unistNodeName(), value: node2.text ?? "" }];
}
unistNodeName() {
return "text";
}
unistNodeToProseMirrorNodes(node2, proseMirrorSchema) {
return [proseMirrorSchema.text(node2.value)];
}
}
class CodeBlockExtension extends NodeExtension {
static liftOutOfCodeBlock() {
return (state, dispatch) => {
const { $from, $to } = state.selection;
if (
// Mustn't be a complex selection
!$from.sameParent($to) || // Must be in a code block
$from.parent.type.name !== "code_block" || // Must be at the end of the code block
$from.parentOffset !== $from.parent.content.size || // There must already be a preceding empty line
!$from.parent.textBetween(0, $from.parentOffset).endsWith("\n\n")
) {
return false;
}
if (dispatch) {
const tr = state.tr;
dispatch(
tr.deleteRange($from.pos - 2, $from.pos).insert(
$from.pos - 1,
tr.doc.type.schema.nodes["paragraph"].create()
).setSelection(Selection.near(tr.doc.resolve($from.pos), 1)).scrollIntoView()
);
}
return true;
};
}
dependencies() {
return [new TextExtension()];
}
proseMirrorInputRules(proseMirrorSchema) {
return [
textblockTypeInputRule(
/^\s{0,3}```$/u,
proseMirrorSchema.nodes[this.proseMirrorNodeName()]
),
textblockTypeInputRule(
/^\s{4}$/u,
proseMirrorSchema.nodes[this.proseMirrorNodeName()]
)
];
}
proseMirrorKeymap(proseMirrorSchema) {
return {
Enter: CodeBlockExtension.liftOutOfCodeBlock(),
"Shift-Mod-\\": setBlockType(
proseMirrorSchema.nodes[this.proseMirrorNodeName()]
)
};
}
proseMirrorNodeName() {
return "code_block";
}
proseMirrorNodeSpec() {
return {
code: true,
content: "text*",
defining: true,
group: "block",
marks: "",
parseDOM: [{ preserveWhitespace: "full", tag: "pre" }],
toDOM() {
return ["pre", ["code", 0]];
}
};
}
proseMirrorNodeToUnistNodes(_node, convertedChildren) {
return [
{
type: this.unistNodeName(),
value: convertedChildren.map((child) => child.value).join("")
}
];
}
unistNodeName() {
return "code";
}
unistNodeToProseMirrorNodes(node2, proseMirrorSchema) {
return createProseMirrorNode(
this.proseMirrorNodeName(),
proseMirrorSchema,
[proseMirrorSchema.text(node2.value)]
);
}
}
class DefinitionExtension extends NodeExtension {
proseMirrorNodeName() {
return null;
}
proseMirrorNodeSpec() {
return null;
}
proseMirrorNodeToUnistNodes() {
return [];
}
unistNodeName() {
return "definition";
}
unistNodeToProseMirrorNodes(node2, _proseMirrorSchema, _convertedChildren, context) {
context.DefinitionExtension ??= { definitions: {} };
context.DefinitionExtension.definitions[node2.identifier] = {
title: node2.title,
url: node2.url
};
return [];
}
}
class ParagraphExtension extends NodeExtension {
proseMirrorNodeName() {
return "paragraph";
}
proseMirrorNodeSpec() {
return {
content: "inline*",
group: "block",
parseDOM: [{ tag: "p" }],
toDOM() {
return ["p", 0];
}
};
}
proseMirrorNodeToUnistNodes(_node, convertedChildren) {
return [{ children: convertedChildren, type: this.unistNodeName() }];
}
unistNodeName() {
return "paragraph";
}
unistNodeToProseMirrorNodes(_node, proseMirrorSchema, convertedChildren) {
return createProseMirrorNode(
this.proseMirrorNodeName(),
proseMirrorSchema,
convertedChildren
);
}
}
class HeadingExtension extends NodeExtension {
static headingLevelCommandBuilder(proseMirrorSchema, levelUpdate, onlyAtStart) {
return (state, dispatch, view) => {
if (onlyAtStart && !HeadingExtension.isAtStart(state, view)) {
return false;
}
const { $anchor } = state.selection;
const headingNode = $anchor.parent;
if (headingNode.type.name !== "heading") {
return false;
}
const newHeadingLevel = headingNode.attrs["level"] + levelUpdate;
if (newHeadingLevel < 0 || newHeadingLevel > 6) {
return false;
}
if (dispatch === void 0) {
return true;
}
const headingPosition = $anchor.before($anchor.depth);
if (newHeadingLevel > 0) {
dispatch(
state.tr.setNodeMarkup(headingPosition, void 0, {
level: newHeadingLevel
})
);
} else {
dispatch(
state.tr.setNodeMarkup(
headingPosition,
proseMirrorSchema.nodes["paragraph"]
)
);
}
return true;
};
}
static isAtStart(state, view) {
if (!state.selection.empty) {
return false;
}
if (view !== void 0) {
return view.endOfTextblock("backward", state);
}
return state.selection.$anchor.parentOffset > 0;
}
dependencies() {
return [new ParagraphExtension(), new TextExtension()];
}
proseMirrorInputRules(proseMirrorSchema) {
return [
textblockTypeInputRule(
/^\s{0,3}(#{1,6})\s$/u,
proseMirrorSchema.nodes[this.proseMirrorNodeName()],
(match) => ({ level: match[1].length })
)
];
}
proseMirrorKeymap(proseMirrorSchema) {
const keymap = {
"#": HeadingExtension.headingLevelCommandBuilder(
proseMirrorSchema,
1,
true
),
Backspace: HeadingExtension.headingLevelCommandBuilder(
proseMirrorSchema,
-1,
true
),
"Shift-Tab": HeadingExtension.headingLevelCommandBuilder(
proseMirrorSchema,
-1,
false
),
Tab: HeadingExtension.headingLevelCommandBuilder(
proseMirrorSchema,
1,
false
)
};
for (let i = 1; i <= 6; i++) {
keymap[`Shift-Mod-${i.toString()}`] = setBlockType(
proseMirrorSchema.nodes[this.proseMirrorNodeName()],
{ level: i }
);
}
return keymap;
}
proseMirrorNodeName() {
return "heading";
}
proseMirrorNodeSpec() {
return {
attrs: { level: { default: 1 } },
content: "text*",
defining: true,
group: "block",
parseDOM: [
{ attrs: { level: 1 }, tag: "h1" },
{ attrs: { level: 2 }, tag: "h2" },
{ attrs: { level: 3 }, tag: "h3" },
{ attrs: { level: 4 }, tag: "h4" },
{ attrs: { level: 5 }, tag: "h5" },
{ attrs: { level: 6 }, tag: "h6" }
],
toDOM(node2) {
return [`h${node2.attrs["level"].toString()}`, 0];
}
};
}
proseMirrorNodeToUnistNodes(node2, convertedChildren) {
return [
{
children: convertedChildren,
depth: node2.attrs["level"],
type: this.unistNodeName()
}
];
}
unistNodeName() {
return "heading";
}
unistNodeToProseMirrorNodes(node2, proseMirrorSchema, convertedChildren) {
return createProseMirrorNode(
this.proseMirrorNodeName(),
proseMirrorSchema,
convertedChildren,
{
level: node2.depth
}
);
}
}
class HorizontalRuleExtension extends NodeExtension {
proseMirrorInputRules(proseMirrorSchema) {
return [
new InputRule(
/^\s{0,3}(?:\*\*\*|---|___)\n$/u,
(state, _, start, end) => state.tr.replaceWith(
start,
end,
createProseMirrorNode(
this.proseMirrorNodeName(),
proseMirrorSchema,
[]
)
)
)
];
}
proseMirrorKeymap(proseMirrorSchema) {
return {
"Mod-_": (state, dispatch) => {
if (dispatch) {
dispatch(
state.tr.replaceSelectionWith(
proseMirrorSchema.nodes[this.proseMirrorNodeName()].create()
).scrollIntoView()
);
}
return true;
}
};
}
proseMirrorNodeName() {
return "horizontal_rule";
}
proseMirrorNodeSpec() {
return {
group: "block",
parseDOM: [{ tag: "hr" }],
toDOM() {
return ["div", ["hr"]];
}
};
}
proseMirrorNodeToUnistNodes() {
return [
{
type: this.unistNodeName()
}
];
}
unistNodeName() {
return "thematicBreak";
}
unistNodeToProseMirrorNodes(_node, proseMirrorSchema, convertedChildren) {
return createProseMirrorNode(
this.proseMirrorNodeName(),
proseMirrorSchema,
convertedChildren
);
}
}
class ImageExtension extends NodeExtension {
dependencies() {
return [new ParagraphExtension()];
}
proseMirrorNodeName() {
return "image";
}
proseMirrorNodeSpec() {
return {
attrs: {
alt: { default: null },
src: {},
title: { default: null }
},
draggable: true,
group: "inline",
inline: true,
parseDOM: [
{
getAttrs(dom) {
return {
alt: dom.getAttribute("alt"),
src: dom.getAttribute("src"),
title: dom.getAttribute("title")
};
},
tag: "img[src]"
}
],
toDOM(node2) {
return ["img", node2.attrs];
}
};
}
proseMirrorNodeToUnistNodes(node2) {
return [
{
type: this.unistNodeName(),
url: node2.attrs["src"],
...node2.attrs["alt"] !== null && { alt: node2.attrs["alt"] },
...node2.attrs["title"] !== null && {
title: node2.attrs["title"]
}
}
];
}
unistNodeName() {
return "image";
}
unistNodeToProseMirrorNodes(node2, proseMirrorSchema, convertedChildren) {
return createProseMirrorNode(
this.proseMirrorNodeName(),
proseMirrorSchema,
convertedChildren,
{
alt: node2.alt,
src: node2.url,
title: node2.title
}
);
}
}
class ImageReferenceExtension extends NodeExtension {
dependencies() {
return [new DefinitionExtension(), new ImageExtension()];
}
postUnistToProseMirrorHook(context) {
if (context.ImageReferenceExtension === void 0 || context.DefinitionExtension === void 0) {
return;
}
for (const id in context.ImageReferenceExtension.proseMirrorNodes) {
if (!(id in context.DefinitionExtension.definitions)) {
continue;
}
const definition2 = context.DefinitionExtension.definitions[id];
const attrs = context.ImageReferenceExtension.proseMirrorNodes[id].attrs;
attrs["src"] = definition2.url;
if (definition2.title !== void 0) {
attrs["title"] = definition2.title;
}
}
}
proseMirrorNodeName() {
return null;
}
proseMirrorNodeSpec() {
return null;
}
proseMirrorNodeToUnistNodes() {
return [];
}
unistNodeName() {
return "imageReference";
}
unistNodeToProseMirrorNodes(node2, proseMirrorSchema, convertedChildren, context) {
const proseMirrorNode = proseMirrorSchema.nodes["image"].createAndFill(
{ alt: node2.alt, src: "", title: node2.label },
convertedChildren
);
if (proseMirrorNode === null) {
return [];
}
context.ImageReferenceExtension ??= { proseMirrorNodes: {} };
context.ImageReferenceExtension.proseMirrorNodes[node2.identifier] = proseMirrorNode;
return [proseMirrorNode];
}
}
class InlineCodeExtension extends MarkExtension {
processConvertedUnistNode(convertedNode) {
return { type: this.unistNodeName(), value: convertedNode.value };
}
proseMirrorInputRules(proseMirrorSchema) {
return [
new MarkInputRule(
/`([^\s](?:.*[^\s])?)`([\s\S])$/u,
proseMirrorSchema.marks[this.proseMirrorMarkName()]
)
];
}
proseMirrorKeymap(proseMirrorSchema) {
const markType = proseMirrorSchema.marks[this.proseMirrorMarkName()];
return {
"Ctrl-`": toggleMark(markType)
};
}
proseMirrorMarkName() {
return "code";
}
proseMirrorMarkSpec() {
return {
inclusive: false,
parseDOM: [{ tag: "code" }],
toDOM() {
return ["code", 0];
}
};
}
unistNodeName() {
return "inlineCode";
}
unistNodeToProseMirrorNodes(node2, proseMirrorSchema) {
return [
proseMirrorSchema.text(node2.value).mark([proseMirrorSchema.marks[this.proseMirrorMarkName()].create()])
];
}
}
class ItalicExtension extends MarkExtension {
processConvertedUnistNode(convertedNode) {
return { children: [convertedNode], type: this.unistNodeName() };
}
proseMirrorInputRules(proseMirrorSchema) {
return [
new MarkInputRule(
new RegExp("(?<!\\*)\\*([^\\s*](?:.*[^\\s])?)\\*([^*])$", "u"),
proseMirrorSchema.marks[this.proseMirrorMarkName()]
),
new MarkInputRule(
new RegExp("(?<!_)_([^\\s_](?:.*[^\\s])?)_([^_])$", "u"),
proseMirrorSchema.marks[this.proseMirrorMarkName()]
)
];
}
proseMirrorKeymap(proseMirrorSchema) {
const markType = proseMirrorSchema.marks[this.proseMirrorMarkName()];
return {
"Mod-i": toggleMark(markType),
"Mod-I": toggleMark(markType)
};
}
proseMirrorMarkName() {
return "em";
}
proseMirrorMarkSpec() {
return {
parseDOM: [
{ tag: "i" },
{ tag: "em" },
{
getAttrs: (value) => value === "italic" && null,
style: "font-style"
}
],
toDOM() {
return ["em", 0];
}
};
}
unistNodeName() {
return "emphasis";
}
unistNodeToProseMirrorNodes(_node, proseMirrorSchema, convertedChildren) {
return convertedChildren.map(
(child) => child.mark(
child.marks.concat([
proseMirrorSchema.marks[this.proseMirrorMarkName()].create()
])
)
);
}
}
class LinkExtension extends MarkExtension {
processConvertedUnistNode(convertedNode, originalMark) {
return {
type: this.unistNodeName(),
url: originalMark.attrs["href"],
...originalMark.attrs["title"] !== null && {
title: originalMark.attrs["title"]
},
children: [convertedNode]
};
}
proseMirrorMarkName() {
return "link";
}
proseMirrorMarkSpec() {
return {
attrs: { href: {}, title: { default: null } },
inclusive: false,
parseDOM: [
{
getAttrs(dom) {
return {
href: dom.getAttribute("href"),
title: dom.getAttribute("title")
};
},
tag: "a[href]"
}
],
toDOM(node2) {
return ["a", node2.attrs, 0];
}
};
}
unistNodeName() {
return "link";
}
unistNodeToProseMirrorNodes(node2, proseMirrorSchema, convertedChildren) {
return convertedChildren.map(
(child) => child.mark(
child.marks.concat([
proseMirrorSchema.marks[this.proseMirrorMarkName()].create({
href: node2.url,
title: node2.title
})
])
)
);
}
}
class LinkReferenceExtension extends MarkExtension {
dependencies() {
return [new DefinitionExtension(), new LinkExtension()];
}
postUnistToProseMirrorHook(context) {
if (context.LinkReferenceExtension === void 0 || context.DefinitionExtension === void 0) {
return;
}
for (const id in context.LinkReferenceExtension.marks) {
if (!(id in context.DefinitionExtension.definitions)) {
continue;
}
const definition2 = context.DefinitionExtension.definitions[id];
const attrs = context.LinkReferenceExtension.marks[id].attrs;
attrs["href"] = definition2.url;
if (definition2.title !== void 0) {
attrs["title"] = definition2.title;
}
}
}
processConvertedUnistNode(convertedNode) {
return convertedNode;
}
proseMirrorMarkName() {
return null;
}
proseMirrorMarkSpec() {
return null;
}
unistNodeName() {
return "linkReference";
}
unistNodeToProseMirrorNodes(node2, proseMirrorSchema, convertedChildren, context) {
const mark = proseMirrorSchema.marks["link"].create({
href: null,
title: null
});
context.LinkReferenceExtension ??= { marks: {} };
context.LinkReferenceExtension.marks[node2.identifier] = mark;
return convertedChildren.map(
(child) => child.mark(child.marks.concat([mark]))
);
}
}
class ListItemExtension extends NodeExtension {
proseMirrorKeymap(proseMirrorSchema) {
const nodeType = proseMirrorSchema.nodes[this.proseMirrorNodeName()];
return {
Enter: splitListItem(nodeType),
"Shift-Tab": liftListItem(nodeType),
Tab: sinkListItem(nodeType)
};
}
proseMirrorNodeName() {
return "regular_list_item";
}
proseMirrorNodeSpec() {
return {
content: "paragraph block*",
defining: true,
group: "list_item",
parseDOM: [{ tag: "li" }],
toDOM() {
return ["li", 0];
}
};
}
proseMirrorNodeToUnistNodes(_node, convertedChildren) {
return [
{
children: convertedChildren,
type: this.unistNodeName()
}
];
}
unistNodeName() {
return "listItem";
}
unistNodeToProseMirrorNodes(_node, proseMirrorSchema, convertedChildren) {
return createProseMirrorNode(
this.proseMirrorNodeName(),
proseMirrorSchema,
convertedChildren
);
}
unistToProseMirrorTest(node2) {
return node2.type === this.unistNodeName() && (!("checked" in node2) || typeof node2.checked !== "boolean");
}
}
class OrderedListExtension extends NodeExtension {
dependencies() {
return [new ListItemExtension()];
}
proseMirrorInputRules(proseMirrorSchema) {
return [
wrappingInputRule(
/^\s{0,3}(\d+)\.\s$/u,
proseMirrorSchema.nodes[this.proseMirrorNodeName()],
(match) => ({ start: +match[1] }),
(match, node2) => node2.childCount + node2.attrs["start"] === +match[1]
)
];
}
proseMirrorKeymap(proseMirrorSchema) {
return {
"Shift-Mod-9": wrapInList(
proseMirrorSchema.nodes[this.proseMirrorNodeName()]
)
};
}
proseMirrorNodeName() {
return "ordered_list";
}
proseMirrorNodeSpec() {
return {
attrs: { spread: { default: false }, start: { default: 1 } },
content: "list_item+",
group: "block",
parseDOM: [
{
getAttrs(dom) {
const start = dom.getAttribute("start");
return {
spread: dom.getAttribute("data-spread") === "true",
start: start !== null ? parseInt(start, 10) : 1
};
},
tag: "ol"
}
],
toDOM(node2) {
return [
"ol",
{
"data-spread": node2.attrs["spread"],
start: node2.attrs["start"]
},
0
];
}
};
}
proseMirrorNodeToUnistNodes(node2, convertedChildren) {
const spread = node2.attrs["spread"];
return [
{
children: convertedChildren.map((child) => {
child.spread = spread;
return child;
}),
ordered: true,
spread,
start: node2.attrs["start"],
type: this.unistNodeName()
}
];
}
unistNodeName() {
return "list";
}
unistNodeToProseMirrorNodes(node2, proseMirrorSchema, convertedChildren) {
return createProseMirrorNode(
this.proseMirrorNodeName(),
proseMirrorSchema,
convertedChildren,
{
spread: node2.spread,
start: node2.start ?? 1
}
);
}
unistToProseMirrorTest(node2) {
return node2.type === this.unistNodeName() && node2.ordered === true;
}
}
class RootExtension extends NodeExtension {
proseMirrorNodeName() {
return "doc";
}
proseMirrorNodeSpec() {
return { content: "block+" };
}
proseMirrorNodeToUnistNodes(_node, convertedChildren) {
return [{ children: convertedChildren, type: this.unistNodeName() }];
}
unistNodeName() {
return "root";
}
unistNodeToProseMirrorNodes(_node, proseMirrorSchema, convertedChildren) {
return createProseMirrorNode(
this.proseMirrorNodeName(),
proseMirrorSchema,
convertedChildren
);
}
}
class UnorderedListExtension extends NodeExtension {
dependencies() {
return [new ListItemExtension()];
}
proseMirrorInputRules(proseMirrorSchema) {
return [
wrappingInputRule(
/^\s{0,3}([-+*])\s$/u,
proseMirrorSchema.nodes[this.proseMirrorNodeName()]
)
];
}
proseMirrorKeymap(proseMirrorSchema) {
return {
"Shift-Mod-8": wrapInList(
proseMirrorSchema.nodes[this.proseMirrorNodeName()]
)
};
}
proseMirrorNodeName() {
return "bullet_list";
}
proseMirrorNodeSpec() {
return {
attrs: { spread: { default: false } },
content: "list_item+",
group: "block",
parseDOM: [
{
getAttrs(dom) {
return {
spread: dom.getAttribute("data-spread") === "true"
};
},
tag: "ul"
}
],
toDOM(node2) {
return ["ul", { "data-spread": node2.attrs["spread"] }, 0];
}
};
}
proseMirrorNodeToUnistNodes(node2, convertedChildren) {
const spread = node2.attrs["spread"];
return [
{
children: convertedChildren.map((child) => {
child.spread = spread;
return child;
}),
ordered: false,
spread,
type: this.unistNodeName()
}
];
}
unistNodeName() {
return "list";
}
unistNodeToProseMirrorNodes(node2, proseMirrorSchema, convertedChildren) {
return createProseMirrorNode(
this.proseMirrorNodeName(),
proseMirrorSchema,
convertedChildren,
{
spread: node2.spread
}
);
}
unistToProseMirrorTest(node2) {
return node2.type === this.unistNodeName() && node2.ordered !== true;
}
}
class MarkdownExtension extends Extension {
dependencies() {
return [
// ParagraphExtension needs to be first so that it is the default block.
new ParagraphExtension(),
new BlockquoteExtension(),
new BoldExtension(),
new BreakExtension(),
new CodeBlockExtension(),
new DefinitionExtension(),
new HeadingExtension(),
new HorizontalRuleExtension(),
new ImageExtension(),
new ImageReferenceExtension(),
new InlineCodeExtension(),
new ItalicExtension(),
new LinkExtension(),
new LinkReferenceExtension(),
new ListItemExtension(),
new OrderedListExtension(),
new RootExtension(),
new TextExtension(),
new UnorderedListExtension()
];
}
unifiedInitializationHook(processor) {
return processor.use(remarkParse).use(remarkStringify, {
fences: true,
listItemIndent: "one",
resourceLink: true,
rule: "-"
});
}
}
function ccount(value, character) {
const source = String(value);
if (typeof character !== "string") {
throw new TypeError("Expected character");
}
let count = 0;
let index = source.indexOf(character);
while (index !== -1) {
count++;
index = source.indexOf(character, index + character.length);
}
return count;
}
function ok$1() {
}
const asciiAlpha = regexCheck(/[A-Za-z]/);
const asciiAlphanumeric = regexCheck(/[\dA-Za-z]/);
function asciiControl(code2) {
return (
// Special whitespace codes (which have negative values), C0 and Control
// character DEL
code2 !== null && (code2 < 32 || code2 === 127)
);
}
function markdownLineEnding(code2) {
return code2 !== null && code2 < -2;
}
function markdownLineEndingOrSpace(code2) {
return code2 !== null && (code2 < 0 || code2 === 32);
}
function markdownSpace(code2) {
return code2 === -2 || code2 === -1 || code2 === 32;
}
const unicodePunctuation = regexCheck(new RegExp("\\p{P}|\\p{S}", "u"));
const unicodeWhitespace = regexCheck(/\s/);
function regexCheck(regex) {
return check;
function check(code2) {
return code2 !== null && code2 > -1 && regex.test(String.fromCharCode(code2));
}
}
function escapeStringRegexp(string) {
if (typeof string !== "string") {
throw new TypeError("Expected a string");
}
return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
}
const convert = (
// Note: overloads in JSDoc can’t yet use different `@template`s.
/**
* @type {(
* (<Condition extends string>(test: Condition) => (node: unknown, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node & {type: Condition}) &
* (<Condition extends Props>(test: Condition) => (node: unknown, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node & Condition) &
* (<Condition extends TestFunction>(test: Condition) => (node: unknown, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node & Predicate<Condition, Node>) &
* ((test?: null | undefined) => (node?: unknown, index?: number | null | undefined, parent?: Parent | null | undefined, context?: unknown) => node is Node) &
* ((test?: Test) => Check)
* )}
*/
/**
* @param {Test} [test]
* @returns {Check}
*/
(function(test) {
if (test === null || test === void 0) {
return ok;
}
if (typeof test === "function") {
return castFactory(test);
}
if (typeof test === "object") {
return Array.isArray(test) ? anyFactory(test) : (
// Cast because `ReadonlyArray` goes into the above but `isArray`
// narrows to `Array`.
propertiesFactory(
/** @type {Props} */
test
)
);
}
if (typeof test === "string") {
return typeFactory(test);
}
throw new Error("Expected function, string, or object as test");
})
);
function anyFactory(tests) {
const checks = [];
let index = -1;
while (++index < tests.length) {
checks[index] = convert(tests[index]);
}
return castFactory(any);
function any(...parameters) {
let index2 = -1;
while (++index2 < checks.length) {
if (checks[index2].apply(this, parameters)) return true;
}
return false;
}
}
function propertiesFactory(check) {
const checkAsRecord = (
/** @type {Record<string, unknown>} */
check
);
return castFactory(all2);
function all2(node2) {
const nodeAsRecord = (
/** @type {Record<string, unknown>} */
/** @type {unknown} */
node2
);
let key;
for (key in check) {
if (nodeAsRecord[key] !== checkAsRecord[key]) return false;
}
return true;
}
}
function typeFactory(check) {
return castFactory(type);
function type(node2) {
return node2 && node2.type === check;
}
}
function castFactory(testFunction) {
return check;
function check(value, index, parent) {
return Boolean(
looksLikeANode(value) && testFunction.call(
this,
value,
typeof index === "number" ? index : void 0,
parent || void 0
)
);
}
}
function ok() {
return true;
}
function looksLikeANode(value) {
return value !== null && typeof value === "object" && "type" in value;
}
function color(d) {
return d;
}
const empty = [];
const CONTINUE = true;
const EXIT = false;
const SKIP = "skip";
function visitParents(tree, test, visitor, reverse) {
let check;
if (typeof test === "function" && typeof visitor !== "function") {
reverse = visitor;
visitor = test;
} else {
check = test;
}
const is = convert(check);
const step = reverse ? -1 : 1;
factory(tree, void 0, [])();
function factory(node2, index, parents) {
const value = (
/** @type {Record<string, unknown>} */
node2 && typeof node2 === "object" ? node2 : {}
);
if (typeof value.type === "string") {
const name = (
// `hast`
typeof value.tagName === "string" ? value.tagName : (
// `xast`
typeof value.name === "string" ? value.name : void 0
)
);
Object.defineProperty(visit2, "name", {
value: "node (" + color(node2.type + (name ? "<" + name + ">" : "")) + ")"
});
}
return visit2;
function visit2() {
let result = empty;
let subresult;
let offset;
let grandparents;
if (!test || is(node2, index, parents[parents.length - 1] || void 0)) {
result = toResult(visitor(node2, parents));
if (result[0] === EXIT) {
return result;
}
}
if ("children" in node2 && node2.children) {
const nodeAsParent = (
/** @type {UnistParent} */
node2
);
if (nodeAsParent.children && result[0] !== SKIP) {
offset = (reverse ? nodeAsParent.children.length : -1) + step;
grandparents = parents.concat(nodeAsParent);
while (offset > -1 && offset < nodeAsParent.children.length) {
const child = nodeAsParent.children[offset];
subresult = factory(child, offset, grandparents)();
if (subresult[0] === EXIT) {
return subresult;
}
offset = typeof subresult[1] === "number" ? subresult[1] : offset + step;
}
}
}
return result;
}
}
}
function toResult(value) {
if (Array.isArray(value)) {
return value;
}
if (typeof value === "number") {
return [CONTINUE, value];
}
return value === null || value === void 0 ? empty : [value];
}
function findAndReplace(tree, list2, options) {
const settings = options || {};
const ignored = convert(settings.ignore || []);
const pairs = toPairs(list2);
let pairIndex = -1;
while (++pairIndex < pairs.length) {
visitParents(tree, "text", visitor);
}
function visitor(node2, parents) {
let index = -1;
let grandparent;
while (++index < parents.length) {
const parent = parents[index];
const siblings = grandparent ? grandparent.children : void 0;
if (ignored(
parent,
siblings ? siblings.indexOf(parent) : void 0,
grandparent
)) {
return;
}
grandparent = parent;
}
if (grandparent) {
return handler(node2, parents);
}
}
function handler(node2, parents) {
const parent = parents[parents.length - 1];
const find = pairs[pairIndex][0];
const replace = pairs[pairIndex][1];
let start = 0;
const siblings = parent.children;
const index = siblings.indexOf(node2);
let change = false;
let nodes = [];
find.lastIndex = 0;
let match = find.exec(node2.value);
while (match) {
const position = match.index;
const matchObject = {
index: match.index,
input: match.input,
stack: [...parents, node2]
};
let value = replace(...match, matchObject);
if (typeof value === "string") {
value = value.length > 0 ? { type: "text", value } : void 0;
}
if (value === false) {
find.lastIndex = position + 1;
} else {
if (start !== position) {
nodes.push({
type: "text",
value: node2.value.slice(start, position)
});
}
if (Array.isArray(value)) {
nodes.push(...value);
} else if (value) {
nodes.push(value);
}
start = position + match[0].length;
change = true;
}
if (!find.global) {
break;
}
match = find.exec(node2.value);
}
if (change) {
if (start < node2.value.length) {
nodes.push({ type: "text", value: node2.value.slice(start) });
}
parent.children.splice(index, 1, ...nodes);
} else {
nodes = [node2];
}
return index + nodes.length;
}
}
function toPairs(tupleOrList) {
const result = [];
if (!Array.isArray(tupleOrList)) {
throw new TypeError("Expected find and replace tuple or list of tuples");
}
const list2 = !tupleOrList[0] || Array.isArray(tupleOrList[0]) ? tupleOrList : [tupleOrList];
let index = -1;
while (++index < list2.length) {
const tuple = list2[index];
result.push([toExpression(tuple[0]), toFunction(tuple[1])]);
}
return result;
}
function toExpression(find) {
return typeof find === "string" ? new RegExp(escapeStringRegexp(find), "g") : find;
}
function toFunction(replace) {
return typeof replace === "function" ? replace : function() {
return replace;
};
}
const inConstruct = "phrasing";
const notInConstruct = ["autolink", "link", "image", "label"];
function gfmAutolinkLiteralFromMarkdown() {
return {
transforms: [transformGfmAutolinkLiterals],
enter: {
literalAutolink: enterLiteralAutolink,
literalAutolinkEmail: enterLiteralAutolinkValue,
literalAutolinkHttp: enterLiteralAutolinkValue,
literalAutolinkWww: enterLiteralAutolinkValue
},
exit: {
literalAutolink: exitLiteralAutolink,
literalAutolinkEmail: exitLiteralAutolinkEmail,
literalAutolinkHttp: exitLiteralAutolinkHttp,
literalAutolinkWww: exitLiteralAutolinkWww
}
};
}
function gfmAutolinkLiteralToMarkdown() {
return {
unsafe: [
{
character: "@",
before: "[+\\-.\\w]",
after: "[\\-.\\w]",
inConstruct,
notInConstruct
},
{
character: ".",
before: "[Ww]",
after: "[\\-.\\w]",
inConstruct,
notInConstruct
},
{
character: ":",
before: "[ps]",
after: "\\/",
inConstruct,
notInConstruct
}
]
};
}
function enterLiteralAutolink(token) {
this.enter({ type: "link", title: null, url: "", children: [] }, token);
}
function enterLiteralAutolinkValue(token) {
this.config.enter.autolinkProtocol.call(this, token);
}
function exitLiteralAutolinkHttp(token) {
this.config.exit.autolinkProtocol.call(this, token);
}
function exitLiteralAutolinkWww(token) {
this.config.exit.data.call(this, token);
const node2 = this.stack[this.stack.length - 1];
ok$1(node2.type === "link");
node2.url = "http://" + this.sliceSerialize(token);
}
function exitLiteralAutolinkEmail(token) {
this.config.exit.autolinkEmail.call(this, token);
}
function exitLiteralAutolink(token) {
this.exit(token);
}
function transformGfmAutolinkLiterals(tree) {
findAndReplace(
tree,
[
[/(https?:\/\/|www(?=\.))([-.\w]+)([^ \t\r\n]*)/gi, findUrl],
[new RegExp("(?<=^|\\s|\\p{P}|\\p{S})([-.\\w+]+)@([-\\w]+(?:\\.[-\\w]+)+)", "gu"), findEmail]
],
{ ignore: ["link", "linkReference"] }
);
}
function findUrl(_, protocol, domain2, path2, match) {
let prefix = "";
if (!previous(match)) {
return false;
}
if (/^w/i.test(protocol)) {
domain2 = protocol + domain2;
protocol = "";
prefix = "http://";
}
if (!isCorrectDomain(domain2)) {
return false;
}
const parts = splitUrl(domain2 + path2);
if (!parts[0]) return false;
const result = {
type: "link",
title: null,
url: prefix + protocol + parts[0],
children: [{ type: "text", value: protocol + parts[0] }]
};
if (parts[1]) {
return [result, { type: "text", value: parts[1] }];
}
return result;
}
function findEmail(_, atext, label, match) {
if (
// Not an expected previous character.
!previous(match, true) || // Label ends in not allowed character.
/[-\d_]$/.test(label)
) {
return false;
}
return {
type: "link",
title: null,
url: "mailto:" + atext + "@" + label,
children: [{ type: "text", value: atext + "@" + label }]
};
}
function isCorrectDomain(domain2) {
const parts = domain2.split(".");
if (parts.length < 2 || parts[parts.length - 1] && (/_/.test(parts[parts.length - 1]) || !/[a-zA-Z\d]/.test(parts[parts.length - 1])) || parts[parts.length - 2] && (/_/.test(parts[parts.length - 2]) || !/[a-zA-Z\d]/.test(parts[parts.length - 2]))) {
return false;
}
return true;
}
function splitUrl(url) {
const trailExec = /[!"&'),.:;<>?\]}]+$/.exec(url);
if (!trailExec) {
return [url, void 0];
}
url = url.slice(0, trailExec.index);
let trail2 = trailExec[0];
let closingParenIndex = trail2.indexOf(")");
const openingParens = ccount(url, "(");
let closingParens = ccount(url, ")");
while (closingParenIndex !== -1 && openingParens > closingParens) {
url += trail2.slice(0, closingParenIndex + 1);
trail2 = trail2.slice(closingParenIndex + 1);
closingParenIndex = trail2.indexOf(")");
closingParens++;
}
return [url, trail2];
}
function previous(match, email) {
const code2 = match.input.charCodeAt(match.index - 1);
return (match.index === 0 || unicodeWhitespace(code2) || unicodePunctuation(code2)) && // If it’s an email, the previous character should not be a slash.
(!email || code2 !== 47);
}
const wwwPrefix = {
tokenize: tokenizeWwwPrefix,
partial: true
};
const domain = {
tokenize: tokenizeDomain,
partial: true
};
const path = {
tokenize: tokenizePath,
partial: true
};
const trail = {
tokenize: tokenizeTrail,
partial: true
};
const emailDomainDotTrail = {
tokenize: tokenizeEmailDomainDotTrail,
partial: true
};
const wwwAutolink = {
name: "wwwAutolink",
tokenize: tokenizeWwwAutolink,
previous: previousWww
};
const protocolAutolink = {
name: "protocolAutolink",
tokenize: tokenizeProtocolAutolink,
previous: previousProtocol
};
const emailAutolink = {
name: "emailAutolink",
tokenize: tokenizeEmailAutolink,
previous: previousEmail
};
const text$1 = {};
function gfmAutolinkLiteral() {
return {
text: text$1
};
}
let code$1 = 48;
while (code$1 < 123) {
text$1[code$1] = emailAutolink;
code$1++;
if (code$1 === 58) code$1 = 65;
else if (code$1 === 91) code$1 = 97;
}
text$1[43] = emailAutolink;
text$1[45] = emailAutolink;
text$1[46] = emailAutolink;
text$1[95] = emailAutolink;
text$1[72] = [emailAutolink, protocolAutolink];
text$1[104] = [emailAutolink, protocolAutolink];
text$1[87] = [emailAutolink, wwwAutolink];
text$1[119] = [emailAutolink, wwwAutolink];
function tokenizeEmailAutolink(effects, ok2, nok) {
const self = this;
let dot;
let data;
return start;
function start(code2) {
if (!gfmAtext(code2) || !previousEmail.call(self, self.previous) || previousUnbalanced(self.events)) {
return nok(code2);
}
effects.enter("literalAutolink");
effects.enter("literalAutolinkEmail");
return atext(code2);
}
function atext(code2) {
if (gfmAtext(code2)) {
effects.consume(code2);
return atext;
}
if (code2 === 64) {
effects.consume(code2);
return emailDomain;
}
return nok(code2);
}
function emailDomain(code2) {
if (code2 === 46) {
return effects.check(emailDomainDotTrail, emailDomainAfter, emailDomainDot)(code2);
}
if (code2 === 45 || code2 === 95 || asciiAlphanumeric(code2)) {
data = true;
effects.consume(code2);
return emailDomain;
}
return emailDomainAfter(code2);
}
function emailDomainDot(code2) {
effects.consume(code2);
dot = true;
return emailDomain;
}
function emailDomainAfter(code2) {
if (data && dot && asciiAlpha(self.previous)) {
effects.exit("literalAutolinkEmail");
effects.exit("literalAutolink");
return ok2(code2);
}
return nok(code2);
}
}
function tokenizeWwwAutolink(effects, ok2, nok) {
const self = this;
return wwwStart;
function wwwStart(code2) {
if (code2 !== 87 && code2 !== 119 || !previousWww.call(self, self.previous) || previousUnbalanced(self.events)) {
return nok(code2);
}
effects.enter("literalAutolink");
effects.enter("literalAutolinkWww");
return effects.check(wwwPrefix, effects.attempt(domain, effects.attempt(path, wwwAfter), nok), nok)(code2);
}
function wwwAfter(code2) {
effects.exit("literalAutolinkWww");
effects.exit("literalAutolink");
return ok2(code2);
}
}
function tokenizeProtocolAutolink(effects, ok2, nok) {
const self = this;
let buffer = "";
let seen = false;
return protocolStart;
function protocolStart(code2) {
if ((code2 === 72 || code2 === 104) && previousProtocol.call(self, self.previous) && !previousUnbalanced(self.events)) {
effects.enter("literalAutolink");
effects.enter("literalAutolinkHttp");
buffer += String.fromCodePoint(code2);
effects.consume(code2);
return protocolPrefixInside;
}
return nok(code2);
}
function protocolPrefixInside(code2) {
if (asciiAlpha(code2) && buffer.length < 5) {
buffer += String.fromCodePoint(code2);
effects.consume(code2);
return protocolPrefixInside;
}
if (code2 === 58) {
const protocol = buffer.toLowerCase();
if (protocol === "http" || protocol === "https") {
effects.consume(code2);
return protocolSlashesInside;
}
}
return nok(code2);
}
function protocolSlashesInside(code2) {
if (code2 === 47) {
effects.consume(code2);
if (seen) {
return afterProtocol;
}
seen = true;
return protocolSlashesInside;
}
return nok(code2);
}
function afterProtocol(code2) {
return code2 === null || asciiControl(code2) || markdownLineEndingOrSpace(code2) || unicodeWhitespace(code2) || unicodePunctuation(code2) ? nok(code2) : effects.attempt(domain, effects.attempt(path, protocolAfter), nok)(code2);
}
function protocolAfter(code2) {
effects.exit("literalAutolinkHttp");
effects.exit("literalAutolink");
return ok2(code2);
}
}
function tokenizeWwwPrefix(effects, ok2, nok) {
let size = 0;
return wwwPrefixInside;
function wwwPrefixInside(code2) {
if ((code2 === 87 || code2 === 119) && size < 3) {
size++;
effects.consume(code2);
return wwwPrefixInside;
}
if (code2 === 46 && size === 3) {
effects.consume(code2);
return wwwPrefixAfter;
}
return nok(code2);
}
function wwwPrefixAfter(code2) {
return code2 === null ? nok(code2) : ok2(code2);
}
}
function tokenizeDomain(effects, ok2, nok) {
let underscoreInLastSegment;
let underscoreInLastLastSegment;
let seen;
return domainInside;
function domainInside(code2) {
if (code2 === 46 || code2 === 95) {
return effects.check(trail, domainAfter, domainAtPunctuation)(code2);
}
if (code2 === null || markdownLineEndingOrSpace(code2) || unicodeWhitespace(code2) || code2 !== 45 && unicodePunctuation(code2)) {
return domainAfter(code2);
}
seen = true;
effects.consume(code2);
return domainInside;
}
function domainAtPunctuation(code2) {
if (code2 === 95) {
underscoreInLastSegment = true;
} else {
underscoreInLastLastSegment = underscoreInLastSegment;
underscoreInLastSegment = void 0;
}
effects.consume(code2);
return domainInside;
}
function domainAfter(code2) {
if (underscoreInLastLastSegment || underscoreInLastSegment || !seen) {
return nok(code2);
}
return ok2(code2);
}
}
function tokenizePath(effects, ok2) {
let sizeOpen = 0;
let sizeClose = 0;
return pathInside;
function pathInside(code2) {
if (code2 === 40) {
sizeOpen++;
effects.consume(code2);
return pathInside;
}
if (code2 === 41 && sizeClose < sizeOpen) {
return pathAtPunctuation(code2);
}
if (code2 === 33 || code2 === 34 || code2 === 38 || code2 === 39 || code2 === 41 || code2 === 42 || code2 === 44 || code2 === 46 || code2 === 58 || code2 === 59 || code2 === 60 || code2 === 63 || code2 === 93 || code2 === 95 || code2 === 126) {
return effects.check(trail, ok2, pathAtPunctuation)(code2);
}
if (code2 === null || markdownLineEndingOrSpace(code2) || unicodeWhitespace(code2)) {
return ok2(code2);
}
effects.consume(code2);
return pathInside;
}
function pathAtPunctuation(code2) {
if (code2 === 41) {
sizeClose++;
}
effects.consume(code2);
return pathInside;
}
}
function tokenizeTrail(effects, ok2, nok) {
return trail2;
function trail2(code2) {
if (code2 === 33 || code2 === 34 || code2 === 39 || code2 === 41 || code2 === 42 || code2 === 44 || code2 === 46 || code2 === 58 || code2 === 59 || code2 === 63 || code2 === 95 || code2 === 126) {
effects.consume(code2);
return trail2;
}
if (code2 === 38) {
effects.consume(code2);
return trailCharacterReferenceStart;
}
if (code2 === 93) {
effects.consume(code2);
return trailBracketAfter;
}
if (
// `<` is an end.
code2 === 60 || // So is whitespace.
code2 === null || markdownLineEndingOrSpace(code2) || unicodeWhitespace(code2)
) {
return ok2(code2);
}
return nok(code2);
}
function trailBracketAfter(code2) {
if (code2 === null || code2 === 40 || code2 === 91 || markdownLineEndingOrSpace(code2) || unicodeWhitespace(code2)) {
return ok2(code2);
}
return trail2(code2);
}
function trailCharacterReferenceStart(code2) {
return asciiAlpha(code2) ? trailCharacterReferenceInside(code2) : nok(code2);
}
function trailCharacterReferenceInside(code2) {
if (code2 === 59) {
effects.consume(code2);
return trail2;
}
if (asciiAlpha(code2)) {
effects.consume(code2);
return trailCharacterReferenceInside;
}
return nok(code2);
}
}
function tokenizeEmailDomainDotTrail(effects, ok2, nok)