diff --git a/types/prosemirror-test-builder/index.d.ts b/types/prosemirror-test-builder/index.d.ts new file mode 100644 index 0000000000..f6c01420c4 --- /dev/null +++ b/types/prosemirror-test-builder/index.d.ts @@ -0,0 +1,126 @@ +// Type definitions for prosemirror-test-builder 1.0 +// Project: https://github.com/prosemirror/prosemirror-test-builder#readme +// Definitions by: Ifiok Jr. +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.8 + +import { Node as ProsemirrorNode, Schema } from 'prosemirror-model'; + +export interface NodeTypeAttributes extends Record { + nodeType: string; +} + +export interface MarkTypeAttributes extends Record { + markType: string; +} + +export interface TaggedFlatObject { + tag: Record; + flat: Array | TaggedFlatObject>; +} + +export interface TaggedProsemirrorNode + extends TaggedFlatObject, + ProsemirrorNode {} + +type TestNodesUnion = + | 'p' + | 'pre' + | 'h1' + | 'h2' + | 'h3' + | 'li' + | 'ul' + | 'ol' + | 'br' + | 'img' + | 'hr' + | 'ordered_list' + | 'bullet_list' + | 'list_item' + | 'doc' + | 'paragraph' + | 'blockquote' + | 'horizontal_rule' + | 'heading' + | 'code_block' + | 'text' + | 'image' + | 'hard_break'; +type TestMarksUnion = 'a' | 'link' | 'em' | 'strong' | 'code'; + +export type TestSchema = Schema; + +type Args = Array; + +type NodeBuilderMethod = ( + ...args: Args +) => TaggedProsemirrorNode; + +type MarkBuilderMethod = ( + ...args: Args +) => TaggedFlatObject; + +export interface EqMethod { + eq(param: EqMethod): boolean; +} + +export type Builder = < + Obj extends Record< + string, + NodeTypeAttributes | MarkTypeAttributes + > = Record, + N extends string = string, + M extends string = string +>( + testSchema: Schema, + names: Obj, +) => Record>> & + Record>> & + { + [P in keyof Obj]: Obj[P] extends NodeTypeAttributes + ? NodeBuilderMethod> + : MarkBuilderMethod> + }; + +export interface ProsemirrorTestBuilder { + schema: TestSchema; + builders: Builder; + eq(a: EqMethod, b: EqMethod): boolean; + p: NodeBuilderMethod; + pre: NodeBuilderMethod; + h1: NodeBuilderMethod; + h2: NodeBuilderMethod; + h3: NodeBuilderMethod; + li: NodeBuilderMethod; + ul: NodeBuilderMethod; + ol: NodeBuilderMethod; + br: NodeBuilderMethod; + img: NodeBuilderMethod; + hr: NodeBuilderMethod; + a: MarkBuilderMethod; + + // From schema list + ordered_list: NodeBuilderMethod; + bullet_list: NodeBuilderMethod; + list_item: NodeBuilderMethod; + doc: NodeBuilderMethod; + + // From schema basic + paragraph: NodeBuilderMethod; + blockquote: NodeBuilderMethod; + horizontal_rule: NodeBuilderMethod; + heading: NodeBuilderMethod; + code_block: NodeBuilderMethod; + text: NodeBuilderMethod; + image: NodeBuilderMethod; + hard_break: NodeBuilderMethod; + link: MarkBuilderMethod; + em: MarkBuilderMethod; + strong: MarkBuilderMethod; + code: MarkBuilderMethod; +} + +declare const prosemirrorTestBuilder: ProsemirrorTestBuilder; + +export default prosemirrorTestBuilder; diff --git a/types/prosemirror-test-builder/prosemirror-test-builder-tests.ts b/types/prosemirror-test-builder/prosemirror-test-builder-tests.ts new file mode 100644 index 0000000000..dd5aa0c345 --- /dev/null +++ b/types/prosemirror-test-builder/prosemirror-test-builder-tests.ts @@ -0,0 +1,123 @@ +import { Schema } from 'prosemirror-model'; +import { + EditorState, + NodeSelection, + Selection, + TextSelection, + Transaction, +} from 'prosemirror-state'; +import pm, { TaggedProsemirrorNode } from 'prosemirror-test-builder'; + +export type DispatchFunction = (tr: Transaction) => void; +export type CommandFunction = ( + state: EditorState, + dispatch?: DispatchFunction, +) => boolean; + +function selectionFor(docNode: TaggedProsemirrorNode) { + const aTag = docNode.tag.a; + if (aTag != null) { + const $aTag = docNode.resolve(aTag); + if ($aTag.parent.inlineContent) { + return new TextSelection( + $aTag, + docNode.tag.b != null + ? docNode.resolve(docNode.tag.b) + : undefined, + ); + } else { + return new NodeSelection($aTag); + } + } + return Selection.atStart(docNode); +} + +function createState(d: TaggedProsemirrorNode) { + return EditorState.create({ doc: d, selection: selectionFor(d) }); +} + +export function apply( + docNode: TaggedProsemirrorNode, + command: CommandFunction, + result?: TaggedProsemirrorNode, +): [boolean, TaggedProsemirrorNode] { + let state = createState(docNode); + command(state, tr => (state = state.apply(tr))); + + if (!pm.eq(state.doc, result || docNode)) { + return [false, state.doc as TaggedProsemirrorNode]; + } + + if (result && result.tag.a != null) { + return [ + pm.eq(state.selection, selectionFor(result)), + result || docNode, + ]; + } + return [true, state.doc as TaggedProsemirrorNode]; +} + +const { p, doc, builders } = pm; + +apply(doc(p('hi'), p('there')), () => true, doc(p('hi there'))); + +export const schema = new Schema<'doc' | 'paragraph' | 'blockquote', 'em'>({ + nodes: { + doc: { + content: 'block+', + }, + + paragraph: { + group: 'block', + parseDOM: [{ tag: 'p' }], + toDOM() { + return ['p', 0]; + }, + }, + + blockquote: { + content: 'block+', + group: 'block', + parseDOM: [{ tag: 'blockquote' }], + toDOM() { + return ['blockquote', 0]; + }, + }, + }, + + marks: { + em: { + parseDOM: [ + { tag: 'i' }, + { tag: 'em' }, + { + style: 'font-style', + getAttrs: value => value === 'italic' && null, + }, + ], + toDOM() { + return ['em']; + }, + }, + }, +}); + +const { h1, a } = builders(schema, { + p: { nodeType: 'paragraph' }, + h1: { nodeType: 'heading', level: 1 }, + h2: { nodeType: 'heading', level: 2 }, + hr: { nodeType: 'horizontal_rule' }, + li: { nodeType: 'list_item' }, + ol: { nodeType: 'ordered_list' }, + ul: { nodeType: 'bullet_list' }, + pre: { nodeType: 'code_block' }, + a: { markType: 'link', href: 'foo' }, + br: { nodeType: 'hard_break' }, + img: { nodeType: 'image', src: 'img.png', alt: 'x' }, +}); + +const TEST1 = h1; // $ExpectType NodeBuilderMethod> +const TEST2 = a; // $ExpectType MarkBuilderMethod> + +h1(''); // $ExpectType TaggedProsemirrorNode> +a(''); // $ExpectType TaggedFlatObject> diff --git a/types/prosemirror-test-builder/tsconfig.json b/types/prosemirror-test-builder/tsconfig.json new file mode 100644 index 0000000000..3869b95fde --- /dev/null +++ b/types/prosemirror-test-builder/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "module": "commonjs", + "noEmit": true, + "lib": ["es6", "dom"], + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "baseUrl": "../", + "typeRoots": ["../"], + "types": [], + "forceConsistentCasingInFileNames": true + }, + "files": ["index.d.ts", "prosemirror-test-builder-tests.ts"] +} diff --git a/types/prosemirror-test-builder/tslint.json b/types/prosemirror-test-builder/tslint.json new file mode 100644 index 0000000000..3db14f85ea --- /dev/null +++ b/types/prosemirror-test-builder/tslint.json @@ -0,0 +1 @@ +{ "extends": "dtslint/dt.json" }