import cloneDeep from 'clone-deep' import { Plugin } from 'prosemirror-state' import { Editor, CommandSpec } from './Editor' type AnyObject = { [key: string]: any } type NoInfer = [T][T extends any ? 0 : never] type MergeStrategy = 'extend' | 'overwrite' type Configs = { [key: string]: { stategy: MergeStrategy value: any }[] } export interface ExtensionProps { name: string editor: Editor options: Options } export interface ExtensionMethods { name: string options: Options commands: (params: Props) => CommandSpec inputRules: (params: Props) => any[] pasteRules: (params: Props) => any[] keys: (params: Props) => { [key: string]: Function } plugins: (params: Props) => Plugin[] } export default class Extension< Options = {}, Props = ExtensionProps, Methods extends ExtensionMethods = ExtensionMethods > { type = 'extension' config: AnyObject = {} configs: Configs = {} options: Partial = {} protected storeConfig(key: string, value: any, stategy: MergeStrategy) { const item = { stategy, value, } if (this.configs[key]) { this.configs[key].push(item) } else { this.configs[key] = [item] } } public configure(options: Partial) { this.options = { ...this.options, ...options } return this } public name(value: Methods['name']) { this.storeConfig('name', value, 'overwrite') return this } public defaults(value: Options) { this.storeConfig('defaults', value, 'overwrite') return this } public commands(value: Methods['commands']) { this.storeConfig('commands', value, 'overwrite') return this } public keys(value: Methods['keys']) { this.storeConfig('keys', value, 'overwrite') return this } public inputRules(value: Methods['inputRules']) { this.storeConfig('inputRules', value, 'overwrite') return this } public pasteRules(value: Methods['pasteRules']) { this.storeConfig('pasteRules', value, 'overwrite') return this } public plugins(value: Methods['plugins']) { this.storeConfig('plugins', value, 'overwrite') return this } public extend>(key: T, value: Methods[T]) { this.storeConfig(key, value, 'extend') return this } public create() { type ParentOptions = Options return (options?: Partial>) => { return cloneDeep(this, true).configure(options as Options) } } }