Skip to content

Commit

Permalink
Create op messages plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
12joan committed Nov 22, 2023
1 parent 0389228 commit 7498955
Show file tree
Hide file tree
Showing 13 changed files with 467 additions and 169 deletions.
3 changes: 3 additions & 0 deletions packages/op-messages/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
__tests__
__test-utils__
__mocks__
Empty file.
12 changes: 12 additions & 0 deletions packages/op-messages/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Plate operation messages plugin

TODO

## Documentation

Check out
[Operation Messages](https://platejs.org/docs/op-messages).

## License

[MIT](../../LICENSE)
60 changes: 60 additions & 0 deletions packages/op-messages/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"name": "@udecode/plate-op-messages",
"version": "25.0.1",
"description": "TODO",
"license": "MIT",
"homepage": "https://platejs.org",
"repository": {
"type": "git",
"url": "https://github.com/udecode/plate.git",
"directory": "packages/tabbable"
},
"bugs": {
"url": "https://github.com/udecode/plate/issues"
},
"sideEffects": false,
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"files": [
"dist/**/*"
],
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"module": "./dist/index.mjs",
"require": "./dist/index.js"
}
},
"scripts": {
"build": "yarn p:build",
"build:watch": "yarn p:build:watch",
"brl": "yarn p:brl",
"clean": "yarn p:clean",
"lint": "yarn p:lint",
"lint:fix": "yarn p:lint:fix",
"test": "yarn p:test",
"test:watch": "yarn p:test:watch",
"typecheck": "yarn p:typecheck"
},
"dependencies": {
"@udecode/plate-common": "25.0.1"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
"slate": ">=0.94.0",
"slate-history": ">=0.93.0",
"slate-hyperscript": ">=0.66.0",
"slate-react": ">=0.99.0"
},
"keywords": [
"plate",
"plugin",
"slate"
],
"publishConfig": {
"access": "public"
}
}
44 changes: 44 additions & 0 deletions packages/op-messages/src/createOpMessagesPlugin.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { createPlateEditor } from '@udecode/plate-common';

import { createOpMessagesPlugin } from './createOpMessagesPlugin';
import { dispatchOpMessage } from './dispatchOpMessage';

describe('createOpMessagesPlugin', () => {
it('dispatches and receives messages and inverse messages', () => {
const onMessage = jest.fn();

const editor = createPlateEditor({
plugins: [
createOpMessagesPlugin({
options: {
onMessage,
},
}) as any,
],
});

dispatchOpMessage(editor, 'create_user', { name: 'John' });

expect(onMessage).toHaveBeenLastCalledWith({
messageType: 'create_user',
data: { name: 'John' },
inverse: false,
});

editor.undo();

expect(onMessage).toHaveBeenLastCalledWith({
messageType: 'create_user',
data: { name: 'John' },
inverse: true,
});

editor.redo();

expect(onMessage).toHaveBeenLastCalledWith({
messageType: 'create_user',
data: { name: 'John' },
inverse: false,
});
});
});
16 changes: 16 additions & 0 deletions packages/op-messages/src/createOpMessagesPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createPluginFactory } from '@udecode/plate-common';

import { OpMessagesPlugin } from './types';
import { withOpMessages } from './withOpMessages';

export const KEY_OP_MESSAGES = 'opMessages';

export const createOpMessagesPlugin = createPluginFactory<OpMessagesPlugin>({
key: KEY_OP_MESSAGES,
withOverrides: withOpMessages,
options: {
operationPath: [],
catchErrors: true,
onMessage: () => {},
},
});
34 changes: 34 additions & 0 deletions packages/op-messages/src/dispatchOpMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { getPluginOptions, PlateEditor, Value } from '@udecode/plate-common';

import { KEY_OP_MESSAGES } from './createOpMessagesPlugin';
import { OpMessage, OpMessagesPlugin } from './types';

export const dispatchOpMessage = <
V extends Value = Value,
E extends PlateEditor<V> = PlateEditor<V>,
>(
editor: E,
messageType: string,
data: any
) => {
const { operationPath } = getPluginOptions<OpMessagesPlugin, V, E>(
editor,
KEY_OP_MESSAGES
);

const message: OpMessage = {
messageType,
data,
inverse: false,
};

editor.apply({
type: 'set_node',
path: operationPath!,
newProperties: message,
properties: {
...message,
inverse: true,
},
});
};
33 changes: 33 additions & 0 deletions packages/op-messages/src/handleOpMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { getPluginOptions, PlateEditor, Value } from '@udecode/plate-common';

import { KEY_OP_MESSAGES } from './createOpMessagesPlugin';
import { OpMessage, OpMessagesPlugin } from './types';

export const handleOpMessage = <
V extends Value = Value,
E extends PlateEditor<V> = PlateEditor<V>,
>(
editor: E,
message: OpMessage
) => {
const { onMessage, catchErrors } = getPluginOptions<OpMessagesPlugin, V, E>(
editor,
KEY_OP_MESSAGES
);

if (catchErrors) {
try {
onMessage(message);
} catch (error) {
// eslint-disable-next-line no-console
console.error(
'An error occurred while handling an operation message:',
error
);
// eslint-disable-next-line no-console
console.debug('Operation message:', message);
}
} else {
onMessage(message);
}
};
9 changes: 9 additions & 0 deletions packages/op-messages/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @file Automatically generated by barrelsby.
*/

export * from './createOpMessagesPlugin';
export * from './dispatchOpMessage';
export * from './handleOpMessage';
export * from './types';
export * from './withOpMessages';
28 changes: 28 additions & 0 deletions packages/op-messages/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Path } from 'slate';

export interface OpMessage {
messageType: string;
data: any;
inverse: boolean;
}

export interface OpMessagesPlugin {
/**
* Handle operaion messages.
*/
onMessage: (message: OpMessage) => void;

/**
* This plugin passes messages using Slate operations by triggering a
* set_node operation with a path not normally associated with that operation
* type. By default, this path is [], but any other invalid path can be used
* in case of conflicts.
*/
operationPath?: Path;

/**
* Automatically catch errors thrown by the onMessage handler. This helps to
* prevent malicious messages from crashing the editor. Default: true.
*/
catchErrors?: boolean;
}
36 changes: 36 additions & 0 deletions packages/op-messages/src/withOpMessages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { PlateEditor, Value, WithPlatePlugin } from '@udecode/plate-common';
import { Path } from 'slate';

import { handleOpMessage } from './handleOpMessage';
import { OpMessage, OpMessagesPlugin } from './types';

export const withOpMessages = <
V extends Value = Value,
E extends PlateEditor<V> = PlateEditor<V>,
>(
editor: E,
{ options: { operationPath } }: WithPlatePlugin<OpMessagesPlugin, V, E>
) => {
const { apply } = editor;

editor.apply = (op) => {
if (op.type === 'set_node' && Path.equals(op.path, operationPath!)) {
handleOpMessage<V, E>(editor, op.newProperties as OpMessage);

/**
* We need to allow the history editor to save the operation, while
* ignoring any errors caused by attempting to apply set_node to an
* invalid path.
*/
try {
apply(op);
} catch (error) {}

return;
}

apply(op);
};

return editor;
};
8 changes: 8 additions & 0 deletions packages/op-messages/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "../../config/tsconfig.build.json",
"compilerOptions": {
"declarationDir": "./dist",
"outDir": "./dist"
},
"include": ["src"]
}

0 comments on commit 7498955

Please sign in to comment.