From 35776ac011c5cd6723f6ebac567d7d0e1979619d Mon Sep 17 00:00:00 2001 From: Denis Pushkarev Date: Thu, 4 Apr 2019 15:29:53 +0700 Subject: [PATCH 1/2] move `String#replaceAll` to stage 2, update behavior by the spec draft per https://github.com/babel/proposals/issues/57#issuecomment-476838130 and https://tc39.github.io/proposal-string-replaceall/ --- README.md | 30 ++++++------- packages/core-js-compat/src/data.js | 2 + .../core-js-compat/src/modules-by-versions.js | 1 + packages/core-js/features/symbol/index.js | 1 + .../core-js/features/symbol/replace-all.js | 3 ++ .../modules/esnext.string.replace-all.js | 44 +++++++++++++++---- .../modules/esnext.symbol.replace-all.js | 3 ++ .../core-js/proposals/string-replace-all.js | 1 + packages/core-js/stage/1.js | 1 - packages/core-js/stage/2.js | 1 + tests/commonjs.js | 1 + tests/compat/tests.js | 3 ++ tests/pure/esnext.string.replace-all.js | 17 +++++++ tests/pure/esnext.symbol.replace-all.js | 6 +++ tests/pure/index.js | 2 + tests/tests/esnext.string.replace-all.js | 16 +++++++ tests/tests/esnext.symbol.replace-all.js | 13 ++++++ tests/tests/index.js | 1 + 18 files changed, 122 insertions(+), 24 deletions(-) create mode 100644 packages/core-js/features/symbol/replace-all.js create mode 100644 packages/core-js/modules/esnext.symbol.replace-all.js create mode 100644 tests/pure/esnext.symbol.replace-all.js create mode 100644 tests/tests/esnext.symbol.replace-all.js diff --git a/README.md b/README.md index 3786926e6413..e7007d069bb1 100644 --- a/README.md +++ b/README.md @@ -1664,6 +1664,21 @@ new Set([1, 2, 3]).isDisjointFrom([4, 5, 6]); // => true new Set([1, 2, 3]).isSubsetOf([5, 4, 3, 2, 1]); // => true new Set([5, 4, 3, 2, 1]).isSupersetOf([1, 2, 3]); // => true ``` +* `String#replaceAll` [proposal](https://github.com/tc39/proposal-string-replace-all) - module [`esnext.string.replace-all`](https://github.com/zloirock/core-js/blob/v3.0.1/packages/core-js/modules/esnext.string.replace-all.js) +```js +class String { + replaceAll(searchValue: string | RegExp, replaceString: string): string; +} +``` +[*CommonJS entry points:*](#commonjs-api) +```js +core-js/proposals/string-replace-all +core-js/features/string/replace-all +``` +[*Examples*](https://goo.gl/wUXNXN): +```js +'Test abc test test abc test.'.replaceAll('abc', 'foo'); // -> 'Test foo test test foo test.' +``` #### Stage 1 proposals [*CommonJS entry points:*](#commonjs-api) @@ -1693,21 +1708,6 @@ array.lastItem = 4; array; // => [1, 2, 4] ``` -* `String#replaceAll` [proposal](https://github.com/tc39/proposal-string-replace-all) - module [`esnext.string.replace-all`](https://github.com/zloirock/core-js/blob/v3.0.1/packages/core-js/modules/esnext.string.replace-all.js) -```js -class String { - replaceAll(searchValue: string | RegExp, replaceString: string): string; -} -``` -[*CommonJS entry points:*](#commonjs-api) -```js -core-js/proposals/string-replace-all -core-js/features/string/replace-all -``` -[*Examples*](https://goo.gl/wUXNXN): -```js -'Test abc test test abc test.'.replaceAll('abc', 'foo'); // -> 'Test foo test test foo test.' -``` * `Promise.try` [proposal](https://github.com/tc39/proposal-promise-try) - module [`esnext.promise.try`](https://github.com/zloirock/core-js/blob/v3.0.1/packages/core-js/modules/esnext.promise.try.js) ```js class Promise { diff --git a/packages/core-js-compat/src/data.js b/packages/core-js-compat/src/data.js index f9f27a07d163..f70dd4ca6915 100644 --- a/packages/core-js-compat/src/data.js +++ b/packages/core-js-compat/src/data.js @@ -1331,6 +1331,8 @@ const data = { }, 'esnext.symbol.pattern-match': { }, + 'esnext.symbol.replace-all': { + }, 'esnext.weak-map.delete-all': { }, 'esnext.weak-map.from': { diff --git a/packages/core-js-compat/src/modules-by-versions.js b/packages/core-js-compat/src/modules-by-versions.js index 62ca8fcba710..2ca530c7a020 100644 --- a/packages/core-js-compat/src/modules-by-versions.js +++ b/packages/core-js-compat/src/modules-by-versions.js @@ -3,5 +3,6 @@ module.exports = { 3.1: [ 'es.string.match-all', 'es.symbol.match-all', + 'esnext.symbol.replace-all', ], }; diff --git a/packages/core-js/features/symbol/index.js b/packages/core-js/features/symbol/index.js index bdaac9b462a8..578691cd71a3 100644 --- a/packages/core-js/features/symbol/index.js +++ b/packages/core-js/features/symbol/index.js @@ -3,3 +3,4 @@ module.exports = require('../../es/symbol'); require('../../modules/esnext.symbol.dispose'); require('../../modules/esnext.symbol.observable'); require('../../modules/esnext.symbol.pattern-match'); +require('../../modules/esnext.symbol.replace-all'); diff --git a/packages/core-js/features/symbol/replace-all.js b/packages/core-js/features/symbol/replace-all.js new file mode 100644 index 000000000000..9d24d37ae902 --- /dev/null +++ b/packages/core-js/features/symbol/replace-all.js @@ -0,0 +1,3 @@ +require('../../modules/esnext.symbol.replace-all'); + +module.exports = require('../../internals/wrapped-well-known-symbol').f('replaceAll'); diff --git a/packages/core-js/modules/esnext.string.replace-all.js b/packages/core-js/modules/esnext.string.replace-all.js index a6e114eb80ce..235247fe0e76 100644 --- a/packages/core-js/modules/esnext.string.replace-all.js +++ b/packages/core-js/modules/esnext.string.replace-all.js @@ -1,22 +1,50 @@ 'use strict'; +var hide = require('../internals/hide'); var requireObjectCoercible = require('../internals/require-object-coercible'); +var anObject = require('../internals/an-object'); var isRegExp = require('../internals/is-regexp'); var getRegExpFlags = require('../internals/regexp-flags'); var speciesConstructor = require('../internals/species-constructor'); +var REPLACE_ALL = require('../internals/well-known-symbol')('replaceAll'); +var IS_PURE = require('../internals/is-pure'); +var RegExpPrototype = RegExp.prototype; + +var $replaceAll = function (string, replaceValue) { + var rx = anObject(this); + var flags = String('flags' in RegExpPrototype ? rx.flags : getRegExpFlags.call(rx)); + if (!~flags.indexOf('g')) { + rx = new (speciesConstructor(rx, RegExp))(rx.source, flags + 'g'); + } + return String(string).replace(rx, replaceValue); +}; // `String.prototype.replaceAll` method // https://github.com/tc39/proposal-string-replace-all require('../internals/export')({ target: 'String', proto: true }, { replaceAll: function replaceAll(searchValue, replaceValue) { var O = requireObjectCoercible(this); - var search, flags; - if (isRegExp(searchValue)) { - flags = getRegExpFlags.call(searchValue); - if (!~flags.indexOf('g')) { - search = new (speciesConstructor(searchValue, RegExp))(searchValue.source, flags + 'g'); - } else search = searchValue; - return String(O).replace(search, replaceValue); + var replacer, string, searchString, template, result, i; + if (searchValue != null) { + replacer = searchValue[REPLACE_ALL]; + if (replacer !== undefined) { + return replacer.call(searchValue, O, replaceValue); + } else if (IS_PURE && isRegExp(searchValue)) { + return $replaceAll.call(searchValue, O, replaceValue); + } + } + string = String(O); + searchString = String(searchValue); + template = string.split(searchString); + if (typeof replaceValue !== 'function') { + return template.join(String(replaceValue)); } - return String(O).split(searchValue).join(replaceValue); + result = template[0]; + for (i = 1; i < template.length; i++) { + result += String(replaceValue(searchString, i - 1, string)); + result += template[i]; + } + return result; } }); + +IS_PURE || REPLACE_ALL in RegExpPrototype || hide(RegExpPrototype, REPLACE_ALL, $replaceAll); diff --git a/packages/core-js/modules/esnext.symbol.replace-all.js b/packages/core-js/modules/esnext.symbol.replace-all.js new file mode 100644 index 000000000000..ed80d7049e29 --- /dev/null +++ b/packages/core-js/modules/esnext.symbol.replace-all.js @@ -0,0 +1,3 @@ +// `Symbol.replaceAll` well-known symbol +// https://tc39.github.io/proposal-string-replaceall/ +require('../internals/define-well-known-symbol')('replaceAll'); diff --git a/packages/core-js/proposals/string-replace-all.js b/packages/core-js/proposals/string-replace-all.js index d5d30341fd0f..c36697d72242 100644 --- a/packages/core-js/proposals/string-replace-all.js +++ b/packages/core-js/proposals/string-replace-all.js @@ -1 +1,2 @@ require('../modules/esnext.string.replace-all'); +require('../modules/esnext.symbol.replace-all'); diff --git a/packages/core-js/stage/1.js b/packages/core-js/stage/1.js index c49e999ed490..7625b8da9ee3 100644 --- a/packages/core-js/stage/1.js +++ b/packages/core-js/stage/1.js @@ -10,7 +10,6 @@ require('../proposals/promise-try'); require('../proposals/keys-composition'); require('../proposals/seeded-random'); require('../proposals/string-code-points'); -require('../proposals/string-replace-all'); require('../proposals/using-statement'); require('../proposals/promise-any'); diff --git a/packages/core-js/stage/2.js b/packages/core-js/stage/2.js index 872defffa76f..edac10dbc879 100644 --- a/packages/core-js/stage/2.js +++ b/packages/core-js/stage/2.js @@ -1,3 +1,4 @@ require('../proposals/set-methods'); +require('../proposals/string-replace-all'); module.exports = require('./3'); diff --git a/tests/commonjs.js b/tests/commonjs.js index d3904d65d6c4..2f75941b66e2 100644 --- a/tests/commonjs.js +++ b/tests/commonjs.js @@ -286,6 +286,7 @@ for (const _PATH of ['../packages/core-js-pure', '../packages/core-js']) { ok(load('features/symbol/async-iterator')); ok(load('features/symbol/observable')); ok(load('features/symbol/pattern-match')); + ok(load('features/symbol/replace-all')); ok(load('features/symbol/dispose')); ok(typeof load('features/symbol/for') === 'function'); ok(typeof load('features/symbol/key-for') === 'function'); diff --git a/tests/compat/tests.js b/tests/compat/tests.js index 9616c8c26fbc..608bf9fa6e93 100644 --- a/tests/compat/tests.js +++ b/tests/compat/tests.js @@ -1233,6 +1233,9 @@ GLOBAL.tests = { 'esnext.symbol.pattern-match': function () { return Symbol.patternMatch; }, + 'esnext.symbol.replace-all': function () { + return Symbol.replaceAll; + }, 'esnext.weak-map.delete-all': function () { return WeakMap.prototype.deleteAll; }, diff --git a/tests/pure/esnext.string.replace-all.js b/tests/pure/esnext.string.replace-all.js index 92710ac3d2e9..a597c9b2f88b 100644 --- a/tests/pure/esnext.string.replace-all.js +++ b/tests/pure/esnext.string.replace-all.js @@ -1,6 +1,7 @@ import { STRICT } from '../helpers/constants'; import replaceAll from 'core-js-pure/features/string/replace-all'; +import Symbol from 'core-js-pure/features/symbol'; QUnit.test('String#replaceAll', assert => { assert.isFunction(replaceAll); @@ -8,6 +9,22 @@ QUnit.test('String#replaceAll', assert => { assert.same(replaceAll('foo', 'o', {}), 'f[object Object][object Object]'); assert.same(replaceAll('[object Object]x[object Object]', {}, 'y'), 'yxy'); assert.same(replaceAll({}, 'bject', 'lolo'), '[ololo Ololo]'); + assert.same(replaceAll('aba', 'b', (search, i, string) => { + assert.same(search, 'b', '`search` is `b`'); + assert.same(i, 0, '`i` is 0'); + assert.same(string, 'aba', '`string` is `aba`'); + return 'c'; + }), 'aca'); + const searcher = { + [Symbol.replaceAll](O, replaceValue) { + assert.same(this, searcher, '`this` is `searcher`'); + assert.same(String(O), 'aba', '`O` is `aba`'); + assert.same(String(replaceValue), 'c', '`replaceValue` is `c`'); + return 'foo'; + }, + }; + assert.same(replaceAll('aba', searcher, 'c'), 'foo'); + assert.same(replaceAll('aba', 'b'), 'aundefineda'); if (STRICT) { assert.throws(() => replaceAll(null, 'a', 'b'), TypeError); assert.throws(() => replaceAll(undefined, 'a', 'b'), TypeError); diff --git a/tests/pure/esnext.symbol.replace-all.js b/tests/pure/esnext.symbol.replace-all.js new file mode 100644 index 000000000000..85996dafad77 --- /dev/null +++ b/tests/pure/esnext.symbol.replace-all.js @@ -0,0 +1,6 @@ +import Symbol from 'core-js-pure/features/symbol'; + +QUnit.test('Symbol.replaceAll', assert => { + assert.ok('replaceAll' in Symbol, 'Symbol.replaceAll is available'); + assert.ok(Object(Symbol.replaceAll) instanceof Symbol, 'Symbol.replaceAll is symbol'); +}); diff --git a/tests/pure/index.js b/tests/pure/index.js index 9441af7db155..5f2b001edd7a 100644 --- a/tests/pure/index.js +++ b/tests/pure/index.js @@ -205,8 +205,10 @@ import './esnext.set.symmetric-difference'; import './esnext.set.union'; import './esnext.string.at'; import './esnext.string.code-points'; +import './esnext.symbol.dispose'; import './esnext.symbol.observable'; import './esnext.symbol.pattern-match'; +import './esnext.symbol.replace-all'; import './esnext.weak-map.delete-all'; import './esnext.weak-map.from'; import './esnext.weak-map.of'; diff --git a/tests/tests/esnext.string.replace-all.js b/tests/tests/esnext.string.replace-all.js index e8d1dbfa0bec..91f9612d7f54 100644 --- a/tests/tests/esnext.string.replace-all.js +++ b/tests/tests/esnext.string.replace-all.js @@ -11,6 +11,22 @@ QUnit.test('String#replaceAll', assert => { assert.same('foo'.replaceAll('o', {}), 'f[object Object][object Object]'); assert.same('[object Object]x[object Object]'.replaceAll({}, 'y'), 'yxy'); assert.same(replaceAll.call({}, 'bject', 'lolo'), '[ololo Ololo]'); + assert.same('aba'.replaceAll('b', (search, i, string) => { + assert.same(search, 'b', '`search` is `b`'); + assert.same(i, 0, '`i` is 0'); + assert.same(string, 'aba', '`string` is `aba`'); + return 'c'; + }), 'aca'); + const searcher = { + [Symbol.replaceAll](O, replaceValue) { + assert.same(this, searcher, '`this` is `searcher`'); + assert.same(String(O), 'aba', '`O` is `aba`'); + assert.same(String(replaceValue), 'c', '`replaceValue` is `c`'); + return 'foo'; + }, + }; + assert.same('aba'.replaceAll(searcher, 'c'), 'foo'); + assert.same('aba'.replaceAll('b'), 'aundefineda'); if (STRICT) { assert.throws(() => replaceAll.call(null, 'a', 'b'), TypeError); assert.throws(() => replaceAll.call(undefined, 'a', 'b'), TypeError); diff --git a/tests/tests/esnext.symbol.replace-all.js b/tests/tests/esnext.symbol.replace-all.js new file mode 100644 index 000000000000..d4ab590d89e3 --- /dev/null +++ b/tests/tests/esnext.symbol.replace-all.js @@ -0,0 +1,13 @@ +import { DESCRIPTORS } from '../helpers/constants'; + +QUnit.test('Symbol.replaceAll', assert => { + assert.ok('replaceAll' in Symbol, 'Symbol.replaceAll is available'); + assert.nonEnumerable(Symbol, 'replaceAll'); + assert.ok(Object(Symbol.replaceAll) instanceof Symbol, 'Symbol.replaceAll is symbol'); + if (DESCRIPTORS) { + const descriptor = Object.getOwnPropertyDescriptor(Symbol, 'replaceAll'); + assert.ok(!descriptor.enumerble, 'non-enumerable'); + assert.ok(!descriptor.writable, 'non-writable'); + assert.ok(!descriptor.configurable, 'non-configurable'); + } +}); diff --git a/tests/tests/index.js b/tests/tests/index.js index a6fe094f48db..0d73fe220332 100644 --- a/tests/tests/index.js +++ b/tests/tests/index.js @@ -260,6 +260,7 @@ import './esnext.string.replace-all'; import './esnext.symbol.dispose'; import './esnext.symbol.observable'; import './esnext.symbol.pattern-match'; +import './esnext.symbol.replace-all'; import './esnext.weak-map.delete-all'; import './esnext.weak-map.from'; import './esnext.weak-map.of'; From 1678ce689d4e8ab5656d6548ac4300355318f1e4 Mon Sep 17 00:00:00 2001 From: Denis Pushkarev Date: Sat, 11 May 2019 05:25:29 +0700 Subject: [PATCH 2/2] update the changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f117842745e..c8e438fff977 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ##### 3.1.0 - `String#matchAll` moved to stable ES, exposed `Symbol.matchAll`, [babel/proposals#57 (comment)](https://github.com/babel/proposals/issues/57#issuecomment-476783115) - `Promise.allSettled` moved to stage 3, [babel/proposals#57 (comment)](https://github.com/babel/proposals/issues/57#issuecomment-476837985) +- `String#replaceAll` moved to stage 2, behavior updated by the spec draft, [babel/proposals#57 (comment)](https://github.com/babel/proposals/issues/57#issuecomment-476838130), [`https://tc39.github.io/proposal-string-replaceall/`](https://tc39.github.io/proposal-string-replaceall/) - `Promise.any` moved to stage 1, [babel/proposals#57 (comment)](https://github.com/babel/proposals/issues/57#issuecomment-477335963) - Removed `es.regexp.flags` dependency from `es.regexp.to-string`, [#536](https://github.com/zloirock/core-js/issues/536), [#537](https://github.com/zloirock/core-js/issues/537) - Fixed IE8- non-enumerable properties support in `Object.{ assign, entries, values }`, [#541](https://github.com/zloirock/core-js/issues/541)