Merge pull request #32739 from ifiokjr/prosemirror-test-builder

add type definition for prosemirror-test-builder
This commit is contained in:
Armando Aguirre
2019-02-05 15:16:13 -08:00
committed by GitHub
4 changed files with 268 additions and 0 deletions

View File

@@ -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. <https://github.com/ifiokjr>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.8
import { Node as ProsemirrorNode, Schema } from 'prosemirror-model';
export interface NodeTypeAttributes extends Record<string, any> {
nodeType: string;
}
export interface MarkTypeAttributes extends Record<string, any> {
markType: string;
}
export interface TaggedFlatObject<S extends Schema = any> {
tag: Record<string, number>;
flat: Array<TaggedProsemirrorNode<S> | TaggedFlatObject<S>>;
}
export interface TaggedProsemirrorNode<S extends Schema = any>
extends TaggedFlatObject<S>,
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<TestNodesUnion, TestMarksUnion>;
type Args = Array<string | TaggedProsemirrorNode | TaggedFlatObject>;
type NodeBuilderMethod<S extends Schema = any> = (
...args: Args
) => TaggedProsemirrorNode<S>;
type MarkBuilderMethod<S extends Schema = any> = (
...args: Args
) => TaggedFlatObject<S>;
export interface EqMethod {
eq(param: EqMethod): boolean;
}
export type Builder = <
Obj extends Record<
string,
NodeTypeAttributes | MarkTypeAttributes
> = Record<string, NodeTypeAttributes | MarkTypeAttributes>,
N extends string = string,
M extends string = string
>(
testSchema: Schema<N, M>,
names: Obj,
) => Record<N, NodeBuilderMethod<Schema<N, M>>> &
Record<M, MarkBuilderMethod<Schema<N, M>>> &
{
[P in keyof Obj]: Obj[P] extends NodeTypeAttributes
? NodeBuilderMethod<Schema<N, M>>
: MarkBuilderMethod<Schema<N, M>>
};
export interface ProsemirrorTestBuilder {
schema: TestSchema;
builders: Builder;
eq(a: EqMethod, b: EqMethod): boolean;
p: NodeBuilderMethod<TestSchema>;
pre: NodeBuilderMethod<TestSchema>;
h1: NodeBuilderMethod<TestSchema>;
h2: NodeBuilderMethod<TestSchema>;
h3: NodeBuilderMethod<TestSchema>;
li: NodeBuilderMethod<TestSchema>;
ul: NodeBuilderMethod<TestSchema>;
ol: NodeBuilderMethod<TestSchema>;
br: NodeBuilderMethod<TestSchema>;
img: NodeBuilderMethod<TestSchema>;
hr: NodeBuilderMethod<TestSchema>;
a: MarkBuilderMethod<TestSchema>;
// From schema list
ordered_list: NodeBuilderMethod<TestSchema>;
bullet_list: NodeBuilderMethod<TestSchema>;
list_item: NodeBuilderMethod<TestSchema>;
doc: NodeBuilderMethod<TestSchema>;
// From schema basic
paragraph: NodeBuilderMethod<TestSchema>;
blockquote: NodeBuilderMethod<TestSchema>;
horizontal_rule: NodeBuilderMethod<TestSchema>;
heading: NodeBuilderMethod<TestSchema>;
code_block: NodeBuilderMethod<TestSchema>;
text: NodeBuilderMethod<TestSchema>;
image: NodeBuilderMethod<TestSchema>;
hard_break: NodeBuilderMethod<TestSchema>;
link: MarkBuilderMethod<TestSchema>;
em: MarkBuilderMethod<TestSchema>;
strong: MarkBuilderMethod<TestSchema>;
code: MarkBuilderMethod<TestSchema>;
}
declare const prosemirrorTestBuilder: ProsemirrorTestBuilder;
export default prosemirrorTestBuilder;

View File

@@ -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('<a>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<Schema<"doc" | "paragraph" | "blockquote", "em">>
const TEST2 = a; // $ExpectType MarkBuilderMethod<Schema<"doc" | "paragraph" | "blockquote", "em">>
h1(''); // $ExpectType TaggedProsemirrorNode<Schema<"doc" | "paragraph" | "blockquote", "em">>
a(''); // $ExpectType TaggedFlatObject<Schema<"doc" | "paragraph" | "blockquote", "em">>

View File

@@ -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"]
}

View File

@@ -0,0 +1 @@
{ "extends": "dtslint/dt.json" }