Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions docs/web/docs/features/proposals/iterator-join.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Iterator join
[Specification](https://bakkot.github.io/proposal-iterator-join/)\
[Proposal repo](https://github.com/bakkot/proposal-iterator-join)

## Modules
[`esnext.iterator.join`](https://github.com/zloirock/core-js/blob/v4/packages/core-js/modules/esnext.iterator.join.js)

## Built-ins signatures
```ts
class Iterator {
join(separator?: string | undefined): string;
}
```

## [Entry points]({docs-version}/docs/usage#h-entry-points)
```ts
core-js/proposals/iterator-join
core-js(-pure)/full/iterator/join
```

## Examples
```js
const digits = () => [1, 2, 3].values();
digits().join(); // => '1,2,3'

const words = () => ['Hello', 'core-js'].values();
words().join(' '); // => 'Hello core-js'
```
4 changes: 4 additions & 0 deletions docs/web/docs/menu.json
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,10 @@
{
"title": "Function.prototype.demethodize",
"url": "{docs-version}/docs/features/proposals/function-prototype-demethodize"
},
{
"title": "Iterator join",
"url": "{docs-version}/docs/features/proposals/iterator-join"
}
]
}
Expand Down
2 changes: 2 additions & 0 deletions packages/core-js-compat/src/data.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2347,6 +2347,8 @@ export const data = {
},
'esnext.iterator.concat': {
},
'esnext.iterator.join': {
},
'esnext.iterator.range': {
},
'esnext.iterator.to-async': {
Expand Down
2 changes: 2 additions & 0 deletions packages/core-js-pure/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,8 @@
"./proposals/iterator-helpers.js": "./proposals/iterator-helpers.js",
"./proposals/iterator-chunking": "./proposals/iterator-chunking.js",
"./proposals/iterator-chunking.js": "./proposals/iterator-chunking.js",
"./proposals/iterator-join": "./proposals/iterator-join.js",
"./proposals/iterator-join.js": "./proposals/iterator-join.js",
"./proposals/iterator-range": "./proposals/iterator-range.js",
"./proposals/iterator-range.js": "./proposals/iterator-range.js",
"./proposals/iterator-sequencing": "./proposals/iterator-sequencing.js",
Expand Down
33 changes: 33 additions & 0 deletions packages/core-js/modules/esnext.iterator.join.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use strict';
var $ = require('../internals/export');
var $toString = require('../internals/to-string');
var anObject = require('../internals/an-object');
var getIteratorDirect = require('../internals/get-iterator-direct');
var isNullOrUndefined = require('../internals/is-null-or-undefined');
var iterate = require('../internals/iterate');
var iteratorClose = require('../internals/iterator-close');
var uncurryThis = require('../internals/function-uncurry-this');

var $join = uncurryThis([].join);
var push = uncurryThis([].push);

// `Iterator.prototype.join` method
// https://bakkot.github.io/proposal-iterator-join/
// dependency: es.iterator.constructor
$({ target: 'Iterator', proto: true, real: true, forced: true }, {
join: function join(separator) {
var O = anObject(this);
var sep;
try {
sep = separator === undefined ? ',' : $toString(separator);
} catch (error) {
iteratorClose(O, 'throw', error);
}
var result = [];
var iterated = getIteratorDirect(O);
iterate(iterated, function (value) {
push(result, isNullOrUndefined(value) ? '' : $toString(value));
}, { IS_RECORD: true });
return $join(result, sep);
},
});
2 changes: 2 additions & 0 deletions packages/core-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,8 @@
"./proposals/iterator-helpers.js": "./proposals/iterator-helpers.js",
"./proposals/iterator-chunking": "./proposals/iterator-chunking.js",
"./proposals/iterator-chunking.js": "./proposals/iterator-chunking.js",
"./proposals/iterator-join": "./proposals/iterator-join.js",
"./proposals/iterator-join.js": "./proposals/iterator-join.js",
"./proposals/iterator-range": "./proposals/iterator-range.js",
"./proposals/iterator-range.js": "./proposals/iterator-range.js",
"./proposals/iterator-sequencing": "./proposals/iterator-sequencing.js",
Expand Down
19 changes: 19 additions & 0 deletions scripts/build-entries/entries-definitions.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1306,6 +1306,18 @@ export const features = {
namespace: 'Iterator',
name: 'forEach',
},
'iterator/join': {
modules: ['esnext.iterator.join'],
template: $uncurried,
namespace: 'Iterator',
name: 'join',
},
'iterator/prototype/join': {
modules: ['esnext.iterator.join'],
template: $prototype,
namespace: 'Iterator',
name: 'join',
},
'iterator/map': {
modules: ['es.iterator.map'],
template: $uncurried,
Expand Down Expand Up @@ -3457,6 +3469,13 @@ export const proposals = {
'esnext.iterator.windows',
],
},
'iterator-join': {
link: 'https://github.com/bakkot/proposal-iterator-join',
stage: 0,
modules: [
'esnext.iterator.join',
],
},
'iterator-range': {
link: 'https://github.com/tc39/proposal-Number.range',
stage: 2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import "core-js/modules/esnext.function.demethodize";
import "core-js/modules/esnext.function.metadata";
import "core-js/modules/esnext.iterator.chunks";
import "core-js/modules/esnext.iterator.concat";
import "core-js/modules/esnext.iterator.join";
import "core-js/modules/esnext.iterator.range";
import "core-js/modules/esnext.iterator.to-async";
import "core-js/modules/esnext.iterator.windows";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import "core-js/modules/esnext.function.demethodize";
import "core-js/modules/esnext.function.metadata";
import "core-js/modules/esnext.iterator.chunks";
import "core-js/modules/esnext.iterator.concat";
import "core-js/modules/esnext.iterator.join";
import "core-js/modules/esnext.iterator.range";
import "core-js/modules/esnext.iterator.to-async";
import "core-js/modules/esnext.iterator.windows";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ import "core-js/modules/esnext.function.demethodize";
import "core-js/modules/esnext.function.metadata";
import "core-js/modules/esnext.iterator.chunks";
import "core-js/modules/esnext.iterator.concat";
import "core-js/modules/esnext.iterator.join";
import "core-js/modules/esnext.iterator.range";
import "core-js/modules/esnext.iterator.to-async";
import "core-js/modules/esnext.iterator.windows";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ import "core-js/modules/esnext.function.demethodize";
import "core-js/modules/esnext.function.metadata";
import "core-js/modules/esnext.iterator.chunks";
import "core-js/modules/esnext.iterator.concat";
import "core-js/modules/esnext.iterator.join";
import "core-js/modules/esnext.iterator.range";
import "core-js/modules/esnext.iterator.to-async";
import "core-js/modules/esnext.iterator.windows";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import "core-js/modules/esnext.function.demethodize";
import "core-js/modules/esnext.function.metadata";
import "core-js/modules/esnext.iterator.chunks";
import "core-js/modules/esnext.iterator.concat";
import "core-js/modules/esnext.iterator.join";
import "core-js/modules/esnext.iterator.range";
import "core-js/modules/esnext.iterator.to-async";
import "core-js/modules/esnext.iterator.windows";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import "core-js/modules/esnext.function.demethodize";
import "core-js/modules/esnext.function.metadata";
import "core-js/modules/esnext.iterator.chunks";
import "core-js/modules/esnext.iterator.concat";
import "core-js/modules/esnext.iterator.join";
import "core-js/modules/esnext.iterator.range";
import "core-js/modules/esnext.iterator.to-async";
import "core-js/modules/esnext.iterator.windows";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ import "core-js/modules/esnext.function.demethodize";
import "core-js/modules/esnext.function.metadata";
import "core-js/modules/esnext.iterator.chunks";
import "core-js/modules/esnext.iterator.concat";
import "core-js/modules/esnext.iterator.join";
import "core-js/modules/esnext.iterator.range";
import "core-js/modules/esnext.iterator.to-async";
import "core-js/modules/esnext.iterator.windows";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ import "core-js/modules/esnext.function.demethodize";
import "core-js/modules/esnext.function.metadata";
import "core-js/modules/esnext.iterator.chunks";
import "core-js/modules/esnext.iterator.concat";
import "core-js/modules/esnext.iterator.join";
import "core-js/modules/esnext.iterator.range";
import "core-js/modules/esnext.iterator.to-async";
import "core-js/modules/esnext.iterator.windows";
Expand Down
3 changes: 3 additions & 0 deletions tests/compat/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -1837,6 +1837,9 @@ GLOBAL.tests = {
'esnext.iterator.concat': function () {
return Iterator.concat;
},
'esnext.iterator.join': function () {
return Iterator.prototype.join;
},
'esnext.iterator.range': function () {
return Iterator.range;
},
Expand Down
2 changes: 2 additions & 0 deletions tests/entries/unit.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,7 @@ for (PATH of ['@core-js/pure', 'core-js']) {
ok(typeof load(NS, 'iterator/zip') == 'function');
ok(typeof load(NS, 'iterator/zip-keyed') == 'function');
ok(typeof load(NS, 'iterator/chunks') == 'function');
ok(typeof load(NS, 'iterator/join') == 'function');
ok(typeof load(NS, 'iterator/to-async') == 'function');
ok(typeof load(NS, 'iterator/windows') == 'function');
ok(typeof load(NS, 'iterator/prototype/chunks') == 'function');
Expand Down Expand Up @@ -820,6 +821,7 @@ for (PATH of ['@core-js/pure', 'core-js']) {
load('proposals/iterator-range');
load('proposals/iterator-sequencing');
load('proposals/iterator-chunking');
load('proposals/iterator-join');
load('proposals/joint-iteration');
load('proposals/json-parse-with-source');
load('proposals/math-clamp');
Expand Down
1 change: 1 addition & 0 deletions tests/eslint/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -1272,6 +1272,7 @@ const forbidCompletelyNonExistentBuiltIns = {
] }],
'es/no-nonstandard-iterator-prototype-properties': [ERROR, { allow: [
'chunks',
'join',
'sliding',
'toAsync',
'windows',
Expand Down
40 changes: 40 additions & 0 deletions tests/unit-global/esnext.iterator.join.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { createIterator } from '../helpers/helpers.js';

QUnit.test('Iterator#join', assert => {
const { join } = Iterator.prototype;
const symbol = Symbol('a');

assert.isFunction(join);
assert.arity(join, 1);
assert.name(join, 'join');
assert.looksNative(join);
assert.nonEnumerable(Iterator.prototype, 'join');

const observableReturn = {
return() {
this.called = true;
return { done: true, value: undefined };
},
};
const itObservable1 = createIterator([1, 2, 3], observableReturn);

assert.strictEqual(join.call(itObservable1), '1,2,3', 'without separator attribute');
assert.true(itObservable1.called, 'iterator closes');
assert.strictEqual(join.call(createIterator([1, 2, 3]), '-'), '1-2-3', 'with separator attribute');
assert.strictEqual(join.call(createIterator([1, null, 3]), '-'), '1--3', 'skips nullish values');
assert.strictEqual(join.call(createIterator([1, undefined, 3]), '-'), '1--3', 'skips undefined values');
assert.strictEqual(join.call(createIterator([])), '', 'empty iterator');

assert.throws(() => join.call(''), TypeError, 'iterable non-object this');
assert.throws(() => join.call(undefined), TypeError, 'non-iterable-object this #1');
assert.throws(() => join.call(null), TypeError, 'non-iterable-object this #2');
assert.throws(() => join.call(5), TypeError, 'non-iterable-object this #3');

const itObservable2 = createIterator([1, 2, 3], observableReturn);
assert.throws(() => join.call(itObservable2, symbol), TypeError, 'throws on symbol separator');
assert.true(itObservable2.called, 'iterator closes on argument validation error');

const itObservable3 = createIterator([1, symbol, 3], observableReturn);
assert.throws(() => join.call(itObservable3), TypeError, 'throws on non-stringable value');
assert.true(itObservable3.called, 'iterator closes on value conversion error');
});
42 changes: 42 additions & 0 deletions tests/unit-pure/esnext.iterator.join.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { createIterator } from '../helpers/helpers.js';

import Iterator from '@core-js/pure/full/iterator';
import Symbol from '@core-js/pure/es/symbol';

QUnit.test('Iterator#join', assert => {
const { join } = Iterator.prototype;

assert.isFunction(join);
assert.arity(join, 1);
assert.name(join, 'join');
assert.nonEnumerable(Iterator.prototype, 'join');

const observableReturn = {
return() {
this.called = true;
return { done: true, value: undefined };
},
};

const itObservable1 = createIterator([1, 2, 3], observableReturn);
assert.strictEqual(join.call(itObservable1), '1,2,3', 'without separator attribute');
assert.true(itObservable1.called, 'iterator closes');
assert.strictEqual(join.call(createIterator([1, 2, 3]), '-'), '1-2-3', 'with separator attribute');
assert.strictEqual(join.call(createIterator([1, null, 3]), '-'), '1--3', 'skips nullish values');
assert.strictEqual(join.call(createIterator([1, undefined, 3]), '-'), '1--3', 'skips undefined values');
assert.strictEqual(join.call(createIterator([])), '', 'empty iterator');

assert.throws(() => join.call(''), TypeError, 'iterable non-object this');
assert.throws(() => join.call(undefined), TypeError, 'non-iterable-object this #1');
assert.throws(() => join.call(null), TypeError, 'non-iterable-object this #2');
assert.throws(() => join.call(5), TypeError, 'non-iterable-object this #3');

const symbol = Symbol('a');
const itObservable2 = createIterator([1, 2, 3], observableReturn);
assert.throws(() => join.call(itObservable2, symbol), TypeError, 'throws on symbol separator');
assert.true(itObservable2.called, 'iterator closes on argument validation error');

const itObservable3 = createIterator([1, symbol, 3], observableReturn);
assert.throws(() => join.call(itObservable3), TypeError, 'throws on non-stringable value');
assert.true(itObservable3.called, 'iterator closes on value conversion error');
});