Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: $convertFromMarkdownString triggers the breakdown of the editor #5385

Closed
2wheeh opened this issue Dec 13, 2023 · 1 comment · Fixed by #5393
Closed

Bug: $convertFromMarkdownString triggers the breakdown of the editor #5385

2wheeh opened this issue Dec 13, 2023 · 1 comment · Fixed by #5393

Comments

@2wheeh
Copy link
Contributor

2wheeh commented Dec 13, 2023

Lexical version: latest (0.12.5)

Steps To Reproduce

  1. go to the Playground.
  2. place the link and image on the same line on markdown mode.
  3. you should place link on the more left than image.
  4. and there shouldn't anything but just text in between those.
  5. click convert button.
  6. bomb

Link to code example: can reproduce in the Playground

The current behavior

before-convertfrommarkdown.mov

The expected behavior

it should convert to link node and image node

@2wheeh
Copy link
Contributor Author

2wheeh commented Dec 14, 2023

This bug happens as following scenario within this function:

function importTextMatchTransformers(

  1. When textNode initially contains several matches and the more rightward match comes first,
  2. textNode is not re-assigned, and we get leftTextNode, replaceNode and rightTextNode.
  3. Now textNode and leftTextNode is referencing the same node.
  4. Subsequently, if leftTextNode has match on the very first position (index 0), it replace this node in the recursive calls.
  5. which means this node no longer has parent, pre, next. (but has key, text yet).
  6. After this recursive calls done, it get back to the first call continuing while statement with textNode.
  7. As It has same text as leftTextNode matches, it has match (but this node doesn't have parent anymore.)
  8. It eventually reaches to the replace function.
  9. which is followed by getParentOrThrow so it throws an exception.

suggestions A.

  1. Pass textNode as a referencedNode to recursive calls.
  2. Check the return value and if it has been replaced,
  3. then set textNode.__text to empty string.
  4. Because textNode has type TextNode, re-assigning it to null or undefined is not allowed.
function importTextMatchTransformers(
  textNode_: TextNode,
  textMatchTransformers: Array<TextMatchTransformer>,
  referencedNode?: TextNode,
) {
  let textNode = textNode_;
  let isReferencedNodeReplaced = false;
    // ... 
    // ...
      if (leftTextNode) {
        const isTextNodeReplaced = importTextMatchTransformers(
          leftTextNode,
          textMatchTransformers,
          textNode,
        );

        if (isTextNodeReplaced) {
          textNode.__text = '';
        }
      }

      transformer.replace(replaceNode, match);

      if (referencedNode === replaceNode) {
        isReferencedNodeReplaced = true;
      }

      continue mainLoop;
    }

    break;
  }
  return isReferencedNodeReplaced;
}

Suggestion B.

  1. Avoid using leftTextNode which can point to the same node as textNode.
  2. Don't pass it to recursive calls.
  3. to achieve it, modify the sequence a bit.

I think this way is a less error-prone and simpler approach.
I open a PR with this.

function importTextMatchTransformers(
  textNode_: TextNode,
  textMatchTransformers: Array<TextMatchTransformer>,
) {
  let textNode = textNode_;

  mainLoop: while (textNode) {
    for (const transformer of textMatchTransformers) {
        // ....
      const startIndex = match.index || 0;
      const endIndex = startIndex + match[0].length;
      let replaceNode, newTextNode;

      if (startIndex === 0) {
        [replaceNode, textNode] = textNode.splitText(endIndex);
      } else {
        [, replaceNode, newTextNode] = textNode.splitText(startIndex, endIndex);
      }

      if (newTextNode) {
        importTextMatchTransformers(newTextNode, textMatchTransformers);
      }
      transformer.replace(replaceNode, match);
      continue mainLoop;
    }

    break;
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
1 participant