syntax-tree / hast-util-find-and-replace

utility to find and replace text in a hast tree

Home Page:https://unifiedjs.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Very particular situation results in some text not being replaced

0xR opened this issue · comments

Initial checklist

Affected packages and versions

3 and 4

Link to runnable example

https://codesandbox.io/s/hast-replace-error-fzuxo3?file=/error.spec.js

Steps to reproduce

The code sandbox has a broken test. It should not be broken.

Expected behavior

Both replaceme instances should be replaced.

Actual behavior

The first "replaceme" instance is replaced the 2nd bold instance of "replaceme" is not replaced. If I move the bold text earlier in the sentence there is no issue. If I make the text not bold there is also no issue.

I had trouble finding the bug in the sourcecode of this package. Maybe you can help out.

Runtime

Node v16, Node v14

Package manager

npm v7

OS

Linux

Build and bundle tools

Webpack, Parcel

commented

Managed to narrow it down a bit.

  1. It also occurs in mdast-util-find-and-replace
  2. Here are some more cases. true means works, false means fails:
['x x', true],
['*x* x', true],
['*x* x x', true],
['x *x*', false],
['x *x* x', false],
['x x *x*', false]
  1. I believe the index that’s returned here after replacing is wrong: the text inside emphasis(/strong) is not checked.

Hi! This was marked as ready to be worked on! Note that while this is ready to be worked on, nothing is said about priority: it may take a while for this to be solved.

Is this something you can and want to work on?

Team: please use the area/* (to describe the scope of the change), platform/* (if this is related to a specific one), and semver/* and type/* labels to annotate this. If this is first-timers friendly, add good first issue and if this could use help, add help wanted.

commented

I’m off for now and focussing on non-issues this month, so might be a while to get to this!

As a workaround I created my own plugin that does not seem to have the bug.
Maybe it's useful for someone reading this:

function isText(node: Node): node is Text {
  return (node as Text).type === 'text';
}

function makeText(value: string): Text {
  return {
    type: 'text',
    value,
  };
}

export const rehypeReplaceRegex: Plugin<
  [{ regex: RegExp; replacer: (match: string) => Element | string }]
> = ({ replacer, regex }) => (tree) => {
  unistFlatMap(tree, (node, index, parent) => {
    if (isText(node)) {
      const match = node.value.match(regex);
      if (!match) {
        return [node];
      }
      const split = node.value.split(regex);
      const result: (Text | Element)[] = [];
      split.forEach((nonMatch, i) => {
        const precedingMatch = match[i - 1];
        if (precedingMatch) {
          const replacement = replacer(precedingMatch);
          if (typeof replacement === 'string') {
            result.push(makeText(replacement));
          } else {
            result.push(replacement);
          }
        }
        if (nonMatch) {
          result.push(makeText(nonMatch));
        }
      });
      return result;
    }
    return [node];
  });
};

Hi! This was closed. Team: If this was fixed, please add phase/solved. Otherwise, please add one of the no/* labels.

commented

Found it! P.S. Your deps are out of date!