UNPKG

@milkdown/core

Version:

The core module of [milkdown](https://milkdown.dev/).

697 lines (696 loc) 23.9 kB
var __typeError = (msg) => { throw TypeError(msg); }; var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg); var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); var _container, _ctx, _enableInspector, _status, _configureList, _onStatusChange, _container2, _clock, _usrPluginStore, _sysPluginStore, _ctx2, _loadInternal, _prepare, _cleanup, _cleanupInternal, _setStatus, _loadPluginInStore; import { createSlice, createTimer, Container, Clock, Ctx } from "@milkdown/ctx"; import { Schema, DOMParser, Node } from "@milkdown/prose/model"; import remarkParse from "remark-parse"; import remarkStringify from "remark-stringify"; import { unified } from "unified"; import { callCommandBeforeEditorView, ctxCallOutOfScope, docTypeError } from "@milkdown/exception"; import { ParserState, SerializerState } from "@milkdown/transformer"; import { customInputRules } from "@milkdown/prose"; import { chainCommands, deleteSelection, joinBackward, selectNodeBackward, baseKeymap } from "@milkdown/prose/commands"; import { undoInputRule } from "@milkdown/prose/inputrules"; import { keymap } from "@milkdown/prose/keymap"; import { Plugin, PluginKey, EditorState } from "@milkdown/prose/state"; import { EditorView } from "@milkdown/prose/view"; function withMeta(plugin, meta) { plugin.meta = { package: "@milkdown/core", group: "System", ...meta }; return plugin; } const remarkHandlers = { strong: (node, _, state, info) => { const marker = node.marker || state.options.strong || "*"; const exit = state.enter("strong"); const tracker = state.createTracker(info); let value = tracker.move(marker + marker); value += tracker.move( state.containerPhrasing(node, { before: value, after: marker, ...tracker.current() }) ); value += tracker.move(marker + marker); exit(); return value; }, emphasis: (node, _, state, info) => { const marker = node.marker || state.options.emphasis || "*"; const exit = state.enter("emphasis"); const tracker = state.createTracker(info); let value = tracker.move(marker); value += tracker.move( state.containerPhrasing(node, { before: value, after: marker, ...tracker.current() }) ); value += tracker.move(marker); exit(); return value; } }; const editorViewCtx = createSlice({}, "editorView"); const editorStateCtx = createSlice({}, "editorState"); const initTimerCtx = createSlice([], "initTimer"); const editorCtx = createSlice({}, "editor"); const inputRulesCtx = createSlice([], "inputRules"); const prosePluginsCtx = createSlice([], "prosePlugins"); const remarkPluginsCtx = createSlice( [], "remarkPlugins" ); const nodeViewCtx = createSlice([], "nodeView"); const markViewCtx = createSlice([], "markView"); const remarkCtx = createSlice( unified().use(remarkParse).use(remarkStringify), "remark" ); const remarkStringifyOptionsCtx = createSlice( { handlers: remarkHandlers }, "remarkStringifyOptions" ); const ConfigReady = createTimer("ConfigReady"); function config(configure) { const plugin = (ctx) => { ctx.record(ConfigReady); return async () => { await configure(ctx); ctx.done(ConfigReady); return () => { ctx.clearTimer(ConfigReady); }; }; }; withMeta(plugin, { displayName: "Config" }); return plugin; } const InitReady = createTimer("InitReady"); function init(editor) { const plugin = (ctx) => { ctx.inject(editorCtx, editor).inject(prosePluginsCtx, []).inject(remarkPluginsCtx, []).inject(inputRulesCtx, []).inject(nodeViewCtx, []).inject(markViewCtx, []).inject(remarkStringifyOptionsCtx, { handlers: remarkHandlers }).inject(remarkCtx, unified().use(remarkParse).use(remarkStringify)).inject(initTimerCtx, [ConfigReady]).record(InitReady); return async () => { await ctx.waitTimers(initTimerCtx); const options = ctx.get(remarkStringifyOptionsCtx); ctx.set( remarkCtx, unified().use(remarkParse).use(remarkStringify, options) ); ctx.done(InitReady); return () => { ctx.remove(editorCtx).remove(prosePluginsCtx).remove(remarkPluginsCtx).remove(inputRulesCtx).remove(nodeViewCtx).remove(markViewCtx).remove(remarkStringifyOptionsCtx).remove(remarkCtx).remove(initTimerCtx).clearTimer(InitReady); }; }; }; withMeta(plugin, { displayName: "Init" }); return plugin; } const SchemaReady = createTimer("SchemaReady"); const schemaTimerCtx = createSlice([], "schemaTimer"); const schemaCtx = createSlice({}, "schema"); const nodesCtx = createSlice([], "nodes"); const marksCtx = createSlice([], "marks"); function extendPriority(x) { var _a; return { ...x, parseDOM: (_a = x.parseDOM) == null ? void 0 : _a.map((rule) => ({ priority: x.priority, ...rule })) }; } const schema = (ctx) => { ctx.inject(schemaCtx, {}).inject(nodesCtx, []).inject(marksCtx, []).inject(schemaTimerCtx, [InitReady]).record(SchemaReady); return async () => { await ctx.waitTimers(schemaTimerCtx); const remark = ctx.get(remarkCtx); const remarkPlugins = ctx.get(remarkPluginsCtx); const processor = remarkPlugins.reduce( (acc, plug) => acc.use(plug.plugin, plug.options), remark ); ctx.set(remarkCtx, processor); const nodes = Object.fromEntries( ctx.get(nodesCtx).map(([key2, x]) => [key2, extendPriority(x)]) ); const marks = Object.fromEntries( ctx.get(marksCtx).map(([key2, x]) => [key2, extendPriority(x)]) ); const schema2 = new Schema({ nodes, marks }); ctx.set(schemaCtx, schema2); ctx.done(SchemaReady); return () => { ctx.remove(schemaCtx).remove(nodesCtx).remove(marksCtx).remove(schemaTimerCtx).clearTimer(SchemaReady); }; }; }; withMeta(schema, { displayName: "Schema" }); class CommandManager { constructor() { __privateAdd(this, _container); __privateAdd(this, _ctx); __privateSet(this, _container, new Container()); __privateSet(this, _ctx, null); this.setCtx = (ctx) => { __privateSet(this, _ctx, ctx); }; } get ctx() { return __privateGet(this, _ctx); } /// Register a command into the manager. create(meta, value) { const slice = meta.create(__privateGet(this, _container).sliceMap); slice.set(value); return slice; } get(slice) { return __privateGet(this, _container).get(slice).get(); } remove(slice) { return __privateGet(this, _container).remove(slice); } call(slice, payload) { if (__privateGet(this, _ctx) == null) throw callCommandBeforeEditorView(); const cmd = this.get(slice); const command = cmd(payload); const view = __privateGet(this, _ctx).get(editorViewCtx); return command(view.state, view.dispatch, view); } } _container = new WeakMap(); _ctx = new WeakMap(); function createCmdKey(key2 = "cmdKey") { return createSlice(() => () => false, key2); } const commandsCtx = createSlice(new CommandManager(), "commands"); const commandsTimerCtx = createSlice([SchemaReady], "commandsTimer"); const CommandsReady = createTimer("CommandsReady"); const commands = (ctx) => { const cmd = new CommandManager(); cmd.setCtx(ctx); ctx.inject(commandsCtx, cmd).inject(commandsTimerCtx, [SchemaReady]).record(CommandsReady); return async () => { await ctx.waitTimers(commandsTimerCtx); ctx.done(CommandsReady); return () => { ctx.remove(commandsCtx).remove(commandsTimerCtx).clearTimer(CommandsReady); }; }; }; withMeta(commands, { displayName: "Commands" }); const ParserReady = createTimer("ParserReady"); const outOfScope$1 = () => { throw ctxCallOutOfScope(); }; const parserCtx = createSlice(outOfScope$1, "parser"); const parserTimerCtx = createSlice([], "parserTimer"); const parser = (ctx) => { ctx.inject(parserCtx, outOfScope$1).inject(parserTimerCtx, [SchemaReady]).record(ParserReady); return async () => { await ctx.waitTimers(parserTimerCtx); const remark = ctx.get(remarkCtx); const schema2 = ctx.get(schemaCtx); ctx.set(parserCtx, ParserState.create(schema2, remark)); ctx.done(ParserReady); return () => { ctx.remove(parserCtx).remove(parserTimerCtx).clearTimer(ParserReady); }; }; }; withMeta(parser, { displayName: "Parser" }); const SerializerReady = createTimer("SerializerReady"); const serializerTimerCtx = createSlice( [], "serializerTimer" ); const outOfScope = () => { throw ctxCallOutOfScope(); }; const serializerCtx = createSlice( outOfScope, "serializer" ); const serializer = (ctx) => { ctx.inject(serializerCtx, outOfScope).inject(serializerTimerCtx, [SchemaReady]).record(SerializerReady); return async () => { await ctx.waitTimers(serializerTimerCtx); const remark = ctx.get(remarkCtx); const schema2 = ctx.get(schemaCtx); ctx.set(serializerCtx, SerializerState.create(schema2, remark)); ctx.done(SerializerReady); return () => { ctx.remove(serializerCtx).remove(serializerTimerCtx).clearTimer(SerializerReady); }; }; }; withMeta(serializer, { displayName: "Serializer" }); const defaultValueCtx = createSlice("", "defaultValue"); const editorStateOptionsCtx = createSlice( (x) => x, "stateOptions" ); const editorStateTimerCtx = createSlice( [], "editorStateTimer" ); const EditorStateReady = createTimer("EditorStateReady"); function getDoc(defaultValue, parser2, schema2) { if (typeof defaultValue === "string") return parser2(defaultValue); if (defaultValue.type === "html") return DOMParser.fromSchema(schema2).parse(defaultValue.dom); if (defaultValue.type === "json") return Node.fromJSON(schema2, defaultValue.value); throw docTypeError(defaultValue); } const key$1 = new PluginKey("MILKDOWN_STATE_TRACKER"); function overrideBaseKeymap(keymap2) { const handleBackspace = chainCommands( undoInputRule, deleteSelection, joinBackward, selectNodeBackward ); keymap2.Backspace = handleBackspace; return keymap2; } const editorState = (ctx) => { ctx.inject(defaultValueCtx, "").inject(editorStateCtx, {}).inject(editorStateOptionsCtx, (x) => x).inject(editorStateTimerCtx, [ParserReady, SerializerReady, CommandsReady]).record(EditorStateReady); return async () => { await ctx.waitTimers(editorStateTimerCtx); const schema2 = ctx.get(schemaCtx); const parser2 = ctx.get(parserCtx); const rules = ctx.get(inputRulesCtx); const optionsOverride = ctx.get(editorStateOptionsCtx); const prosePlugins = ctx.get(prosePluginsCtx); const defaultValue = ctx.get(defaultValueCtx); const doc = getDoc(defaultValue, parser2, schema2); const plugins = [ ...prosePlugins, new Plugin({ key: key$1, state: { init: () => { }, apply: (_tr, _value, _oldState, newState) => { ctx.set(editorStateCtx, newState); } } }), customInputRules({ rules }), keymap(overrideBaseKeymap(baseKeymap)) ]; ctx.set(prosePluginsCtx, plugins); const options = optionsOverride({ schema: schema2, doc, plugins }); const state = EditorState.create(options); ctx.set(editorStateCtx, state); ctx.done(EditorStateReady); return () => { ctx.remove(defaultValueCtx).remove(editorStateCtx).remove(editorStateOptionsCtx).remove(editorStateTimerCtx).clearTimer(EditorStateReady); }; }; }; withMeta(editorState, { displayName: "EditorState" }); const EditorViewReady = createTimer("EditorViewReady"); const editorViewTimerCtx = createSlice( [], "editorViewTimer" ); const editorViewOptionsCtx = createSlice( {}, "editorViewOptions" ); const rootCtx = createSlice(null, "root"); const rootDOMCtx = createSlice(null, "rootDOM"); const rootAttrsCtx = createSlice( {}, "rootAttrs" ); function createViewContainer(root, ctx) { const container = document.createElement("div"); container.className = "milkdown"; root.appendChild(container); ctx.set(rootDOMCtx, container); const attrs = ctx.get(rootAttrsCtx); Object.entries(attrs).forEach( ([key2, value]) => container.setAttribute(key2, value) ); return container; } function prepareViewDom(dom) { dom.classList.add("editor"); dom.setAttribute("role", "textbox"); } const key = new PluginKey("MILKDOWN_VIEW_CLEAR"); const editorView = (ctx) => { ctx.inject(rootCtx, document.body).inject(editorViewCtx, {}).inject(editorViewOptionsCtx, {}).inject(rootDOMCtx, null).inject(rootAttrsCtx, {}).inject(editorViewTimerCtx, [EditorStateReady]).record(EditorViewReady); return async () => { await ctx.wait(InitReady); const root = ctx.get(rootCtx) || document.body; const el = typeof root === "string" ? document.querySelector(root) : root; ctx.update(prosePluginsCtx, (xs) => [ new Plugin({ key, view: (editorView2) => { const container = el ? createViewContainer(el, ctx) : void 0; const handleDOM = () => { if (container && el) { const editor = editorView2.dom; el.replaceChild(container, editor); container.appendChild(editor); } }; handleDOM(); return { destroy: () => { if (container == null ? void 0 : container.parentNode) container == null ? void 0 : container.parentNode.replaceChild(editorView2.dom, container); container == null ? void 0 : container.remove(); } }; } }), ...xs ]); await ctx.waitTimers(editorViewTimerCtx); const state = ctx.get(editorStateCtx); const options = ctx.get(editorViewOptionsCtx); const nodeViews = Object.fromEntries(ctx.get(nodeViewCtx)); const markViews = Object.fromEntries(ctx.get(markViewCtx)); const view = new EditorView(el, { state, nodeViews, markViews, ...options }); prepareViewDom(view.dom); ctx.set(editorViewCtx, view); ctx.done(EditorViewReady); return () => { view == null ? void 0 : view.destroy(); ctx.remove(rootCtx).remove(editorViewCtx).remove(editorViewOptionsCtx).remove(rootDOMCtx).remove(rootAttrsCtx).remove(editorViewTimerCtx).clearTimer(EditorViewReady); }; }; }; withMeta(editorView, { displayName: "EditorView" }); var EditorStatus = /* @__PURE__ */ ((EditorStatus2) => { EditorStatus2["Idle"] = "Idle"; EditorStatus2["OnCreate"] = "OnCreate"; EditorStatus2["Created"] = "Created"; EditorStatus2["OnDestroy"] = "OnDestroy"; EditorStatus2["Destroyed"] = "Destroyed"; return EditorStatus2; })(EditorStatus || {}); const _Editor = class _Editor { constructor() { __privateAdd(this, _enableInspector); __privateAdd(this, _status); __privateAdd(this, _configureList); __privateAdd(this, _onStatusChange); __privateAdd(this, _container2); __privateAdd(this, _clock); __privateAdd(this, _usrPluginStore); __privateAdd(this, _sysPluginStore); __privateAdd(this, _ctx2); __privateAdd(this, _loadInternal); __privateAdd(this, _prepare); __privateAdd(this, _cleanup); __privateAdd(this, _cleanupInternal); __privateAdd(this, _setStatus); __privateAdd(this, _loadPluginInStore); __privateSet(this, _enableInspector, false); __privateSet(this, _status, "Idle"); __privateSet(this, _configureList, []); __privateSet(this, _onStatusChange, () => void 0); __privateSet(this, _container2, new Container()); __privateSet(this, _clock, new Clock()); __privateSet(this, _usrPluginStore, /* @__PURE__ */ new Map()); __privateSet(this, _sysPluginStore, /* @__PURE__ */ new Map()); __privateSet(this, _ctx2, new Ctx(__privateGet(this, _container2), __privateGet(this, _clock))); __privateSet(this, _loadInternal, () => { const configPlugin = config(async (ctx) => { await Promise.all(__privateGet(this, _configureList).map((fn) => fn(ctx))); }); const internalPlugins = [ schema, parser, serializer, commands, editorState, editorView, init(this), configPlugin ]; __privateGet(this, _prepare).call(this, internalPlugins, __privateGet(this, _sysPluginStore)); }); __privateSet(this, _prepare, (plugins, store) => { plugins.forEach((plugin) => { const ctx = __privateGet(this, _ctx2).produce( __privateGet(this, _enableInspector) ? plugin.meta : void 0 ); const handler = plugin(ctx); store.set(plugin, { ctx, handler, cleanup: void 0 }); }); }); __privateSet(this, _cleanup, (plugins, remove = false) => { return Promise.all( [plugins].flat().map((plugin) => { const loader = __privateGet(this, _usrPluginStore).get(plugin); const cleanup = loader == null ? void 0 : loader.cleanup; if (remove) __privateGet(this, _usrPluginStore).delete(plugin); else __privateGet(this, _usrPluginStore).set(plugin, { ctx: void 0, handler: void 0, cleanup: void 0 }); if (typeof cleanup === "function") return cleanup(); return cleanup; }) ); }); __privateSet(this, _cleanupInternal, async () => { await Promise.all( [...__privateGet(this, _sysPluginStore).entries()].map(([_, { cleanup }]) => { if (typeof cleanup === "function") return cleanup(); return cleanup; }) ); __privateGet(this, _sysPluginStore).clear(); }); __privateSet(this, _setStatus, (status) => { __privateSet(this, _status, status); __privateGet(this, _onStatusChange).call(this, status); }); __privateSet(this, _loadPluginInStore, (store) => { return [...store.entries()].map(async ([key2, loader]) => { const { ctx, handler } = loader; if (!handler) return; const cleanup = await handler(); store.set(key2, { ctx, handler, cleanup }); }); }); this.enableInspector = (enable = true) => { __privateSet(this, _enableInspector, enable); return this; }; this.onStatusChange = (onChange) => { __privateSet(this, _onStatusChange, onChange); return this; }; this.config = (configure) => { __privateGet(this, _configureList).push(configure); return this; }; this.removeConfig = (configure) => { __privateSet(this, _configureList, __privateGet(this, _configureList).filter((x) => x !== configure)); return this; }; this.use = (plugins) => { const _plugins = [plugins].flat(); _plugins.flat().forEach((plugin) => { __privateGet(this, _usrPluginStore).set(plugin, { ctx: void 0, handler: void 0, cleanup: void 0 }); }); if (__privateGet(this, _status) === "Created") __privateGet(this, _prepare).call(this, _plugins, __privateGet(this, _usrPluginStore)); return this; }; this.remove = async (plugins) => { if (__privateGet(this, _status) === "OnCreate") { console.warn( "[Milkdown]: You are trying to remove plugins when the editor is creating, this is not recommended, please check your code." ); return new Promise((resolve) => { setTimeout(() => { resolve(this.remove(plugins)); }, 50); }); } await __privateGet(this, _cleanup).call(this, [plugins].flat(), true); return this; }; this.create = async () => { if (__privateGet(this, _status) === "OnCreate") return this; if (__privateGet(this, _status) === "Created") await this.destroy(); __privateGet(this, _setStatus).call(this, "OnCreate"); __privateGet(this, _loadInternal).call(this); __privateGet(this, _prepare).call(this, [...__privateGet(this, _usrPluginStore).keys()], __privateGet(this, _usrPluginStore)); await Promise.all( [ __privateGet(this, _loadPluginInStore).call(this, __privateGet(this, _sysPluginStore)), __privateGet(this, _loadPluginInStore).call(this, __privateGet(this, _usrPluginStore)) ].flat() ); __privateGet(this, _setStatus).call(this, "Created"); return this; }; this.destroy = async (clearPlugins = false) => { if (__privateGet(this, _status) === "Destroyed" || __privateGet(this, _status) === "OnDestroy") return this; if (__privateGet(this, _status) === "OnCreate") { return new Promise((resolve) => { setTimeout(() => { resolve(this.destroy(clearPlugins)); }, 50); }); } if (clearPlugins) __privateSet(this, _configureList, []); __privateGet(this, _setStatus).call(this, "OnDestroy"); await __privateGet(this, _cleanup).call(this, [...__privateGet(this, _usrPluginStore).keys()], clearPlugins); await __privateGet(this, _cleanupInternal).call(this); __privateGet(this, _setStatus).call(this, "Destroyed"); return this; }; this.action = (action) => action(__privateGet(this, _ctx2)); this.inspect = () => { if (!__privateGet(this, _enableInspector)) { console.warn( "[Milkdown]: You are trying to collect inspection when inspector is disabled, please enable inspector by `editor.enableInspector()` first." ); return []; } return [...__privateGet(this, _sysPluginStore).values(), ...__privateGet(this, _usrPluginStore).values()].map(({ ctx }) => { var _a; return (_a = ctx == null ? void 0 : ctx.inspector) == null ? void 0 : _a.read(); }).filter((x) => Boolean(x)); }; } /// Create a new editor instance. static make() { return new _Editor(); } /// Get the ctx of the editor. get ctx() { return __privateGet(this, _ctx2); } /// Get the status of the editor. get status() { return __privateGet(this, _status); } }; _enableInspector = new WeakMap(); _status = new WeakMap(); _configureList = new WeakMap(); _onStatusChange = new WeakMap(); _container2 = new WeakMap(); _clock = new WeakMap(); _usrPluginStore = new WeakMap(); _sysPluginStore = new WeakMap(); _ctx2 = new WeakMap(); _loadInternal = new WeakMap(); _prepare = new WeakMap(); _cleanup = new WeakMap(); _cleanupInternal = new WeakMap(); _setStatus = new WeakMap(); _loadPluginInStore = new WeakMap(); let Editor = _Editor; export { CommandManager, CommandsReady, ConfigReady, Editor, EditorStateReady, EditorStatus, EditorViewReady, InitReady, ParserReady, SchemaReady, SerializerReady, commands, commandsCtx, commandsTimerCtx, config, createCmdKey, defaultValueCtx, editorCtx, editorState, editorStateCtx, editorStateOptionsCtx, editorStateTimerCtx, editorView, editorViewCtx, editorViewOptionsCtx, editorViewTimerCtx, getDoc, init, initTimerCtx, inputRulesCtx, markViewCtx, marksCtx, nodeViewCtx, nodesCtx, parser, parserCtx, parserTimerCtx, prosePluginsCtx, remarkCtx, remarkPluginsCtx, remarkStringifyOptionsCtx, rootAttrsCtx, rootCtx, rootDOMCtx, schema, schemaCtx, schemaTimerCtx, serializer, serializerCtx, serializerTimerCtx }; //# sourceMappingURL=index.js.map