Hi there,
Just faced with the same task and want to share my solution to save time of the next generation of ProseMirror lovers
Plugin file:
import {Plugin} from "prosemirror-state";
import {uuidv4} from "uuidv4";
const isTargetNodeOfType = (node, type) => (node.type === type);
const isNodeHasAttribute = (node, attrName) => Boolean(node.attrs && node.attrs[attrName]);
const attrName = "guid";
export const createPlugin = (guidGenerator = uuidv4) => {
return new Plugin({
appendTransaction: (transactions, prevState, nextState) => {
const tr = nextState.tr;
let modified = false;
if (transactions.some((transaction) => transaction.docChanged)) {
// Adds a unique id to a node
nextState.doc.descendants((node, pos) => {
const {paragraph} = nextState.schema.nodes;
if (isTargetNodeOfType(node, paragraph) && !isNodeHasAttribute(node, attrName)) {
const attrs = node.attrs;
tr.setNodeMarkup(pos, undefined, {...attrs, [attrName]: guidGenerator()});
modified = true;
}
});
}
return modified ? tr : null;
},
});
};
Schema file:
import {Schema} from "prosemirror-model";
import {marks, nodes} from "prosemirror-schema-basic";
const baseNodes = {
...nodes,
// customize basic paragraph with guid
paragraph: {
content: "inline*",
group: "block",
attrs: {
guid: {default: ""},
},
parseDOM: [
{
tag: "p",
getAttrs: (dom) => ({guid: dom.getAttribute("data-guid")}),
},
],
toDOM(node) {
const {guid} = node.attrs;
return ["p", {"data-guid": guid}, 0];
},
},
};
const base = new Schema({nodes: baseNodes, marks: marks});
...
Thank you @marijn for such a wonderful library!
Good luck!