Skip to content

Commit

Permalink
feat!: transfer connection of markdown-it-attrs plugin to YfmConfigs …
Browse files Browse the repository at this point in the history
…extension (#263)

- renamed:
  * `ExtensionDeps.parser` ––> `ExtensionDeps.markupParser`
  * `ExtensionDeps.parserWithoutAttrs` ––> `ExtensionDeps.textParser`
- added ability to apply configureMd() only for one type of parser
- renamed YfmDist extension to YfmConfigs
- removed usage of markdown-it-attrs plugin from wysiwyg core module
- using  markdown-it-attrs plugin in YfmConfigs extension via `builder.configureMd()`
- you can pass markdown-it-attrs options via `extensionOptions` of `useMarkdownEditor()` hook
  ```js
  // old usage
  useMarkdownEditor({attrs: {/* options of markdown-it-attrs */}});

  // new usage
  useMarkdownEditor({
    extensionOptions: {
      yfmConfigs: {attrs: {/* options of markdown-it-attrs */}},
    },
  });
  ```
  • Loading branch information
d3m1d0v authored Jun 5, 2024
1 parent 48b9fc7 commit 2816c18
Show file tree
Hide file tree
Showing 50 changed files with 287 additions and 110 deletions.
5 changes: 1 addition & 4 deletions src/bundle/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ type ChangeEditorModeOptions = {

export type EditorOptions = Pick<
WysiwygEditorOptions,
'allowHTML' | 'linkify' | 'linkifyTlds' | 'attrs' | 'extensions'
'allowHTML' | 'linkify' | 'linkifyTlds' | 'extensions'
> & {
initialMarkup?: MarkupString;
/** @default 'wysiwyg' */
Expand Down Expand Up @@ -148,7 +148,6 @@ export class EditorImpl extends SafeEventEmitter<EventMapInt> implements EditorI
#allowHTML?: boolean;
#linkify?: boolean;
#linkifyTlds?: string | string[];
#attrs?: WysiwygEditorOptions['attrs'];
#extensions?: WysiwygEditorOptions['extensions'];
#renderStorage: ReactRenderStorage;
#fileUploadHandler?: FileUploadHandler;
Expand Down Expand Up @@ -238,7 +237,6 @@ export class EditorImpl extends SafeEventEmitter<EventMapInt> implements EditorI
allowHTML: this.#allowHTML,
linkify: this.#linkify,
linkifyTlds: this.#linkifyTlds,
attrs: this.#attrs,
onChange: () => this.emit('rerender-toolbar', null),
onDocChange: () => this.emit('change', null),
});
Expand Down Expand Up @@ -288,7 +286,6 @@ export class EditorImpl extends SafeEventEmitter<EventMapInt> implements EditorI

this.#markup = opts.initialMarkup ?? '';

this.#attrs = opts.attrs;
this.#linkify = opts.linkify;
this.#linkifyTlds = opts.linkifyTlds;
this.#allowHTML = opts.allowHTML;
Expand Down
27 changes: 14 additions & 13 deletions src/core/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@ export type WysiwygEditorOptions = {
allowHTML?: boolean;
linkify?: boolean;
linkifyTlds?: string | string[];
/** markdown-it-attrs options */
attrs?: {
leftDelimiter?: string;
rightDelimiter?: string;
};
/** Call on any state change (move cursor, change selection, etc...) */
onChange?: OnChange;
/** Call only if document change */
Expand Down Expand Up @@ -66,20 +61,26 @@ export class WysiwygEditor implements CommonEditor, ActionStorage {
domElem,
initialContent = '',
extensions = () => {},
attrs: attrsOpts,
allowHTML,
linkify,
linkifyTlds,
onChange,
onDocChange,
}: WysiwygEditorOptions) {
const {schema, parser, serializer, nodeViews, markViews, plugins, rawActions, actions} =
ExtensionsManager.process(extensions, {
// "breaks" option only affects the renderer, but not the parser
mdOpts: {html: allowHTML, linkify, breaks: true},
attrsOpts: {...attrsOpts, allowedAttributes: ['id']},
linkifyTlds,
});
const {
schema,
markupParser: parser,
serializer,
nodeViews,
markViews,
plugins,
rawActions,
actions,
} = ExtensionsManager.process(extensions, {
// "breaks" option only affects the renderer, but not the parser
mdOpts: {html: allowHTML, linkify, breaks: true},
linkifyTlds,
});

const state = EditorState.create({
schema,
Expand Down
37 changes: 33 additions & 4 deletions src/core/ExtensionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,20 @@ import type {Keymap} from './types/keymap';

type InputRulesConfig = Parameters<typeof inputRules>[0];
type ExtensionWithParams = (builder: ExtensionBuilder, ...params: any[]) => void;
type ConfigureMdParams = {
/**
* Apply this configurtion to text parser
*
* @default true
*/
text?: boolean;
/**
* Apply this configurtion to markup parser
*
* @default true
*/
markup?: boolean;
};

type ConfigureMdCallback = (md: MarkdownIt) => MarkdownIt;
type AddPmNodeCallback = () => ExtensionNodeSpec;
Expand Down Expand Up @@ -64,7 +78,7 @@ export class ExtensionBuilder {
readonly PluginPriority = ExtensionBuilder.PluginPriority;
/* eslint-enable @typescript-eslint/member-ordering */

#confMdCbs: ConfigureMdCallback[] = [];
#confMdCbs: {cb: ConfigureMdCallback; params: Required<ConfigureMdParams>}[] = [];
#nodeSpecs: Record<string, {name: string; cb: AddPmNodeCallback}> = {};
#markSpecs: Record<string, {name: string; cb: AddPmMarkCallback; priority: number}> = {};
#plugins: {cb: AddPmPluginCallback; priority: number}[] = [];
Expand All @@ -83,8 +97,14 @@ export class ExtensionBuilder {
return this;
}

configureMd(cb: ConfigureMdCallback): this {
this.#confMdCbs.push(cb);
configureMd(cb: ConfigureMdCallback, params: ConfigureMdParams = {}): this {
this.#confMdCbs.push({
cb,
params: {
text: params.text ?? true,
markup: params.markup ?? true,
},
});
return this;
}

Expand Down Expand Up @@ -137,7 +157,16 @@ export class ExtensionBuilder {
const actions = this.#actions.slice();

return {
configureMd: (md) => confMd.reduce((pMd, cb) => cb(pMd), md),
configureMd: (md, parserType) =>
confMd.reduce((pMd, {cb, params}) => {
if (parserType === 'text' && params.text) {
return cb(pMd);
}
if (parserType === 'markup' && params.markup) {
return cb(pMd);
}
return pMd;
}, md),
nodes: () => {
let map = OrderedMap.from<ExtensionNodeSpec>({});
for (const {name, cb} of Object.values(nodes)) {
Expand Down
29 changes: 10 additions & 19 deletions src/core/ExtensionsManager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import MarkdownIt from 'markdown-it';
import attrsPlugin, {AttrsOptions} from 'markdown-it-attrs'; // eslint-disable-line import/no-extraneous-dependencies
import type {Plugin} from 'prosemirror-state';

import {ActionsManager} from './ActionsManager';
Expand All @@ -24,11 +23,6 @@ type ExtensionsManagerParams = {

type ExtensionsManagerOptions = {
mdOpts?: MarkdownIt.Options;
attrsOpts?: {
leftDelimiter?: string;
rightDelimiter?: string;
allowedAttributes?: string[];
};
linkifyTlds?: string | string[];
};

Expand All @@ -44,8 +38,8 @@ export class ExtensionsManager {
#nodeViewCreators = new Map<string, (deps: ExtensionDeps) => NodeViewConstructor>();
#markViewCreators = new Map<string, (deps: ExtensionDeps) => MarkViewConstructor>();

#md: MarkdownIt;
#mdWithoutAttrs: MarkdownIt;
#mdForMarkup: MarkdownIt;
#mdForText: MarkdownIt;
#extensions: Extension;
#builder: ExtensionBuilder;

Expand All @@ -59,15 +53,12 @@ export class ExtensionsManager {
constructor({extensions, options = {}}: ExtensionsManagerParams) {
this.#extensions = extensions;

this.#md = new MarkdownIt(options.mdOpts ?? {}).use<AttrsOptions>(
attrsPlugin,
options.attrsOpts ?? {},
);
this.#mdWithoutAttrs = new MarkdownIt(options.mdOpts ?? {});
this.#mdForMarkup = new MarkdownIt(options.mdOpts ?? {});
this.#mdForText = new MarkdownIt(options.mdOpts ?? {});

if (options.linkifyTlds) {
this.#md.linkify.tlds(options.linkifyTlds, true);
this.#mdWithoutAttrs.linkify.tlds(options.linkifyTlds, true);
this.#mdForMarkup.linkify.tlds(options.linkifyTlds, true);
this.#mdForText.linkify.tlds(options.linkifyTlds, true);
}

// TODO: add prefilled context
Expand Down Expand Up @@ -97,8 +88,8 @@ export class ExtensionsManager {

private processExtensions() {
this.#spec = this.#builder.use(this.#extensions).build();
this.#md = this.#spec.configureMd(this.#md);
this.#mdWithoutAttrs = this.#spec.configureMd(this.#mdWithoutAttrs);
this.#mdForMarkup = this.#spec.configureMd(this.#mdForMarkup, 'markup');
this.#mdForText = this.#spec.configureMd(this.#mdForText, 'text');
this.#spec.nodes().forEach(this.processNode);
this.#spec.marks().forEach(this.processMark);
}
Expand Down Expand Up @@ -126,8 +117,8 @@ export class ExtensionsManager {
this.#deps = {
schema,
actions: new ActionsManager(),
parser: this.#parserRegistry.createParser(schema, this.#md),
parserWithoutAttrs: this.#parserRegistry.createParser(schema, this.#mdWithoutAttrs),
markupParser: this.#parserRegistry.createParser(schema, this.#mdForMarkup),
textParser: this.#parserRegistry.createParser(schema, this.#mdForText),
serializer: this.#serializerRegistry.createSerializer(),
};
}
Expand Down
6 changes: 3 additions & 3 deletions src/core/types/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export type ExtensionWithOptions<T> = (builder: ExtensionBuilder, options: T) =>
export type ExtensionAuto<T = void> = T extends void ? Extension : ExtensionWithOptions<T>;

export type ExtensionSpec = {
configureMd(md: MarkdownIt): MarkdownIt;
configureMd(md: MarkdownIt, parserType: 'text' | 'markup'): MarkdownIt;
nodes(): OrderedMap<ExtensionNodeSpec>;
marks(): OrderedMap<ExtensionMarkSpec>;
plugins(deps: ExtensionDeps): Plugin[];
Expand Down Expand Up @@ -44,8 +44,8 @@ export type ExtensionMarkSpec = {

export type ExtensionDeps = {
readonly schema: Schema;
readonly parser: Parser;
readonly parserWithoutAttrs: Parser;
readonly textParser: Parser;
readonly markupParser: Parser;
readonly serializer: Serializer;
readonly actions: ActionStorage;
};
6 changes: 5 additions & 1 deletion src/extensions/base/BaseSchema/BaseSchema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import {ExtensionsManager} from '../../../core';

import {BaseNode, BaseSchemaSpecs} from './BaseSchemaSpecs';

const {schema, parser, serializer} = new ExtensionsManager({
const {
schema,
markupParser: parser,
serializer,
} = new ExtensionsManager({
extensions: (builder) => builder.use(BaseSchemaSpecs, {}),
}).buildDeps();

Expand Down
6 changes: 3 additions & 3 deletions src/extensions/behavior/Clipboard/clipboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ import {isInsideCode} from './code';
import {DataTransferType, extractTextContentFromHtml, isIosSafariShare} from './utils';

export type ClipboardPluginOptions = {
yfmParser: Parser;
mdParser: Parser;
textParser: Parser;
serializer: Serializer;
pasteFileHandler?: (file: File) => void;
};

export const clipboard = ({
textParser,
yfmParser,
mdParser,
serializer,
pasteFileHandler,
}: ClipboardPluginOptions) => {
Expand Down Expand Up @@ -120,7 +120,7 @@ export const clipboard = ({
let parser: Parser;
let dataFormat: string;
if (e.clipboardData.types.includes(DataTransferType.Yfm)) {
parser = yfmParser;
parser = mdParser;
dataFormat = DataTransferType.Yfm;
} else {
parser = textParser;
Expand Down
4 changes: 2 additions & 2 deletions src/extensions/behavior/Clipboard/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ export const Clipboard: ExtensionAuto<ClipboardOptions> = (builder, opts) => {
builder.addPlugin(
(deps) =>
clipboard({
yfmParser: deps.parser,
textParser: deps.parserWithoutAttrs,
mdParser: deps.markupParser,
textParser: deps.textParser,
serializer: deps.serializer,
pasteFileHandler: opts.pasteFileHandler,
}),
Expand Down
6 changes: 5 additions & 1 deletion src/extensions/markdown/Blockquote/Blockquote.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import {BaseNode, BaseSpecsPreset} from '../../base/specs';

import {BlockquoteSpecs, blockquoteNodeName} from './BlockquoteSpecs';

const {schema, parser, serializer} = new ExtensionsManager({
const {
schema,
markupParser: parser,
serializer,
} = new ExtensionsManager({
extensions: (builder) => builder.use(BaseSpecsPreset, {}).use(BlockquoteSpecs),
}).buildDeps();

Expand Down
6 changes: 5 additions & 1 deletion src/extensions/markdown/Bold/Bold.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import {BaseNode, BaseSpecsPreset} from '../../base/specs';

import {BoldSpecs, boldMarkName} from './BoldSpecs';

const {schema, parser, serializer} = new ExtensionsManager({
const {
schema,
markupParser: parser,
serializer,
} = new ExtensionsManager({
extensions: (builder) => builder.use(BaseSpecsPreset, {}).use(BoldSpecs),
}).buildDeps();

Expand Down
6 changes: 5 additions & 1 deletion src/extensions/markdown/Breaks/Breaks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import {BoldSpecs, boldMarkName} from '../../markdown/Bold/BoldSpecs';

import {BreakNodeName, BreaksSpecs} from './BreaksSpecs';

const {schema, parser, serializer} = new ExtensionsManager({
const {
schema,
markupParser: parser,
serializer,
} = new ExtensionsManager({
extensions: (builder) => builder.use(BaseSpecsPreset, {}).use(BreaksSpecs, {}).use(BoldSpecs),
}).buildDeps();

Expand Down
6 changes: 5 additions & 1 deletion src/extensions/markdown/Code/Code.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import {ItalicSpecs, italicMarkName} from '../Italic/ItalicSpecs';

import {CodeSpecs, codeMarkName} from './CodeSpecs';

const {schema, parser, serializer} = new ExtensionsManager({
const {
schema,
markupParser: parser,
serializer,
} = new ExtensionsManager({
extensions: (builder) =>
builder.use(BaseSpecsPreset, {}).use(BoldSpecs).use(CodeSpecs).use(ItalicSpecs),
}).buildDeps();
Expand Down
6 changes: 5 additions & 1 deletion src/extensions/markdown/CodeBlock/CodeBlock.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import {BaseNode, BaseSpecsPreset} from '../../base/specs';

import {CodeBlockNodeAttr, CodeBlockSpecs, codeBlockNodeName} from './CodeBlockSpecs';

const {schema, parser, serializer} = new ExtensionsManager({
const {
schema,
markupParser: parser,
serializer,
} = new ExtensionsManager({
extensions: (builder) => builder.use(BaseSpecsPreset, {}).use(CodeBlockSpecs, {}),
}).buildDeps();

Expand Down
6 changes: 5 additions & 1 deletion src/extensions/markdown/Deflist/Deflist.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import {BaseNode, BaseSpecsPreset} from '../../base/specs';

import {DeflistNode, DeflistSpecs} from './DeflistSpecs';

const {schema, parser, serializer} = new ExtensionsManager({
const {
schema,
markupParser: parser,
serializer,
} = new ExtensionsManager({
extensions: (builder) => builder.use(BaseSpecsPreset, {}).use(DeflistSpecs, {}),
}).buildDeps();

Expand Down
6 changes: 5 additions & 1 deletion src/extensions/markdown/Heading/Heading.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import {BoldSpecs, boldMarkName} from '../Bold/BoldSpecs';
import {HeadingSpecs} from './HeadingSpecs';
import {headingLevelAttr, headingNodeName} from './const';

const {schema, parser, serializer} = new ExtensionsManager({
const {
schema,
markupParser: parser,
serializer,
} = new ExtensionsManager({
extensions: (builder) => builder.use(BaseSpecsPreset, {}).use(HeadingSpecs, {}).use(BoldSpecs),
}).buildDeps();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import {
horizontalRuleNodeName,
} from './HorizontalRuleSpecs';

const {schema, parser, serializer} = new ExtensionsManager({
const {
schema,
markupParser: parser,
serializer,
} = new ExtensionsManager({
extensions: (builder) => builder.use(BaseSpecsPreset, {}).use(HorizontalRuleSpecs),
}).buildDeps();

Expand Down
Loading

0 comments on commit 2816c18

Please sign in to comment.