Skip to content

Commit 048ea1c

Browse files
authored
Merge pull request #24 from yandex-cloud/shared-builder
* feat(core)!: use one builder for all plug-in extensions * feat(core): move plugins sorting to the extension builder
2 parents dc66438 + fcc4d35 commit 048ea1c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+196
-375
lines changed

demo/Playground.tsx

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ import {RadioButton, TextInput} from '@gravity-ui/uikit';
55
import {
66
BasePreset,
77
BehaviorPreset,
8-
createExtension,
98
MarkdownBlocksPreset,
109
MarkdownMarksPreset,
1110
MarkupString,
1211
YfmEditorComponent,
1312
useYfmEditor,
1413
YfmPreset,
14+
Extension,
1515
} from '../src';
1616
import {PlaygroundHtmlPreview} from './HtmlPreview';
1717
import {logger} from '../src/index';
@@ -43,33 +43,31 @@ export const Playground = React.memo<PlaygroundProps>((props) => {
4343
const [previewType, setPreviewType] = React.useState<string>(PreviewType.Markup);
4444
const [yfmRaw, setYfmRaw] = React.useState<MarkupString>(initial || '');
4545

46-
const extensions = React.useMemo(() => {
47-
return [
48-
createExtension((builder) =>
49-
builder
50-
.use(BasePreset, {})
51-
.use(BehaviorPreset, {
52-
history: {
53-
undoKey: keys.undo,
54-
redoKey: keys.redo,
55-
},
56-
})
57-
.use(MarkdownBlocksPreset, {
58-
image: false,
59-
heading: false,
60-
breaks: {preferredBreak: breaks ? 'soft' : 'hard'},
61-
})
62-
.use(MarkdownMarksPreset, {
63-
bold: {boldKey: keys.bold},
64-
italic: {italicKey: keys.italic},
65-
strike: {strikeKey: keys.strike},
66-
underline: {underlineKey: keys.underline},
67-
code: {codeKey: keys.code},
68-
})
69-
.use(YfmPreset, {}),
70-
)(),
71-
];
72-
}, [breaks]);
46+
const extensions = React.useMemo<Extension>(
47+
() => (builder) =>
48+
builder
49+
.use(BasePreset, {})
50+
.use(BehaviorPreset, {
51+
history: {
52+
undoKey: keys.undo,
53+
redoKey: keys.redo,
54+
},
55+
})
56+
.use(MarkdownBlocksPreset, {
57+
image: false,
58+
heading: false,
59+
breaks: {preferredBreak: breaks ? 'soft' : 'hard'},
60+
})
61+
.use(MarkdownMarksPreset, {
62+
bold: {boldKey: keys.bold},
63+
italic: {italicKey: keys.italic},
64+
strike: {strikeKey: keys.strike},
65+
underline: {underlineKey: keys.underline},
66+
code: {codeKey: keys.code},
67+
})
68+
.use(YfmPreset, {}),
69+
[breaks],
70+
);
7371

7472
const editor = useYfmEditor({
7573
linkify,

src/core/Editor.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {EditorView} from 'prosemirror-view';
22
import {EditorState} from 'prosemirror-state';
33

44
import type {CommonEditor, ContentHandler, MarkupString} from '../common';
5-
import type {ExtensionSpec} from './types/extension';
5+
import type {Extension} from './types/extension';
66
import {bindActions} from './utils/actions';
77
import type {Serializer} from './types/serializer';
88
import {ExtensionsManager} from './ExtensionsManager';
@@ -17,7 +17,7 @@ export type YfmEditorOptions = {
1717
domElem?: Element;
1818
/** yfm markup */
1919
initialContent?: string;
20-
extensions?: ExtensionSpec[];
20+
extensions?: Extension;
2121
allowHTML?: boolean;
2222
linkify?: boolean;
2323
/** markdown-it-attrs options */
@@ -48,7 +48,7 @@ export class YfmEditor implements CommonEditor, ActionStorage {
4848
constructor({
4949
domElem,
5050
initialContent = '',
51-
extensions = [],
51+
extensions = () => {},
5252
attrs: attrsOpts,
5353
allowHTML,
5454
linkify,

src/core/ExtensionBuilder.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,25 @@ describe('ExtensionBuilder', () => {
7474
expect(plugins.length).toBe(2);
7575
});
7676

77+
it('should sort plugins by priority', () => {
78+
const plugin0 = new Plugin({});
79+
const plugin1 = new Plugin({});
80+
const plugin2 = new Plugin({});
81+
const plugin3 = new Plugin({});
82+
const plugins = new ExtensionBuilder()
83+
.addPlugin(() => plugin3, ExtensionBuilder.PluginPriority.VeryLow)
84+
.addPlugin(() => plugin1)
85+
.addPlugin(() => plugin0, ExtensionBuilder.PluginPriority.VeryHigh)
86+
.addPlugin(() => plugin2)
87+
.build()
88+
.plugins({} as ExtensionDeps);
89+
90+
expect(plugins.indexOf(plugin0)).toBe(0);
91+
expect(plugins.indexOf(plugin1)).toBe(1);
92+
expect(plugins.indexOf(plugin2)).toBe(2);
93+
expect(plugins.indexOf(plugin3)).toBe(3);
94+
});
95+
7796
it('should add actions', () => {
7897
const actions = new ExtensionBuilder()
7998
.addAction('action1', () => ({

src/core/ExtensionBuilder.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -146,17 +146,14 @@ export class ExtensionBuilder {
146146
return map;
147147
},
148148
plugins: (deps) => {
149-
// TODO: sort plugins here after WIKI-16660
150-
return plugins.reduce<{plugin: Plugin; priority: number}[]>(
151-
(acc, {cb, priority}) => {
149+
return plugins
150+
.sort((a, b) => b.priority - a.priority)
151+
.reduce<Plugin[]>((acc, {cb}) => {
152152
const res = cb(deps);
153-
if (Array.isArray(res))
154-
acc.push(...res.map((plugin) => ({plugin, priority})));
155-
else acc.push({plugin: res, priority});
153+
if (Array.isArray(res)) acc.push(...res);
154+
else acc.push(res);
156155
return acc;
157-
},
158-
[],
159-
);
156+
}, []);
160157
},
161158
actions: (deps) =>
162159
actions.reduce((obj, [name, cb]) => {

src/core/ExtensionsManager.ts

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
11
import MarkdownIt from 'markdown-it';
22
import type {Plugin} from 'prosemirror-state';
33
import {ActionsManager} from './ActionsManager';
4+
import {ExtensionBuilder} from './ExtensionBuilder';
45
import {ParserTokensRegistry} from './ParserTokensRegistry';
56
import {SchemaSpecRegistry} from './SchemaSpecRegistry';
67
import {SerializerTokensRegistry} from './SerializerTokensRegistry';
78
import type {ActionSpec} from './types/actions';
8-
import type {ExtensionDeps, ExtensionSpec, YEMarkSpec, YENodeSpec} from './types/extension';
9+
import type {
10+
Extension,
11+
ExtensionDeps,
12+
ExtensionSpec,
13+
YEMarkSpec,
14+
YENodeSpec,
15+
} from './types/extension';
916
import type {MarkViewConstructor, NodeViewConstructor} from './types/node-views';
1017

1118
const attrs = require('markdown-it-attrs');
1219

1320
type ExtensionsManagerParams = {
14-
extensions: ExtensionSpec[];
21+
extensions: Extension;
1522
options?: ExtensionsManagerOptions;
1623
};
1724

@@ -25,7 +32,7 @@ type ExtensionsManagerOptions = {
2532
};
2633

2734
export class ExtensionsManager {
28-
static process(extensions: ExtensionSpec[], options: ExtensionsManagerOptions) {
35+
static process(extensions: Extension, options: ExtensionsManagerOptions) {
2936
return new this({extensions, options}).build();
3037
}
3138

@@ -38,8 +45,10 @@ export class ExtensionsManager {
3845

3946
#md: MarkdownIt;
4047
#mdWithoutAttrs: MarkdownIt;
41-
#extensions: ExtensionSpec[];
48+
#extensions: Extension;
49+
#builder: ExtensionBuilder;
4250

51+
#spec!: ExtensionSpec;
4352
#deps!: ExtensionDeps;
4453
#plugins: Plugin[] = [];
4554
#actions: Record<string, ActionSpec> = {};
@@ -51,6 +60,9 @@ export class ExtensionsManager {
5160

5261
this.#md = new MarkdownIt(options.mdOpts ?? {}).use(attrs, options.attrsOpts ?? {});
5362
this.#mdWithoutAttrs = new MarkdownIt(options.mdOpts ?? {});
63+
64+
// TODO: add prefilled context
65+
this.#builder = new ExtensionBuilder();
5466
}
5567

5668
build() {
@@ -75,12 +87,11 @@ export class ExtensionsManager {
7587
}
7688

7789
private processExtensions() {
78-
for (const ext of this.#extensions) {
79-
this.#md = ext.configureMd(this.#md);
80-
this.#mdWithoutAttrs = ext.configureMd(this.#mdWithoutAttrs);
81-
ext.nodes().forEach(this.processNode);
82-
ext.marks().forEach(this.processMark);
83-
}
90+
this.#spec = this.#builder.use(this.#extensions).build();
91+
this.#md = this.#spec.configureMd(this.#md);
92+
this.#mdWithoutAttrs = this.#spec.configureMd(this.#mdWithoutAttrs);
93+
this.#spec.nodes().forEach(this.processNode);
94+
this.#spec.marks().forEach(this.processMark);
8495
}
8596

8697
private processNode = (name: string, {spec, fromYfm, toYfm, view}: YENodeSpec) => {
@@ -113,14 +124,8 @@ export class ExtensionsManager {
113124
}
114125

115126
private createDerived() {
116-
const plugins: {plugin: Plugin; priority: number}[] = [];
117-
for (const ext of this.#extensions) {
118-
plugins.push(...ext.plugins(this.#deps));
119-
Object.assign(this.#actions, ext.actions(this.#deps));
120-
}
121-
122-
// TODO: move sorting to ExtensionBuilder after WIKI-16660
123-
this.#plugins = plugins.sort((a, b) => b.priority - a.priority).map((item) => item.plugin);
127+
this.#plugins = this.#spec.plugins(this.#deps);
128+
Object.assign(this.#actions, this.#spec.actions(this.#deps));
124129

125130
for (const [name, view] of this.#nodeViewCreators) {
126131
this.#nodeViews[name] = view(this.#deps);

src/core/createExtension.ts

Lines changed: 0 additions & 11 deletions
This file was deleted.

src/core/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
export * from './Editor';
22
export * from './ExtensionBuilder';
33
export * from './ExtensionsManager';
4-
export * from './createExtension';
54
export {bindActions} from './utils/actions';
65
export {trackTransactionMetrics} from './utils/metrics';
76
export type {Keymap} from './types/keymap';
@@ -10,7 +9,6 @@ export type {
109
Extension,
1110
ExtensionAuto,
1211
ExtensionWithOptions,
13-
ExtensionSpec,
1412
ExtensionDeps,
1513
YENodeSpec,
1614
YEMarkSpec,

src/core/types/extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export type ExtensionSpec = {
1717
configureMd(md: MarkdownIt): MarkdownIt;
1818
nodes(): OrderedMap<YENodeSpec>;
1919
marks(): OrderedMap<YEMarkSpec>;
20-
plugins(deps: ExtensionDeps): {plugin: Plugin; priority: number}[];
20+
plugins(deps: ExtensionDeps): Plugin[];
2121
actions(deps: ExtensionDeps): Record<string, ActionSpec>;
2222
};
2323

src/extensions/base/BaseSchema/BaseSchema.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import {builders} from 'prosemirror-test-builder';
22
import {createMarkupChecker} from '../../../../tests/sameMarkup';
33
import {ExtensionsManager} from '../../../core';
4-
import {BaseNode, BaseSchemaE} from './index';
4+
import {BaseNode, BaseSchema} from './index';
55

66
const {schema, parser, serializer} = new ExtensionsManager({
7-
extensions: [BaseSchemaE()],
7+
extensions: (builder) => builder.use(BaseSchema, {}),
88
}).buildDeps();
99

1010
const {doc, p} = builders(schema, {

src/extensions/base/BaseSchema/index.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type {NodeSpec} from 'prosemirror-model';
22
import type {Command} from 'prosemirror-state';
33
import {setBlockType} from 'prosemirror-commands';
44
import {hasParentNodeOfType} from 'prosemirror-utils';
5-
import {Action, createExtension, ExtensionAuto} from '../../../core';
5+
import type {Action, ExtensionAuto} from '../../../core';
66
import {nodeTypeFactory} from '../../../utils/schema';
77

88
export enum BaseNode {
@@ -82,13 +82,6 @@ export const BaseSchema: ExtensionAuto<BaseSchemaOptions> = (builder, opts) => {
8282
});
8383
};
8484

85-
/**
86-
* @deprecated
87-
* For tests only.
88-
* Remove after WIKI-16660
89-
*/
90-
export const BaseSchemaE = createExtension<BaseSchemaOptions>((b, o) => b.use(BaseSchema, o ?? {}));
91-
9285
declare global {
9386
namespace YfmEditor {
9487
interface Actions {

0 commit comments

Comments
 (0)