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

replaceAll #878

Closed
char0n opened this issue Apr 13, 2019 · 10 comments · Fixed by #879
Closed

replaceAll #878

char0n opened this issue Apr 13, 2019 · 10 comments · Fixed by #879
Assignees

Comments

@char0n
Copy link
Owner

char0n commented Apr 13, 2019

https://github.com/tc39/proposal-string-replaceall

Currently in stage 2. Should be fairly safe to implement.

char0n added a commit that referenced this issue Apr 14, 2019
char0n added a commit that referenced this issue Apr 14, 2019
char0n added a commit that referenced this issue Apr 14, 2019
char0n added a commit that referenced this issue Apr 14, 2019
@char0n
Copy link
Owner Author

char0n commented Oct 14, 2019

This has reached stage 3. Possibly to replace our custom implementation with future polyfill. One things was added there and that is handling of empty strings: https://github.com/tc39/proposal-string-replaceall#what-happens-if-searchvalue-is-the-empty-string

We need to make sure our implementation compensate for that.

@char0n char0n reopened this Oct 14, 2019
@js636f
Copy link
Collaborator

js636f commented Oct 15, 2019

@char0n, so, we need this:

'xxx'.replaceAll('', '_');
// → '_x_x_x_'

now we have this:

RA.replaceAll('', '_', 'xxx');
// → 'x_x_x'

I think we may change this:

const replaceAll = curryN(3, (searchValue, replaceValue, str) => {
  if (isRegExp(searchValue)) {
    throw new Error('searchValue must be a String, not a RegExp.');
  }

  return pipe(
    split(String(searchValue)),
    join(String(replaceValue))
  )(str);
});

to something like this:

const replaceAll = curryN(3, (searchValue, replaceValue, str) => {
  if (isRegExp(searchValue)) {
    throw new Error('searchValue must be a String, not a RegExp.');
  }

  const callIfsearchValueIsEmpty = () => {
    return String(str).replace(/(?:)/g, String(replaceValue));
  }

  if (searchValue==='') 
    return callIfsearchValueIsEmpty();

  return pipe(
    split(String(searchValue)),
    join(String(replaceValue))
  )(str);

});

and add this to the test cases:

it('should correctly handle empty searchValue case', function() {
    const value = 'xxx';
    const actual = RA.replaceAll('', '_', value);
    const expected = '_x_x_x_';

    assert.strictEqual(actual, expected);
  });

If these ideas look reasonable - I'm ready to prepare and make a PR.

@char0n
Copy link
Owner Author

char0n commented Oct 15, 2019

@js636f your assumptions are correct. But we need a little bit different strategy. If we get this right we might be the first reference implementation. So let's do it in the following way:

1.) Ponyfill

Create a ponyfil in our ponyfill directory. This ponyfill must not use anything from ramda or ramda-adjunct bust mu be pure javascript, so that it's framework agnostic.

2.) Implementation

Then as usually, we check for String.prototype.replaceAll and use it if present, otherwise use ponyfill.

Here is an example how ponyfill can look like: https://twitter.com/_developit/status/1097302964274892800?lang=en (out of date - stage 1)

I'm assigning this issue to you, own it, if you wish!

@char0n
Copy link
Owner Author

char0n commented Oct 15, 2019

@js636f would be good for you to watch this issue: tc39/proposal-string-replaceall#29

@js636f
Copy link
Collaborator

js636f commented Oct 15, 2019

@char0n, OK, thank you! I'm working on this.

@js636f
Copy link
Collaborator

js636f commented Oct 15, 2019

This is my implementation of the base code.

let replaceAllPonyFill = (str, searchValue, replaceValue) => {

  if (str === undefined || 
      str === null ||
      searchValue === undefined ||
      searchValue === null ||
      replaceValue === undefined ||
      replaceValue === null) {
        throw TypeError('Input values must not be `null` or `undefined`');
    }

    if (typeof str !== 'string' ||
        typeof replaceValue !== 'string') {
          throw TypeError('`str` and `replaceValue` must be strings');
    }

    if (typeof searchValue !== 'string' && 
        !(searchValue instanceof RegExp)) {
          throw TypeError('`searchValue`  must be a string or an regexp');
    }

  //searchValue is an empty string
  if (searchValue==='') return str.replace(/(?:)/g, replaceValue);
  
  //searchValue is a global regexp
  if (searchValue instanceof RegExp){
    if (searchValue.flags.indexOf('g')===-1){
      throw TypeError('`.replaceAll` does not allow non-global regexes');
    }
    return str.replace(searchValue, replaceValue);
  }

  //common case
  return str.split(searchValue).join(replaceValue);
}

let text = 'xxx';

console.log(replaceAllPonyFill(text,'x','v'));
//vvv

console.log(replaceAllPonyFill(text,'','_'));
//_x_x_x_

console.log(replaceAllPonyFill(text,/x/g,'v'));
//vvv

//console.log(replaceAllPonyFill(text,/x/,'v'));
//TypeError

//console.log(replaceAllPonyFill(555,'5','v'));
//TypeError

//console.log(replaceAllPonyFill(text,5,'v'));
//TypeError

//console.log(replaceAllPonyFill(text,'x',5));
//TypeError

//console.log(replaceAllPonyFill(null,'x','v'));
//TypeError

It seems to me it's working and it's in line with the proposal.

If it looks OK, then I'm ready to make a PR, to refine it and to change existing replaceAll.js-related things. And I think we need to make a ponyfill folder (I can't find it) and perhaps we should write dedicated tests for such ponyfills (like in the new folder test/ponyfills).

@char0n
Copy link
Owner Author

char0n commented Oct 16, 2019

Implementation seems about right. Did you base your work on https://github.com/zloirock/core-js/blob/v3.3.2/packages/core-js/modules/esnext.string.replace-all.js ?

Regarding ponyfill vs polyfill. When this library was born, we refereed to all the ponyfills incorectly as polyfills. We have a standing issue which should solve this later. So for now (for consistency), put you ponyfill into src/internal/polyfills directory. Usually when we create a ponyfill we test it directly in the function that's using it as demonstrated here. Because the reasoning it that in any other code we want to use our on RA.replaceAll and not the naked ponyfill.

@js636f
Copy link
Collaborator

js636f commented Oct 16, 2019

My implementation is mainly based on the proposal and on this implementation (global regex check, TypeError exception), I think I understand the structure, so, I'm working on the PR.

@js636f
Copy link
Collaborator

js636f commented Oct 16, 2019

I made the PR.

@char0n
Copy link
Owner Author

char0n commented Oct 30, 2019

Another polyfill https://npmjs.com/string.prototype.replaceall

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

Successfully merging a pull request may close this issue.

2 participants