Skip to content

Commit

Permalink
move well-formed unicode strings proposal to stage 4
Browse files Browse the repository at this point in the history
  • Loading branch information
zloirock committed May 22, 2023
1 parent 8926a86 commit 15c787f
Show file tree
Hide file tree
Showing 37 changed files with 218 additions and 151 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
## Changelog
##### Unreleased
- [Well-formed unicode strings proposal](https://github.com/tc39/proposal-is-usv-string):
- Methods:
- `String.prototype.isWellFormed`
- `String.prototype.toWellFormed`
- Moved to stable ES, [May 2023 TC39 meeting](https://github.com/tc39/proposal-is-usv-string/pull/31)
- Added `es.` namespace modules, `/es/` and `/stable/` namespaces entries
- [Iterator Helpers Stage 3 proposal](https://github.com/tc39/proposal-iterator-helpers):
- Changed `Symbol.iterator` fallback from callable check to `undefined` / `null` check, May 2023 TC39 meeting, [proposal-iterator-helpers/272](https://github.com/tc39/proposal-iterator-helpers/pull/272)
- Removed `IsCallable` check on `NextMethod`, deferring errors to `Call` site, May 2023 TC39 meeting, [proposal-iterator-helpers/274](https://github.com/tc39/proposal-iterator-helpers/pull/274)
Expand Down
47 changes: 23 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -817,7 +817,7 @@ correctionNeeded; // => [1, 1, 3]
```
#### ECMAScript: String and RegExp[⬆](#index)
The main part of `String` features: modules [`es.string.from-code-point`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.from-code-point.js), [`es.string.raw`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.raw.js), [`es.string.iterator`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.iterator.js), [`es.string.split`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.split.js), [`es.string.code-point-at`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.code-point-at.js), [`es.string.ends-with`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.ends-with.js), [`es.string.includes`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.includes.js), [`es.string.repeat`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.repeat.js), [`es.string.pad-start`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.pad-start.js), [`es.string.pad-end`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.pad-end.js), [`es.string.starts-with`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.starts-with.js), [`es.string.trim`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.trim.js), [`es.string.trim-start`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.trim-start.js), [`es.string.trim-end`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.trim-end.js), [`es.string.match-all`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.match-all.js), [`es.string.replace-all`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.replace-all.js), [`es.string.at-alternative`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.at-alternative.js).
The main part of `String` features: modules [`es.string.from-code-point`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.from-code-point.js), [`es.string.raw`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.raw.js), [`es.string.iterator`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.iterator.js), [`es.string.split`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.split.js), [`es.string.code-point-at`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.code-point-at.js), [`es.string.ends-with`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.ends-with.js), [`es.string.includes`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.includes.js), [`es.string.repeat`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.repeat.js), [`es.string.pad-start`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.pad-start.js), [`es.string.pad-end`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.pad-end.js), [`es.string.starts-with`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.starts-with.js), [`es.string.trim`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.trim.js), [`es.string.trim-start`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.trim-start.js), [`es.string.trim-end`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.trim-end.js), [`es.string.match-all`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.match-all.js), [`es.string.replace-all`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.replace-all.js), [`es.string.at-alternative`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.at-alternative.js), [`es.string.is-well-formed`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.is-well-formed.js), [`es.string.to-well-formed`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.to-well-formed.js).
Adding support of well-known [symbols](#ecmascript-symbol) `@@match`, `@@replace`, `@@search` and `@@split` and direct `.exec` calls to related `String` methods, modules [`es.string.match`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.match.js), [`es.string.replace`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.replace.js), [`es.string.search`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.search.js) and [`es.string.split`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.split.js).
Expand Down Expand Up @@ -847,6 +847,8 @@ class String {
trimRight(): string;
trimStart(): string;
trimEnd(): string;
isWellFormed(): boolean;
toWellFormed(): string;
anchor(name: string): string;
big(): string;
blink(): string;
Expand Down Expand Up @@ -907,6 +909,8 @@ core-js(-pure)/es|stable|actual|full/string(/virtual)/trim-start
core-js(-pure)/es|stable|actual|full/string(/virtual)/trim-end
core-js(-pure)/es|stable|actual|full/string(/virtual)/trim-left
core-js(-pure)/es|stable|actual|full/string(/virtual)/trim-right
core-js(-pure)/es|stable|actual|full/string(/virtual)/is-well-formed
core-js(-pure)/es|stable|actual|full/string(/virtual)/to-well-formed
core-js(-pure)/es|stable|actual|full/string(/virtual)/anchor
core-js(-pure)/es|stable|actual|full/string(/virtual)/big
core-js(-pure)/es|stable|actual|full/string(/virtual)/blink
Expand All @@ -932,7 +936,7 @@ core-js/es|stable|actual|full/regexp/to-string
core-js/es|stable|actual|full/escape
core-js/es|stable|actual|full/unescape
```
[*Examples*](https://is.gd/Q8eRhG):
[*Examples*](https://tinyurl.com/22uafm3p):
```js
for (let value of 'a𠮷b') {
console.log(value); // => 'a', '𠮷', 'b'
Expand Down Expand Up @@ -1004,6 +1008,12 @@ for (let [_, d, D] of '1111a2b3cccc'.matchAll(/(\d)(\D)/g)) {

'abc'.at(1); // => 'b'
'abc'.at(-1); // => 'c'

'a💩b'.isWellFormed(); // => true
'a\uD83Db'.isWellFormed(); // => false

'a💩b'.toWellFormed(); // => 'a💩b'
'a\uD83Db'.toWellFormed(); // => 'a�b'
```
#### ECMAScript: Number[⬆](#index)
Module [`es.number.constructor`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.number.constructor.js). `Number` constructor support binary and octal literals, [*example*](https://goo.gl/jRd6b3):
Expand Down Expand Up @@ -2139,6 +2149,17 @@ namespace JSON {
```js
core-js/proposals/well-formed-stringify
```
##### [Well-formed unicode strings](https://github.com/tc39/proposal-is-usv-string)[⬆](#index)
```js
class String {
isWellFormed(): boolean;
toWellFormed(): string;
}
```
[*CommonJS entry points:*](#commonjs-api)
```js
core-js/proposals/well-formed-unicode-strings
```

#### Stage 3 proposals[⬆](#index)

Expand Down Expand Up @@ -2380,28 +2401,6 @@ core-js(-pure)/actual|full/disposable-stack
core-js(-pure)/actual|full/suppressed-error
core-js(-pure)/actual|full/iterator/dispose
```
##### [Well-formed unicode strings](https://github.com/tc39/proposal-is-usv-string)[⬆](#index)
Modules [`esnext.string.is-well-formed`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.string.is-well-formed.js) and [`esnext.string.to-well-formed`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.string.to-well-formed.js)
```js
class String {
isWellFormed(): boolean;
toWellFormed(): string;
}
```
[*CommonJS entry points:*](#commonjs-api)
```js
core-js/proposals/well-formed-unicode-strings
core-js(-pure)/actual|full/string(/virtual)/is-well-formed
core-js(-pure)/actual|full/string(/virtual)/to-well-formed
```
[*Examples*](https://tinyurl.com/2fulc2ak):
```js
'a💩b'.isWellFormed(); // => true
'a\uD83Db'.isWellFormed(); // => false

'a💩b'.toWellFormed(); // => 'a💩b'
'a\uD83Db'.toWellFormed(); // => 'a�b'
```
#### Stage 2 proposals[⬆](#index)
[*CommonJS entry points:*](#commonjs-api)
Expand Down
26 changes: 14 additions & 12 deletions packages/core-js-compat/src/data.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1333,6 +1333,11 @@ export const data = {
hermes: '0.1',
safari: '10.0',
},
'es.string.is-well-formed': {
bun: '0.4.0',
chrome: '111',
safari: '16.4',
},
'es.string.iterator': {
chrome: '41',
edge: '13',
Expand Down Expand Up @@ -1424,6 +1429,13 @@ export const data = {
rhino: '1.7.13',
safari: '1',
},
'es.string.to-well-formed': {
// Safari ToString conversion bug
// https://bugs.webkit.org/show_bug.cgi?id=251757
bun: '0.5.7', // '0.4.0',
chrome: '111',
safari: '16.4',
},
'es.string.trim': {
chrome: '59',
edge: '15',
Expand Down Expand Up @@ -2214,22 +2226,10 @@ export const data = {
},
'esnext.string.dedent': {
},
'esnext.string.is-well-formed': {
bun: '0.4.0',
chrome: '111',
safari: '16.4',
},
// TODO: Remove from `core-js@4`
'esnext.string.match-all': null,
// TODO: Remove from `core-js@4`
'esnext.string.replace-all': null,
'esnext.string.to-well-formed': {
// Safari ToString conversion bug
// https://bugs.webkit.org/show_bug.cgi?id=251757
bun: '0.5.7', // '0.4.0',
chrome: '111',
safari: '16.4',
},
'esnext.symbol.async-dispose': {
},
'esnext.symbol.dispose': {
Expand Down Expand Up @@ -2515,8 +2515,10 @@ export const renamed = new Map([
['esnext.object.has-own', 'es.object.has-own'],
['esnext.promise.all-settled', 'es.promise.all-settled'],
['esnext.promise.any', 'es.promise.any'],
['esnext.string.is-well-formed', 'es.string.is-well-formed'],
['esnext.string.match-all', 'es.string.match-all'],
['esnext.string.replace-all', 'es.string.replace-all'],
['esnext.string.to-well-formed', 'es.string.to-well-formed'],
['esnext.typed-array.at', 'es.typed-array.at'],
['esnext.typed-array.find-last', 'es.typed-array.find-last'],
['esnext.typed-array.find-last-index', 'es.typed-array.find-last-index'],
Expand Down
4 changes: 4 additions & 0 deletions packages/core-js-compat/src/modules-by-versions.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -202,4 +202,8 @@ export default {
'3.30': [
'web.url.can-parse',
],
3.31: [
'es.string.is-well-formed',
'es.string.to-well-formed',
],
};
11 changes: 2 additions & 9 deletions packages/core-js/actual/instance/is-well-formed.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
var isPrototypeOf = require('../../internals/object-is-prototype-of');
var method = require('../string/virtual/is-well-formed');
var parent = require('../../stable/instance/is-well-formed');

var StringPrototype = String.prototype;

module.exports = function (it) {
var own = it.isWellFormed;
return typeof it == 'string' || it === StringPrototype
|| (isPrototypeOf(StringPrototype, it) && own === StringPrototype.isWellFormed) ? method : own;
};
module.exports = parent;
11 changes: 2 additions & 9 deletions packages/core-js/actual/instance/to-well-formed.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
var isPrototypeOf = require('../../internals/object-is-prototype-of');
var method = require('../string/virtual/to-well-formed');
var parent = require('../../stable/instance/to-well-formed');

var StringPrototype = String.prototype;

module.exports = function (it) {
var own = it.toWellFormed;
return typeof it == 'string' || it === StringPrototype
|| (isPrototypeOf(StringPrototype, it) && own === StringPrototype.toWellFormed) ? method : own;
};
module.exports = parent;
1 change: 1 addition & 0 deletions packages/core-js/actual/string/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
var parent = require('../../stable/string');

// TODO: Remove from `core-js@4`
require('../../modules/esnext.string.is-well-formed');
require('../../modules/esnext.string.to-well-formed');

Expand Down
5 changes: 4 additions & 1 deletion packages/core-js/actual/string/is-well-formed.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// TODO: Remove from `core-js@4`
require('../../modules/esnext.string.is-well-formed');

module.exports = require('../../internals/entry-unbind')('String', 'isWellFormed');
var parent = require('../../stable/string/is-well-formed');

module.exports = parent;
5 changes: 4 additions & 1 deletion packages/core-js/actual/string/to-well-formed.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// TODO: Remove from `core-js@4`
require('../../modules/esnext.string.to-well-formed');

module.exports = require('../../internals/entry-unbind')('String', 'toWellFormed');
var parent = require('../../stable/string/to-well-formed');

module.exports = parent;
1 change: 1 addition & 0 deletions packages/core-js/actual/string/virtual/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
var parent = require('../../../stable/string/virtual');

// TODO: Remove from `core-js@4`
require('../../../modules/esnext.string.is-well-formed');
require('../../../modules/esnext.string.to-well-formed');

Expand Down
5 changes: 4 additions & 1 deletion packages/core-js/actual/string/virtual/is-well-formed.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// TODO: Remove from `core-js@4`
require('../../../modules/esnext.string.is-well-formed');

module.exports = require('../../../internals/entry-virtual')('String').isWellFormed;
var parent = require('../../../stable/string/virtual/is-well-formed');

module.exports = parent;
5 changes: 4 additions & 1 deletion packages/core-js/actual/string/virtual/to-well-formed.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// TODO: Remove from `core-js@4`
require('../../../modules/esnext.string.to-well-formed');

module.exports = require('../../../internals/entry-virtual')('String').toWellFormed;
var parent = require('../../../stable/string/virtual/to-well-formed');

module.exports = parent;
10 changes: 10 additions & 0 deletions packages/core-js/es/instance/is-well-formed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
var isPrototypeOf = require('../../internals/object-is-prototype-of');
var method = require('../string/virtual/is-well-formed');

var StringPrototype = String.prototype;

module.exports = function (it) {
var own = it.isWellFormed;
return typeof it == 'string' || it === StringPrototype
|| (isPrototypeOf(StringPrototype, it) && own === StringPrototype.isWellFormed) ? method : own;
};
10 changes: 10 additions & 0 deletions packages/core-js/es/instance/to-well-formed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
var isPrototypeOf = require('../../internals/object-is-prototype-of');
var method = require('../string/virtual/to-well-formed');

var StringPrototype = String.prototype;

module.exports = function (it) {
var own = it.toWellFormed;
return typeof it == 'string' || it === StringPrototype
|| (isPrototypeOf(StringPrototype, it) && own === StringPrototype.toWellFormed) ? method : own;
};
2 changes: 2 additions & 0 deletions packages/core-js/es/string/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require('../../modules/es.string.code-point-at');
require('../../modules/es.string.at-alternative');
require('../../modules/es.string.ends-with');
require('../../modules/es.string.includes');
require('../../modules/es.string.is-well-formed');
require('../../modules/es.string.match');
require('../../modules/es.string.match-all');
require('../../modules/es.string.pad-end');
Expand All @@ -17,6 +18,7 @@ require('../../modules/es.string.search');
require('../../modules/es.string.split');
require('../../modules/es.string.starts-with');
require('../../modules/es.string.substr');
require('../../modules/es.string.to-well-formed');
require('../../modules/es.string.trim');
require('../../modules/es.string.trim-start');
require('../../modules/es.string.trim-end');
Expand Down
3 changes: 3 additions & 0 deletions packages/core-js/es/string/is-well-formed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require('../../modules/es.string.is-well-formed');

module.exports = require('../../internals/entry-unbind')('String', 'isWellFormed');
3 changes: 3 additions & 0 deletions packages/core-js/es/string/to-well-formed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require('../../modules/es.string.to-well-formed');

module.exports = require('../../internals/entry-unbind')('String', 'toWellFormed');
3 changes: 3 additions & 0 deletions packages/core-js/es/string/virtual/is-well-formed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require('../../../modules/es.string.is-well-formed');

module.exports = require('../../../internals/entry-virtual')('String').isWellFormed;
3 changes: 3 additions & 0 deletions packages/core-js/es/string/virtual/to-well-formed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require('../../../modules/es.string.to-well-formed');

module.exports = require('../../../internals/entry-virtual')('String').toWellFormed;
23 changes: 23 additions & 0 deletions packages/core-js/modules/es.string.is-well-formed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use strict';
var $ = require('../internals/export');
var uncurryThis = require('../internals/function-uncurry-this');
var requireObjectCoercible = require('../internals/require-object-coercible');
var toString = require('../internals/to-string');

var charCodeAt = uncurryThis(''.charCodeAt);

// `String.prototype.isWellFormed` method
// https://github.com/tc39/proposal-is-usv-string
$({ target: 'String', proto: true }, {
isWellFormed: function isWellFormed() {
var S = toString(requireObjectCoercible(this));
var length = S.length;
for (var i = 0; i < length; i++) {
var charCode = charCodeAt(S, i);
// single UTF-16 code unit
if ((charCode & 0xF800) != 0xD800) continue;
// unpaired surrogate
if (charCode >= 0xDC00 || ++i >= length || (charCodeAt(S, i) & 0xFC00) != 0xDC00) return false;
} return true;
}
});
43 changes: 43 additions & 0 deletions packages/core-js/modules/es.string.to-well-formed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use strict';
var $ = require('../internals/export');
var call = require('../internals/function-call');
var uncurryThis = require('../internals/function-uncurry-this');
var requireObjectCoercible = require('../internals/require-object-coercible');
var toString = require('../internals/to-string');
var fails = require('../internals/fails');

var $Array = Array;
var charAt = uncurryThis(''.charAt);
var charCodeAt = uncurryThis(''.charCodeAt);
var join = uncurryThis([].join);
// eslint-disable-next-line es/no-string-prototype-iswellformed-towellformed -- safe
var $toWellFormed = ''.toWellFormed;
var REPLACEMENT_CHARACTER = '\uFFFD';

// Safari bug
var TO_STRING_CONVERSION_BUG = $toWellFormed && fails(function () {
return call($toWellFormed, 1) !== '1';
});

// `String.prototype.toWellFormed` method
// https://github.com/tc39/proposal-is-usv-string
$({ target: 'String', proto: true, forced: TO_STRING_CONVERSION_BUG }, {
toWellFormed: function toWellFormed() {
var S = toString(requireObjectCoercible(this));
if (TO_STRING_CONVERSION_BUG) return call($toWellFormed, S);
var length = S.length;
var result = $Array(length);
for (var i = 0; i < length; i++) {
var charCode = charCodeAt(S, i);
// single UTF-16 code unit
if ((charCode & 0xF800) != 0xD800) result[i] = charAt(S, i);
// unpaired surrogate
else if (charCode >= 0xDC00 || i + 1 >= length || (charCodeAt(S, i + 1) & 0xFC00) != 0xDC00) result[i] = REPLACEMENT_CHARACTER;
// surrogate pair
else {
result[i] = charAt(S, i);
result[++i] = charAt(S, i);
}
} return join(result, '');
}
});
25 changes: 2 additions & 23 deletions packages/core-js/modules/esnext.string.is-well-formed.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,2 @@
'use strict';
var $ = require('../internals/export');
var uncurryThis = require('../internals/function-uncurry-this');
var requireObjectCoercible = require('../internals/require-object-coercible');
var toString = require('../internals/to-string');

var charCodeAt = uncurryThis(''.charCodeAt);

// `String.prototype.isWellFormed` method
// https://github.com/tc39/proposal-is-usv-string
$({ target: 'String', proto: true }, {
isWellFormed: function isWellFormed() {
var S = toString(requireObjectCoercible(this));
var length = S.length;
for (var i = 0; i < length; i++) {
var charCode = charCodeAt(S, i);
// single UTF-16 code unit
if ((charCode & 0xF800) != 0xD800) continue;
// unpaired surrogate
if (charCode >= 0xDC00 || ++i >= length || (charCodeAt(S, i) & 0xFC00) != 0xDC00) return false;
} return true;
}
});
// TODO: Remove from `core-js@4`
require('../modules/es.string.is-well-formed');
Loading

0 comments on commit 15c787f

Please sign in to comment.