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

Add relative indexing method proposal #889

Merged
merged 8 commits into from Nov 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,8 @@
## Changelog
##### Unreleased
- Added [relative indexing method stage 3 proposal](https://github.com/tc39/proposal-relative-indexing-method)
- `Array#at`
- `%TypedArray%#at`
- Added [`Number.range` stage 1 proposal](https://github.com/tc39/proposal-Number.range)
- `Number.range`
- `BigInt.range`
Expand All @@ -8,6 +11,7 @@
- `%TypedArray%#filterOut`
- Added [array deduplication stage 1 proposal](https://github.com/tc39/proposal-array-unique)
- `Array#uniqueBy`
- Added code points / code units explicit feature detection in `String#at` for preventing breakage code which use obsolete `String#at` proposal polyfill
- Added the missed `(es|stable)/instance/replace-all` entries
- Updated compat data mapping for Opera - from Opera 69, the difference with Chrome versions increased to 14
- Compat data mapping for modern Android WebView to Chrome moved from targets parser directly to compat data
Expand Down
35 changes: 34 additions & 1 deletion README.md
Expand Up @@ -1778,7 +1778,40 @@ Promise.any([
core-js(-pure)/stage/3
```

None.
##### [Relative indexing method](https://github.com/tc39/proposal-relative-indexing-method)[⬆](#index)
Modules [`esnext.array.at`](https://github.com/zloirock/core-js/blob/v3.7.0/packages/core-js/modules/esnext.array.at.js) and [`esnext.typed-array.at`](https://github.com/zloirock/core-js/blob/v3.7.0/packages/core-js/modules/esnext.typed-array.at.js)
> **Warning! Because of the conflict with [another proposal](#stringat), this method is not available on `String.prototype` in this version.**

```js
class Array {
at(index: int): any;
}

class [
Int8Array,
Uint8Array,
Uint8ClampedArray,
Int16Array,
Uint16Array,
Int32Array,
Uint32Array,
Float32Array,
Float64Array,
] {
at(index: int): any;
}
```
[*CommonJS entry points:*](#commonjs-api)
```
core-js/proposals/relative-indexing-method
core-js(-pure)/features/array/at
core-js(-pure)/features/typed-array/at
```
[*Examples*](http://es6.zloirock.ru/#log(%5B1%2C%202%2C%203%5D.at(1))%3B%20%20%2F%2F%20%3D%3E%202%0Alog(%5B1%2C%202%2C%203%5D.at(-1))%3B%20%2F%2F%20%3D%3E%203):
```js
[1, 2, 3].at(1); // => 2
[1, 2, 3].at(-1); // => 3
```

#### Stage 2 proposals[⬆](#index)
[*CommonJS entry points:*](#commonjs-api)
Expand Down
4 changes: 4 additions & 0 deletions packages/core-js-compat/src/data.js
Expand Up @@ -1258,6 +1258,8 @@ const data = {
},
'esnext.array.is-template-object': {
},
'esnext.array.at': {
},
'esnext.array.last-index': {
},
'esnext.array.last-item': {
Expand Down Expand Up @@ -1492,6 +1494,8 @@ const data = {
// TODO: Remove from `core-js@4`
'esnext.symbol.replace-all': {
},
'esnext.typed-array.at': {
},
'esnext.typed-array.filter-out': {
},
'esnext.weak-map.delete-all': {
Expand Down
2 changes: 2 additions & 0 deletions packages/core-js-compat/src/modules-by-versions.js
Expand Up @@ -65,10 +65,12 @@ module.exports = {
'esnext.weak-map.emplace',
],
3.8: [
'esnext.array.at',
'esnext.array.filter-out',
'esnext.array.unique-by',
'esnext.bigint.range',
'esnext.number.range',
'esnext.typed-array.at',
'esnext.typed-array.filter-out',
],
};
@@ -0,0 +1 @@
// empty
4 changes: 4 additions & 0 deletions packages/core-js/features/array/at.js
@@ -0,0 +1,4 @@
require('../../modules/esnext.array.at');
var entryUnbind = require('../../internals/entry-unbind');

module.exports = entryUnbind('Array', 'at');
1 change: 1 addition & 0 deletions packages/core-js/features/array/index.js
@@ -1,5 +1,6 @@
var parent = require('../../es/array');
require('../../modules/es.map');
require('../../modules/esnext.array.at');
require('../../modules/esnext.array.filter-out');
require('../../modules/esnext.array.is-template-object');
require('../../modules/esnext.array.last-item');
Expand Down
4 changes: 4 additions & 0 deletions packages/core-js/features/array/virtual/at.js
@@ -0,0 +1,4 @@
require('../../../modules/esnext.array.at');
var entryVirtual = require('../../../internals/entry-virtual');

module.exports = entryVirtual('Array').at;
2 changes: 2 additions & 0 deletions packages/core-js/features/array/virtual/index.js
@@ -1,4 +1,6 @@
var parent = require('../../../es/array/virtual');
require('../../../modules/esnext.array.at');
require('../../../modules/esnext.array.filter-out');
require('../../../modules/esnext.array.unique-by');

module.exports = parent;
10 changes: 7 additions & 3 deletions packages/core-js/features/instance/at.js
@@ -1,9 +1,13 @@
var at = require('../string/virtual/at');
var arrayAt = require('../array/virtual/at');
var stringAt = require('../string/virtual/at');

var ArrayPrototype = Array.prototype;
var StringPrototype = String.prototype;

module.exports = function (it) {
var own = it.at;
return typeof it === 'string' || it === StringPrototype
|| (it instanceof String && own === StringPrototype.at) ? at : own;
if (it === ArrayPrototype || (it instanceof Array && own === ArrayPrototype.at)) return arrayAt;
if (typeof it === 'string' || it === StringPrototype || (it instanceof String && own === StringPrototype.at)) {
return stringAt;
} return own;
};
2 changes: 2 additions & 0 deletions packages/core-js/features/string/index.js
@@ -1,5 +1,7 @@
var parent = require('../../es/string');
require('../../modules/esnext.string.at');
// TODO: disabled by default because of the conflict with another proposal
// require('../../modules/esnext.string.at-alternative');
require('../../modules/esnext.string.code-points');
// TODO: remove from `core-js@4`
require('../../modules/esnext.string.match-all');
Expand Down
2 changes: 2 additions & 0 deletions packages/core-js/features/string/virtual/index.js
@@ -1,5 +1,7 @@
var parent = require('../../../es/string/virtual');
require('../../../modules/esnext.string.at');
// TODO: disabled by default because of the conflict with another proposal
// require('../../../modules/esnext.string.at-alternative');
require('../../../modules/esnext.string.code-points');
// TODO: remove from `core-js@4`
require('../../../modules/esnext.string.match-all');
Expand Down
1 change: 1 addition & 0 deletions packages/core-js/features/typed-array/at.js
@@ -0,0 +1 @@
require('../../modules/esnext.typed-array.at');
1 change: 1 addition & 0 deletions packages/core-js/features/typed-array/index.js
@@ -1,4 +1,5 @@
var parent = require('../../es/typed-array');
require('../../modules/esnext.typed-array.at');
require('../../modules/esnext.typed-array.filter-out');

module.exports = parent;
20 changes: 20 additions & 0 deletions packages/core-js/modules/esnext.array.at.js
@@ -0,0 +1,20 @@
'use strict';
var $ = require('../internals/export');
var toObject = require('../internals/to-object');
var toLength = require('../internals/to-length');
var toInteger = require('../internals/to-integer');
var addToUnscopables = require('../internals/add-to-unscopables');

// `Array.prototype.at` method
// https://github.com/tc39/proposal-relative-indexing-method
$({ target: 'Array', proto: true }, {
at: function at(index) {
var O = toObject(this);
var len = toLength(O.length);
var relativeIndex = toInteger(index);
var k = relativeIndex >= 0 ? relativeIndex : len + relativeIndex;
return (k < 0 || k >= len) ? undefined : O[k];
}
});

addToUnscopables('at');
23 changes: 23 additions & 0 deletions packages/core-js/modules/esnext.string.at-alternative.js
@@ -0,0 +1,23 @@
// TODO: disabled by default because of the conflict with another proposal
'use strict';
var $ = require('../internals/export');
var requireObjectCoercible = require('../internals/require-object-coercible');
var toLength = require('../internals/to-length');
var toInteger = require('../internals/to-integer');
var fails = require('../internals/fails');

var FORCED = fails(function () {
return '𠮷'.at(0) !== '\uD842';
});

// `String.prototype.at` method
// https://github.com/tc39/proposal-relative-indexing-method
$({ target: 'String', proto: true, forced: FORCED }, {
at: function at(index) {
var S = String(requireObjectCoercible(this));
var len = toLength(S.length);
var relativeIndex = toInteger(index);
var k = relativeIndex >= 0 ? relativeIndex : len + relativeIndex;
return (k < 0 || k >= len) ? undefined : S.charAt(k);
}
});
7 changes: 6 additions & 1 deletion packages/core-js/modules/esnext.string.at.js
@@ -1,10 +1,15 @@
'use strict';
var $ = require('../internals/export');
var charAt = require('../internals/string-multibyte').charAt;
var fails = require('../internals/fails');

var FORCED = fails(function () {
return '𠮷'.at(0) !== '𠮷';
});

// `String.prototype.at` method
// https://github.com/mathiasbynens/String.prototype.at
$({ target: 'String', proto: true }, {
$({ target: 'String', proto: true, forced: FORCED }, {
at: function at(pos) {
return charAt(this, pos);
}
Expand Down
17 changes: 17 additions & 0 deletions packages/core-js/modules/esnext.typed-array.at.js
@@ -0,0 +1,17 @@
'use strict';
var ArrayBufferViewCore = require('../internals/array-buffer-view-core');
var toLength = require('../internals/to-length');
var toInteger = require('../internals/to-integer');

var aTypedArray = ArrayBufferViewCore.aTypedArray;
var exportTypedArrayMethod = ArrayBufferViewCore.exportTypedArrayMethod;

// `%TypedArray%.prototype.at` method
// https://github.com/tc39/proposal-relative-indexing-method
exportTypedArrayMethod('at', function at(index) {
var O = aTypedArray(this);
var len = toLength(O.length);
var relativeIndex = toInteger(index);
var k = relativeIndex >= 0 ? relativeIndex : len + relativeIndex;
return (k < 0 || k >= len) ? undefined : O[k];
});
5 changes: 5 additions & 0 deletions packages/core-js/proposals/relative-indexing-method.js
@@ -0,0 +1,5 @@
// https://github.com/tc39/proposal-relative-indexing-method
require('../modules/esnext.array.at');
// TODO: disabled by default because of the conflict with another proposal
// require('../modules/esnext.string.at-alternative');
require('../modules/esnext.typed-array.at');
1 change: 1 addition & 0 deletions packages/core-js/stage/3.js
@@ -1,3 +1,4 @@
require('../proposals/relative-indexing-method');
var parent = require('./4');

module.exports = parent;
10 changes: 9 additions & 1 deletion tests/commonjs.js
Expand Up @@ -62,6 +62,7 @@ for (const _PATH of ['../packages/core-js-pure', '../packages/core-js']) {
ok(typeof load('features/array/is-template-object') === 'function');
ok(Array.isArray(load('features/array/from')('qwe')));
ok(Array.isArray(load('features/array/of')('q', 'w', 'e')));
ok(load('features/array/at')([1, 2, 3], -2) === 2);
ok(load('features/array/join')('qwe', 1) === 'q1w1e');
ok(load('features/array/slice')('qwe', 1)[1] === 'e');
ok(load('features/array/sort')([1, 3, 2])[1] === 2);
Expand Down Expand Up @@ -92,6 +93,7 @@ for (const _PATH of ['../packages/core-js-pure', '../packages/core-js']) {
ok(load('features/array/includes')([1, 2, 3], 2));
ok('next' in load('features/array/iterator')([]));
ok(typeof load('features/array/unique-by') === 'function');
ok(load('features/array/virtual/at').call([1, 2, 3], -2) === 2);
ok(load('features/array/virtual/join').call('qwe', 1) === 'q1w1e');
ok(load('features/array/virtual/slice').call('qwe', 1)[1] === 'e');
ok(load('features/array/virtual/splice').call([1, 2, 3], 1, 2)[0] === 2);
Expand Down Expand Up @@ -210,6 +212,7 @@ for (const _PATH of ['../packages/core-js-pure', '../packages/core-js']) {
ok(load('features/string/code-point-at')('a', 0) === 97);
ok(load('features/string/ends-with')('qwe', 'we'));
ok(load('features/string/includes')('qwe', 'w'));
// ok(load('features/string/at-alternative')('123', -2) === '2');
ok(load('features/string/repeat')('q', 3) === 'qqq');
ok(load('features/string/starts-with')('qwe', 'qw'));
ok(typeof load('features/string/anchor') === 'function');
Expand Down Expand Up @@ -239,6 +242,7 @@ for (const _PATH of ['../packages/core-js-pure', '../packages/core-js']) {
ok(load('features/string/virtual/code-point-at').call('a', 0) === 97);
ok(load('features/string/virtual/ends-with').call('qwe', 'we'));
ok(load('features/string/virtual/includes').call('qwe', 'w'));
// ok(load('features/string/virtual/at-alternative').call('123', -2) === '2');
ok(load('features/string/virtual/repeat').call('q', 3) === 'qqq');
ok(load('features/string/virtual/starts-with').call('qwe', 'qw'));
ok(typeof load('features/string/virtual/anchor') === 'function');
Expand Down Expand Up @@ -977,6 +981,7 @@ for (const _PATH of ['../packages/core-js-pure', '../packages/core-js']) {
load('proposals/promise-any');
load('proposals/promise-try');
load('proposals/reflect-metadata');
load('proposals/relative-indexing-method');
load('proposals/keys-composition');
load('proposals/seeded-random');
load('proposals/set-methods');
Expand All @@ -999,8 +1004,10 @@ for (const _PATH of ['../packages/core-js-pure', '../packages/core-js']) {
const instanceAt = load('features/instance/at');
ok(typeof instanceAt === 'function');
ok(instanceAt({}) === undefined);
ok(typeof instanceAt([]) === 'function');
ok(typeof instanceAt('') === 'function');
ok(instanceAt('').call('abc', 1) === 'b');
ok(instanceAt([]).call([1, 2, 3], 2) === 3);
ok(instanceAt('').call('123', 2) === '3');

let instanceBind = load('features/instance/bind');
ok(typeof instanceBind === 'function');
Expand Down Expand Up @@ -1653,6 +1660,7 @@ ok(typeof load('features/typed-array/int32-array') === 'function');
ok(typeof load('features/typed-array/uint32-array') === 'function');
ok(typeof load('features/typed-array/float32-array') === 'function');
ok(typeof load('features/typed-array/float64-array') === 'function');
load('features/typed-array/at');
load('features/typed-array/copy-within');
load('features/typed-array/entries');
load('features/typed-array/every');
Expand Down
8 changes: 7 additions & 1 deletion tests/compat/tests.js
Expand Up @@ -1100,6 +1100,9 @@ GLOBAL.tests = {
&& set.add({}) == set
&& set[Symbol.toStringTag];
}],
'esnext.array.at': function () {
return [].at;
},
'esnext.array.filter-out': function () {
return [].filterOut;
},
Expand Down Expand Up @@ -1422,7 +1425,7 @@ GLOBAL.tests = {
return Set.prototype.union;
},
'esnext.string.at': function () {
return String.prototype.at;
return '𠮷'.at(0) === '𠮷';
},
'esnext.string.code-points': function () {
return String.prototype.codePoints;
Expand All @@ -1440,6 +1443,9 @@ GLOBAL.tests = {
'esnext.symbol.replace-all': function () {
return Symbol.replaceAll;
},
'esnext.typed-array.at': function () {
return Int8Array.prototype.at;
},
'esnext.typed-array.filter-out': function () {
return Int8Array.prototype.filterOut;
},
Expand Down
27 changes: 27 additions & 0 deletions tests/pure/esnext.array.at.js
@@ -0,0 +1,27 @@
import { STRICT } from '../helpers/constants';

import at from 'core-js-pure/features/array/at';

QUnit.test('Array#at', assert => {
assert.isFunction(at);
assert.same(1, at([1, 2, 3], 0));
assert.same(2, at([1, 2, 3], 1));
assert.same(3, at([1, 2, 3], 2));
assert.same(undefined, at([1, 2, 3], 3));
assert.same(3, at([1, 2, 3], -1));
assert.same(2, at([1, 2, 3], -2));
assert.same(1, at([1, 2, 3], -3));
assert.same(undefined, at([1, 2, 3], -4));
assert.same(1, at([1, 2, 3], 0.4));
assert.same(1, at([1, 2, 3], 0.5));
assert.same(1, at([1, 2, 3], 0.6));
assert.same(1, at([1], NaN));
assert.same(1, at([1]));
assert.same(1, at([1, 2, 3], -0));
assert.same(undefined, at(Array(1), 0));
assert.same(1, at({ 0: 1, length: 1 }, 0));
if (STRICT) {
assert.throws(() => at(null, 0), TypeError);
assert.throws(() => at(undefined, 0), TypeError);
}
});
28 changes: 28 additions & 0 deletions tests/pure/esnext.string.at-alternative.js
@@ -0,0 +1,28 @@
// TODO: disabled by default because of the conflict with another proposal
import { STRICT } from '../helpers/constants';

import at from 'core-js-pure/features/string/at';

QUnit.test('String#at', assert => {
assert.isFunction(at);
assert.same('1', at('123', 0));
assert.same('2', at('123', 1));
assert.same('3', at('123', 2));
assert.same(undefined, at('123', 3));
assert.same('3', at('123', -1));
assert.same('2', at('123', -2));
assert.same('1', at('123', -3));
assert.same(undefined, at('123', -4));
assert.same('1', at('123', 0.4));
assert.same('1', at('123', 0.5));
assert.same('1', at('123', 0.6));
assert.same('1', at('1', NaN));
assert.same('1', at('1'));
assert.same('1', at('123', -0));
assert.same('\uD842', at('𠮷'));
assert.same('1', at({ toString() { return '123'; } }, 0));
if (STRICT) {
assert.throws(() => at(null, 0), TypeError);
assert.throws(() => at(undefined, 0), TypeError);
}
});