Bug while transforming slices? Or maybe just doing things wrong

I was trying to follow this:


to get link pasting working nicely in my project, but I ran into some issues while updating it. This is what I ended up with:

let linkPlugin = new Plugin({
  props: {
    transformPasted: (slice: Slice) => {
      return new Slice(linkify(slice.content), slice.openLeft, slice.openRight)

const HTTP_LINK_REGEX = /\bhttps?:\/\/[\w_\/\.]+/g
let linkify = function(fragment: Fragment): Fragment {
  var linkified : Node[] = []
  fragment.forEach(function(child: Node){
    if (child.isText) {
      const text = child.text as string
      var pos = 0, match

      while (match = HTTP_LINK_REGEX.exec(text)) {
        var start = match.index
        var end = start + match[0].length
        var link = child.type.schema.marks['link']

        // simply copy across the text from before the match
        if (start > 0) {
          linkified.push(child.cut(pos, start))

        const urlText = text.slice(start, end)
          child.cut(start, end).mark(link.create({href: urlText}).addToSet(child.marks))
        pos = end

      // copy over whatever is left
      if (pos < text.length) {
    } else {

  return Fragment.fromArray(linkified)

In particular, my first attempt I used

child.type.create(undefined, child.cut(start, end), link.create({href: urlText}).addToSet(child.marks))

This was invoking the generic Node constructor here: https://github.com/ProseMirror/prosemirror-model/blob/master/src/node.js#L22

rather than the TextNode constructor, so I ended up with a text node which had node.text == undefined, which then caused an error to be thrown while rendering.

Updating the code to the above:

child.cut(start, end).mark(link.create({href: urlText}).addToSet(child.marks))

fixed the issue (the created node had text populated).

Is there a reason why one way works and another doesn’t? Or should I file a bug regarding this issue?

NodeType.create doesn’t work for text nodes, since it doesn’t provide a way to specify the node’s text. In this case, you’re passing a Node object as second argument, which is probably also not intentional.

I’ll add an error check to the method to point this out more clearly.

Thanks very much for your reply. I hope you don’t mind considering a couple followup questions,

Since NodeType#create is not static, I don’t understand why the text instance of NodeType can’t be used to create well-behaved text nodes… I think I’m generally not getting the role that NodeType holds, especially between NodeSpec and Node.

The second argument to NodeType#create seems to take Fragment | Node | [Node], so I’m not sure why passing the node as the second arg would be the wrong thing to do.

Basically, the function signature doesn’t fit. A text node needs a text string, and NodeType.create doesn’t allow you to pass one. Giving it a different signature depending on the node type seems a very bad idea.

That’s the content of the node. Text nodes never allow content, so giving it a content node doesn’t make sense.

Ah! I think my confusion stemmed from me assuming the content use here was fairly general, and from not realizing the specialness of the text node. So, in my head that made sense as “the content of the text node should be text”

Things are much more clear now! Thanks again for your time.