New Schema - Extending/customizing it

With the recent changes to the SchemaSpec and etc., how can I customize the schema? I need, for example, to add a Video node type and disable code blocks. Previously, I could accomplish this with code like (assuming I have a Video class):

const mySchema = new Schema(defaultSchema.spec.update({
  video: Video,
  code_block: null
}))

It would be nice to have the dinos demo updated when possible since customizing the schema is probably very common.

The idea is that you’ll probably want to duplicate the whole schema definition, so that you have it all in one place and can adjust it as needed. If you prefer not to do that, use plain object copying and changing or define your own helper functions.

const mySpec = {
  nodes: Object.assign({}, defaultSchema.spec.nodes, {video: {type: Video}}),
  groups: Object.assign({}, defaultSchema.spec.groups),
  marks: defaultSchema.spec.marks
})
delete mySpec.nodes.code_block
mySpec.groups.block = mySpec.nodes.block.filter(n => n != "code_block")

So that answer didn’t really satisfy me, because A) plain raw objects are a pain to manipulate, and B) having group declaration separate from nodes makes it very painful to add/remove nodes.

With the current code, which I believe I’ll publish as 0.7.0, you can do:

const mySchema = new Schema({
  nodes: defaultSchema.nodeSpec.remove("code_block").addToEnd("video", {
    type: Video,
    group: "block"
  }),
  marks: defaultSchema.markSpec
})
1 Like

If I add the Video node type like this it later results in an error in model/schema.js:233: spec is undefined This is the method:

static compile(nodes, schema) {
    let result = Object.create(null)
    nodes.forEach((name, spec) => result[name] = new spec.type(name, schema))

    if (!result.doc) throw new RangeError("Every schema needs a 'doc' type")
    if (!result.text) throw new RangeError("Every schema needs a 'text' type")

    return result
  }

In a default node, name would a be a string and spec an object. But on the Video node name is an object (the same one I passed to addToEnd) and spec is undefined, because the node list comes like this: [..., 20: "hardbreak", 21: Object, 22: Object, 23: undefined] (the last object is the Video)

Yeah, I missed a parameter in the original code snippet. I’ve updated (added "video" as first argument to addToEnd)

Right, worked well. Thanks!

It doesn’t look like this API is available anymore. With 0.9.1, what’s the proper way to extend the basic/default schema? I looked around trying to figure out, but couldn’t so I ended up copying the complete code calling new Schema from the schema-basic/index.js.

Thanks, Boris

The SchemaSpec abstraction was dropped. You are now encouraged to use the methods in OrderedMap to extend a schema, by creating a new Schema instance based on the nodeSpec (or markSpec) property or an existing schema.

Ok, thanks!

How can a plugin update an already existing Schema? In my specific case, I have instantiated pm.doc with defaultMarkdownParser.parse('Hello **world**') and I’d like to add the Dino Inline node. I guess I can update the nodeSpec inside the plugin class constructor (and I don’t know if this makes any sense :slight_smile:) like this:

exports.myplugin = new Plugin(class {
  constructor(pm, options) {
    //...
    pm.schema = new Schema({
      nodes: pm.schema.nodeSpec.addBefore("image", "dino", {type: Dino, group: "inline"}),
      marks: pm.schema.markSpec
    })
})

When I try to pm.tr.delete(from, to).insertInline(from, dinoSchema.node("dino", {type: name})).apply() no error is being thrown, but nothing happens. What am I doing wrong?

Thank you for your help

They can’t. First comes a schema, then an editor, and then plugins that act on that, so to say. So you can export a schema or a helper function for creating a schema from your plugin package, but you’ll have to pass it to your editor yourself. The plugin can’t do that.

Thanks! Right, that makes sense.