diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..62b6a21 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,62 @@ +{ + "root": true, + + "extends": "@ljharb", + + "globals": { + "Iterator": false, + }, + + "rules": { + "array-bracket-newline": 0, + "func-name-matching": 0, + "id-length": 0, + "max-lines-per-function": 0, + "multiline-comment-style": 0, + "new-cap": [2, { + "capIsNewExceptions": [ + "Call", + "CreateIteratorFromClosure", + "CreateIterResultObject", + "GeneratorResume", + "GeneratorResumeAbrupt", + "GeneratorStart", + "GeneratorValidate", + "Get", + "GetIntrinsic", + "GetIterator", + "GetIteratorDirect", + "GetIteratorFlattenable", + "GetMethod", + "IsCallable", + "IteratorClose", + "IteratorComplete", + "IteratorNext", + "IteratorStep", + "IteratorValue", + "NormalCompletion", + "OrdinaryHasInstance", + "OrdinaryObjectCreate", + "PromiseResolve", + "ThrowCompletion", + "ToBoolean", + "ToIntegerOrInfinity", + "ToNumber", + "ToObject", + "Type", + ], + }], + "no-negated-condition": 1, + "object-curly-newline": 0, + "sort-keys": 0, + }, + + "overrides": [ + { + "files": "test/**", + "rules": { + "max-params": 0, + }, + }, + ], +} diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..168ae6a --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: [ljharb] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: npm/iterator-helpers +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/workflows/node-aught.yml b/.github/workflows/node-aught.yml new file mode 100644 index 0000000..f3cddd8 --- /dev/null +++ b/.github/workflows/node-aught.yml @@ -0,0 +1,18 @@ +name: 'Tests: node.js < 10' + +on: [pull_request, push] + +jobs: + tests: + uses: ljharb/actions/.github/workflows/node.yml@main + with: + range: '< 10' + type: minors + command: npm run tests-only + + node: + name: 'node < 10' + needs: [tests] + runs-on: ubuntu-latest + steps: + - run: 'echo tests completed' diff --git a/.github/workflows/node-pretest.yml b/.github/workflows/node-pretest.yml new file mode 100644 index 0000000..765edf7 --- /dev/null +++ b/.github/workflows/node-pretest.yml @@ -0,0 +1,7 @@ +name: 'Tests: pretest/posttest' + +on: [pull_request, push] + +jobs: + tests: + uses: ljharb/actions/.github/workflows/pretest.yml@main diff --git a/.github/workflows/node-tens.yml b/.github/workflows/node-tens.yml new file mode 100644 index 0000000..b49ceb1 --- /dev/null +++ b/.github/workflows/node-tens.yml @@ -0,0 +1,18 @@ +name: 'Tests: node.js >= 10' + +on: [pull_request, push] + +jobs: + tests: + uses: ljharb/actions/.github/workflows/node.yml@main + with: + range: '>= 10' + type: minors + command: npm run tests-only + + node: + name: 'node >= 10' + needs: [tests] + runs-on: ubuntu-latest + steps: + - run: 'echo tests completed' diff --git a/.github/workflows/rebase.yml b/.github/workflows/rebase.yml new file mode 100644 index 0000000..b9e1712 --- /dev/null +++ b/.github/workflows/rebase.yml @@ -0,0 +1,9 @@ +name: Automatic Rebase + +on: [pull_request_target] + +jobs: + _: + uses: ljharb/actions/.github/workflows/rebase.yml@main + secrets: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/require-allow-edits.yml b/.github/workflows/require-allow-edits.yml new file mode 100644 index 0000000..7b842f8 --- /dev/null +++ b/.github/workflows/require-allow-edits.yml @@ -0,0 +1,12 @@ +name: Require “Allow Edits” + +on: [pull_request_target] + +jobs: + _: + name: "Require “Allow Edits”" + + runs-on: ubuntu-latest + + steps: + - uses: ljharb/require-allow-edits@main diff --git a/.gitignore b/.gitignore index 52ab08e..335819f 100644 --- a/.gitignore +++ b/.gitignore @@ -107,3 +107,5 @@ dist npm-shrinkwrap.json package-lock.json yarn.lock + +.npmignore diff --git a/.npmrc b/.npmrc index 43c97e7..eacea13 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,3 @@ package-lock=false +allow-same-version=true +message=v%s diff --git a/.nycrc b/.nycrc new file mode 100644 index 0000000..1826526 --- /dev/null +++ b/.nycrc @@ -0,0 +1,13 @@ +{ + "all": true, + "check-coverage": false, + "reporter": ["text-summary", "text", "html", "json"], + "lines": 86, + "statements": 85.93, + "functions": 82.43, + "branches": 76.06, + "exclude": [ + "coverage", + "test" + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..03a962f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). diff --git a/Iterator.from/auto.js b/Iterator.from/auto.js new file mode 100644 index 0000000..8ebf606 --- /dev/null +++ b/Iterator.from/auto.js @@ -0,0 +1,3 @@ +'use strict'; + +require('./shim')(); diff --git a/Iterator.from/implementation.js b/Iterator.from/implementation.js new file mode 100644 index 0000000..5bfb3ac --- /dev/null +++ b/Iterator.from/implementation.js @@ -0,0 +1,34 @@ +'use strict'; + +var OrdinaryHasInstance = require('es-abstract/2022/OrdinaryHasInstance'); +var OrdinaryObjectCreate = require('es-abstract/2022/OrdinaryObjectCreate'); +var ToObject = require('es-abstract/2022/ToObject'); +var Type = require('es-abstract/2022/Type'); + +var GetIteratorFlattenable = require('../aos/GetIteratorFlattenable'); + +var $Iterator = require('../Iterator/polyfill')(); +var $WrapForValidIteratorPrototype = require('../WrapForValidIteratorPrototype'); + +var SLOT = require('internal-slot'); + +module.exports = function from(O) { + if (Type(O) === 'String') { + // eslint-disable-next-line no-param-reassign + O = ToObject(O); // step 1 + } + + var iteratorRecord = GetIteratorFlattenable(O, 'sync'); // step 2 + + var hasInstance = OrdinaryHasInstance($Iterator, iteratorRecord['[[Iterator]]']); // step 3 + + if (hasInstance) { // step 4 + return iteratorRecord['[[Iterator]]']; // step 4.a + } + + var wrapper = OrdinaryObjectCreate($WrapForValidIteratorPrototype); // , ['[[Iterated]]']); // step 5 + + SLOT.set(wrapper, '[[Iterated]]', iteratorRecord); // step 6 + + return wrapper; // step 7 +}; diff --git a/Iterator.from/index.js b/Iterator.from/index.js new file mode 100644 index 0000000..4c50b07 --- /dev/null +++ b/Iterator.from/index.js @@ -0,0 +1,18 @@ +'use strict'; + +var callBind = require('call-bind'); +var define = require('define-properties'); + +var implementation = require('./implementation'); +var getPolyfill = require('./polyfill'); +var shim = require('./shim'); + +var bound = callBind(getPolyfill(), null); + +define(bound, { + getPolyfill: getPolyfill, + implementation: implementation, + shim: shim +}); + +module.exports = bound; diff --git a/Iterator.from/polyfill.js b/Iterator.from/polyfill.js new file mode 100644 index 0000000..2413acf --- /dev/null +++ b/Iterator.from/polyfill.js @@ -0,0 +1,9 @@ +'use strict'; + +var implementation = require('./implementation'); + +var $Iterator = require('../Iterator'); + +module.exports = function getPolyfill() { + return typeof $Iterator.from === 'function' ? $Iterator.from : implementation; +}; diff --git a/Iterator.from/shim.js b/Iterator.from/shim.js new file mode 100644 index 0000000..6d43cd6 --- /dev/null +++ b/Iterator.from/shim.js @@ -0,0 +1,18 @@ +'use strict'; + +var getPolyfill = require('./polyfill'); +var define = require('define-properties'); + +var getIteratorPolyfill = require('../Iterator/polyfill'); + +module.exports = function shimIteratorFrom() { + var $Iterator = getIteratorPolyfill(); + var polyfill = getPolyfill(); + define( + $Iterator, + { from: polyfill }, + { from: function () { return $Iterator.from !== polyfill; } } + ); + + return polyfill; +}; diff --git a/Iterator.prototype.constructor/auto.js b/Iterator.prototype.constructor/auto.js new file mode 100644 index 0000000..8ebf606 --- /dev/null +++ b/Iterator.prototype.constructor/auto.js @@ -0,0 +1,3 @@ +'use strict'; + +require('./shim')(); diff --git a/Iterator.prototype.constructor/implementation.js b/Iterator.prototype.constructor/implementation.js new file mode 100644 index 0000000..70987b2 --- /dev/null +++ b/Iterator.prototype.constructor/implementation.js @@ -0,0 +1,5 @@ +'use strict'; + +var $Iterator = require('../Iterator/polyfill')(); + +module.exports = $Iterator; diff --git a/Iterator.prototype.constructor/index.js b/Iterator.prototype.constructor/index.js new file mode 100644 index 0000000..a6b0aec --- /dev/null +++ b/Iterator.prototype.constructor/index.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var bind = require('function-bind'); + +var implementation = require('./implementation'); +var getPolyfill = require('./polyfill'); +var shim = require('./shim'); + +var polyfill = bind.call(getPolyfill()); + +define(polyfill, { + getPolyfill: getPolyfill, + implementation: implementation, + shim: shim +}); + +module.exports = polyfill; diff --git a/Iterator.prototype.constructor/polyfill.js b/Iterator.prototype.constructor/polyfill.js new file mode 100644 index 0000000..382fb9b --- /dev/null +++ b/Iterator.prototype.constructor/polyfill.js @@ -0,0 +1,7 @@ +'use strict'; + +var implementation = require('./implementation'); + +module.exports = function getPolyfill() { + return implementation; +}; diff --git a/Iterator.prototype.constructor/shim.js b/Iterator.prototype.constructor/shim.js new file mode 100644 index 0000000..ee35d80 --- /dev/null +++ b/Iterator.prototype.constructor/shim.js @@ -0,0 +1,20 @@ +'use strict'; + +var define = require('define-properties'); +var getPolyfill = require('./polyfill'); + +var $IteratorPrototype = require('iterator.prototype'); + +var $Iterator = require('./implementation'); + +module.exports = function shimIteratorPrototypeCtor() { + var polyfill = getPolyfill(); + + define( + $IteratorPrototype, + { constructor: $Iterator }, + { constructor: function () { return $Iterator.constructor !== polyfill; } } + ); + + return polyfill; +}; diff --git a/Iterator.prototype.drop/auto.js b/Iterator.prototype.drop/auto.js new file mode 100644 index 0000000..8ebf606 --- /dev/null +++ b/Iterator.prototype.drop/auto.js @@ -0,0 +1,3 @@ +'use strict'; + +require('./shim')(); diff --git a/Iterator.prototype.drop/implementation.js b/Iterator.prototype.drop/implementation.js new file mode 100644 index 0000000..1220dc8 --- /dev/null +++ b/Iterator.prototype.drop/implementation.js @@ -0,0 +1,72 @@ +'use strict'; + +var GetIntrinsic = require('get-intrinsic'); + +var $RangeError = GetIntrinsic('%RangeError%'); + +var IteratorClose = require('es-abstract/2022/IteratorClose'); +var IteratorStep = require('es-abstract/2022/IteratorStep'); +var IteratorValue = require('es-abstract/2022/IteratorValue'); +var ToIntegerOrInfinity = require('es-abstract/2022/ToIntegerOrInfinity'); +var ToNumber = require('es-abstract/2022/ToNumber'); + +var GetIteratorDirect = require('../aos/GetIteratorDirect'); +var CreateIteratorFromClosure = require('../aos/CreateIteratorFromClosure'); + +var iterHelperProto = require('../IteratorHelperPrototype'); + +var isNaN = require('es-abstract/helpers/isNaN'); + +var SLOT = require('internal-slot'); + +module.exports = function drop(limit) { + var iterated = GetIteratorDirect(this); // step 1 + + var numLimit = ToNumber(limit); // step 2 + if (isNaN(numLimit)) { + throw new $RangeError('`limit` must be a non-NaN number'); // step 3 + } + + var integerLimit = ToIntegerOrInfinity(numLimit); // step 4 + if (integerLimit < 0) { + throw new $RangeError('`limit` must be a >= 0'); // step 5 + } + + var sentinel = {}; + var remaining = integerLimit; // step 6.a + var closure = function () { // step 6 + var next; + while (remaining > 0) { // step 6.b + if (remaining !== Infinity) { // step 6.b.i + remaining -= 1; // step 6.b.i.1 + } + + next = IteratorStep(iterated['[[Iterator]]']); // step 6.b.ii + if (!next) { + // return void undefined; // step 6.b.iii + return sentinel; + } + } + // while (true) { // step 6.c + next = IteratorStep(iterated['[[Iterator]]']); // step 6.c.i + if (!next) { + // return void undefined; // step 6.c.ii + return sentinel; + } + try { + var value = IteratorValue(next); // step 6.c.iii + return value; // step 6.c.iii + } catch (e) { + // close iterator // step 6.c.icv + IteratorClose( + iterated['[[Iterator]]'], + function () { throw e; } + ); + } + // } + return void undefined; + }; + SLOT.set(closure, '[[Sentinel]]', sentinel); // for the userland implementation + + return CreateIteratorFromClosure(closure, 'Iterator Helper', iterHelperProto); // step 4 +}; diff --git a/Iterator.prototype.drop/index.js b/Iterator.prototype.drop/index.js new file mode 100644 index 0000000..751f5fb --- /dev/null +++ b/Iterator.prototype.drop/index.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var callBind = require('call-bind'); + +var implementation = require('./implementation'); +var getPolyfill = require('./polyfill'); +var shim = require('./shim'); + +var polyfill = callBind(getPolyfill()); + +define(polyfill, { + getPolyfill: getPolyfill, + implementation: implementation, + shim: shim +}); + +module.exports = polyfill; diff --git a/Iterator.prototype.drop/polyfill.js b/Iterator.prototype.drop/polyfill.js new file mode 100644 index 0000000..5f56a9c --- /dev/null +++ b/Iterator.prototype.drop/polyfill.js @@ -0,0 +1,9 @@ +'use strict'; + +var implementation = require('./implementation'); + +module.exports = function getPolyfill() { + return typeof Iterator === 'function' && typeof Iterator.prototype.drop === 'function' + ? Iterator.prototype.drop + : implementation; +}; diff --git a/Iterator.prototype.drop/shim.js b/Iterator.prototype.drop/shim.js new file mode 100644 index 0000000..491d8b7 --- /dev/null +++ b/Iterator.prototype.drop/shim.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var getPolyfill = require('./polyfill'); + +var $IteratorPrototype = require('iterator.prototype'); + +module.exports = function shimIteratorPrototypeDrop() { + var polyfill = getPolyfill(); + + define( + $IteratorPrototype, + { drop: polyfill }, + { drop: function () { return $IteratorPrototype.drop !== polyfill; } } + ); + + return polyfill; +}; diff --git a/Iterator.prototype.every/auto.js b/Iterator.prototype.every/auto.js new file mode 100644 index 0000000..8ebf606 --- /dev/null +++ b/Iterator.prototype.every/auto.js @@ -0,0 +1,3 @@ +'use strict'; + +require('./shim')(); diff --git a/Iterator.prototype.every/implementation.js b/Iterator.prototype.every/implementation.js new file mode 100644 index 0000000..a257d88 --- /dev/null +++ b/Iterator.prototype.every/implementation.js @@ -0,0 +1,51 @@ +'use strict'; + +var GetIntrinsic = require('get-intrinsic'); + +var $TypeError = GetIntrinsic('%TypeError%'); + +var Call = require('es-abstract/2022/Call'); +var IsCallable = require('es-abstract/2022/IsCallable'); +var IteratorClose = require('es-abstract/2022/IteratorClose'); +var IteratorStep = require('es-abstract/2022/IteratorStep'); +var IteratorValue = require('es-abstract/2022/IteratorValue'); +var ToBoolean = require('es-abstract/2022/ToBoolean'); + +var GetIteratorDirect = require('../aos/GetIteratorDirect'); + +module.exports = function every(predicate) { + var iterated = GetIteratorDirect(this); // step 1 + + if (!IsCallable(predicate)) { + throw new $TypeError('`predicate` must be a function'); // step 2 + } + + var counter = 0; // step 3 + + // eslint-disable-next-line no-constant-condition + while (true) { // step 4 + var next = IteratorStep(iterated['[[Iterator]]']); // step 4.a + if (!next) { + return true; // step 4.b + } + var value = IteratorValue(next); // step 4.c + var result; + try { + result = Call(predicate, void undefined, [value, counter]); // step 4.d + } catch (e) { + // close iterator // step 4.e + IteratorClose( + iterated['[[Iterator]]'], + function () { throw e; } + ); + } finally { + counter += 1; // step 4.g + } + if (!ToBoolean(result)) { + return IteratorClose( + iterated['[[Iterator]]'], + function () { return false; } + ); // step 4.f + } + } +}; diff --git a/Iterator.prototype.every/index.js b/Iterator.prototype.every/index.js new file mode 100644 index 0000000..751f5fb --- /dev/null +++ b/Iterator.prototype.every/index.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var callBind = require('call-bind'); + +var implementation = require('./implementation'); +var getPolyfill = require('./polyfill'); +var shim = require('./shim'); + +var polyfill = callBind(getPolyfill()); + +define(polyfill, { + getPolyfill: getPolyfill, + implementation: implementation, + shim: shim +}); + +module.exports = polyfill; diff --git a/Iterator.prototype.every/polyfill.js b/Iterator.prototype.every/polyfill.js new file mode 100644 index 0000000..db66f3e --- /dev/null +++ b/Iterator.prototype.every/polyfill.js @@ -0,0 +1,9 @@ +'use strict'; + +var implementation = require('./implementation'); + +module.exports = function getPolyfill() { + return typeof Iterator === 'function' && typeof Iterator.prototype.every === 'function' + ? Iterator.prototype.every + : implementation; +}; diff --git a/Iterator.prototype.every/shim.js b/Iterator.prototype.every/shim.js new file mode 100644 index 0000000..23a6cc0 --- /dev/null +++ b/Iterator.prototype.every/shim.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var getPolyfill = require('./polyfill'); + +var $IteratorPrototype = require('iterator.prototype'); + +module.exports = function shimIteratorPrototypeEvery() { + var polyfill = getPolyfill(); + + define( + $IteratorPrototype, + { every: polyfill }, + { every: function () { return $IteratorPrototype.every !== polyfill; } } + ); + + return polyfill; +}; diff --git a/Iterator.prototype.filter/auto.js b/Iterator.prototype.filter/auto.js new file mode 100644 index 0000000..8ebf606 --- /dev/null +++ b/Iterator.prototype.filter/auto.js @@ -0,0 +1,3 @@ +'use strict'; + +require('./shim')(); diff --git a/Iterator.prototype.filter/implementation.js b/Iterator.prototype.filter/implementation.js new file mode 100644 index 0000000..73d35c2 --- /dev/null +++ b/Iterator.prototype.filter/implementation.js @@ -0,0 +1,58 @@ +'use strict'; + +var GetIntrinsic = require('get-intrinsic'); + +var $TypeError = GetIntrinsic('%TypeError%'); + +var Call = require('es-abstract/2022/Call'); +var IsCallable = require('es-abstract/2022/IsCallable'); +var IteratorClose = require('es-abstract/2022/IteratorClose'); +var IteratorStep = require('es-abstract/2022/IteratorStep'); +var IteratorValue = require('es-abstract/2022/IteratorValue'); +var ToBoolean = require('es-abstract/2022/ToBoolean'); + +var GetIteratorDirect = require('../aos/GetIteratorDirect'); +var CreateIteratorFromClosure = require('../aos/CreateIteratorFromClosure'); + +var iterHelperProto = require('../IteratorHelperPrototype'); + +var SLOT = require('internal-slot'); + +module.exports = function filter(predicate) { + var iterated = GetIteratorDirect(this); // step 1 + + if (!IsCallable(predicate)) { + throw new $TypeError('`predicate` must be a function'); // step 2 + } + + var sentinel = {}; + var closure = function () { + var counter = 0; // step 3.a + // eslint-disable-next-line no-constant-condition + while (true) { // step 3.b + var next = IteratorStep(iterated['[[Iterator]]']); // step 3.b.i + if (!next) { + // return void undefined; // step 3.b.ii + return sentinel; + } + var value = IteratorValue(next); // step 3.b.iii + var selected; + try { + selected = Call(predicate, void undefined, [value, counter]); // step 3.b.iv + // yield mapped // step 3.b.vi + if (ToBoolean(selected)) { + return value; + } + } catch (e) { + // close iterator // step 3.b.v, 3.b.vii + IteratorClose(iterated['[[Iterator]]'], true); + throw e; + } finally { + counter += 1; // step 3.b.viii + } + } + }; + SLOT.set(closure, '[[Sentinel]]', sentinel); // for the userland implementation + + return CreateIteratorFromClosure(closure, 'Iterator Helper', iterHelperProto); // step 4 +}; diff --git a/Iterator.prototype.filter/index.js b/Iterator.prototype.filter/index.js new file mode 100644 index 0000000..751f5fb --- /dev/null +++ b/Iterator.prototype.filter/index.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var callBind = require('call-bind'); + +var implementation = require('./implementation'); +var getPolyfill = require('./polyfill'); +var shim = require('./shim'); + +var polyfill = callBind(getPolyfill()); + +define(polyfill, { + getPolyfill: getPolyfill, + implementation: implementation, + shim: shim +}); + +module.exports = polyfill; diff --git a/Iterator.prototype.filter/polyfill.js b/Iterator.prototype.filter/polyfill.js new file mode 100644 index 0000000..a5acc9b --- /dev/null +++ b/Iterator.prototype.filter/polyfill.js @@ -0,0 +1,9 @@ +'use strict'; + +var implementation = require('./implementation'); + +module.exports = function getPolyfill() { + return typeof Iterator === 'function' && typeof Iterator.prototype.filter === 'function' + ? Iterator.prototype.filter + : implementation; +}; diff --git a/Iterator.prototype.filter/shim.js b/Iterator.prototype.filter/shim.js new file mode 100644 index 0000000..146eb31 --- /dev/null +++ b/Iterator.prototype.filter/shim.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var getPolyfill = require('./polyfill'); + +var $IteratorPrototype = require('iterator.prototype'); + +module.exports = function shimIteratorPrototypeFilter() { + var polyfill = getPolyfill(); + + define( + $IteratorPrototype, + { filter: polyfill }, + { filter: function () { return $IteratorPrototype.filter !== polyfill; } } + ); + + return polyfill; +}; diff --git a/Iterator.prototype.find/auto.js b/Iterator.prototype.find/auto.js new file mode 100644 index 0000000..8ebf606 --- /dev/null +++ b/Iterator.prototype.find/auto.js @@ -0,0 +1,3 @@ +'use strict'; + +require('./shim')(); diff --git a/Iterator.prototype.find/implementation.js b/Iterator.prototype.find/implementation.js new file mode 100644 index 0000000..50006d5 --- /dev/null +++ b/Iterator.prototype.find/implementation.js @@ -0,0 +1,51 @@ +'use strict'; + +var GetIntrinsic = require('get-intrinsic'); + +var $TypeError = GetIntrinsic('%TypeError%'); + +var Call = require('es-abstract/2022/Call'); +var IsCallable = require('es-abstract/2022/IsCallable'); +var IteratorClose = require('es-abstract/2022/IteratorClose'); +var IteratorStep = require('es-abstract/2022/IteratorStep'); +var IteratorValue = require('es-abstract/2022/IteratorValue'); +var ToBoolean = require('es-abstract/2022/ToBoolean'); + +var GetIteratorDirect = require('../aos/GetIteratorDirect'); + +module.exports = function find(predicate) { + var iterated = GetIteratorDirect(this); // step 1 + + if (!IsCallable(predicate)) { + throw new $TypeError('`predicate` must be a function'); // step 2 + } + + var counter = 0; // step 3 + + // eslint-disable-next-line no-constant-condition + while (true) { // step 4 + var next = IteratorStep(iterated['[[Iterator]]']); // step 4.a + if (!next) { + return void undefined; // step 4.b + } + var value = IteratorValue(next); // step 4.c + var result; + try { + result = Call(predicate, void undefined, [value, counter]); // step 4.d + } catch (e) { + // close iterator // step 4.e + IteratorClose( + iterated['[[Iterator]]'], + function () { throw e; } + ); + } finally { + counter += 1; // step 4.g + } + if (ToBoolean(result)) { + return IteratorClose( + iterated['[[Iterator]]'], + function () { return value; } // eslint-disable-line no-loop-func + ); // step 4.f + } + } +}; diff --git a/Iterator.prototype.find/index.js b/Iterator.prototype.find/index.js new file mode 100644 index 0000000..751f5fb --- /dev/null +++ b/Iterator.prototype.find/index.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var callBind = require('call-bind'); + +var implementation = require('./implementation'); +var getPolyfill = require('./polyfill'); +var shim = require('./shim'); + +var polyfill = callBind(getPolyfill()); + +define(polyfill, { + getPolyfill: getPolyfill, + implementation: implementation, + shim: shim +}); + +module.exports = polyfill; diff --git a/Iterator.prototype.find/polyfill.js b/Iterator.prototype.find/polyfill.js new file mode 100644 index 0000000..ce49155 --- /dev/null +++ b/Iterator.prototype.find/polyfill.js @@ -0,0 +1,9 @@ +'use strict'; + +var implementation = require('./implementation'); + +module.exports = function getPolyfill() { + return typeof Iterator === 'function' && typeof Iterator.prototype.find === 'function' + ? Iterator.prototype.find + : implementation; +}; diff --git a/Iterator.prototype.find/shim.js b/Iterator.prototype.find/shim.js new file mode 100644 index 0000000..2582793 --- /dev/null +++ b/Iterator.prototype.find/shim.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var getPolyfill = require('./polyfill'); + +var $IteratorPrototype = require('iterator.prototype'); + +module.exports = function shimIteratorPrototypeFind() { + var polyfill = getPolyfill(); + + define( + $IteratorPrototype, + { find: polyfill }, + { find: function () { return $IteratorPrototype.find !== polyfill; } } + ); + + return polyfill; +}; diff --git a/Iterator.prototype.flatMap/auto.js b/Iterator.prototype.flatMap/auto.js new file mode 100644 index 0000000..8ebf606 --- /dev/null +++ b/Iterator.prototype.flatMap/auto.js @@ -0,0 +1,3 @@ +'use strict'; + +require('./shim')(); diff --git a/Iterator.prototype.flatMap/implementation.js b/Iterator.prototype.flatMap/implementation.js new file mode 100644 index 0000000..4ac0fe9 --- /dev/null +++ b/Iterator.prototype.flatMap/implementation.js @@ -0,0 +1,85 @@ +'use strict'; + +var GetIntrinsic = require('get-intrinsic'); + +var $TypeError = GetIntrinsic('%TypeError%'); + +var Call = require('es-abstract/2022/Call'); +var IsCallable = require('es-abstract/2022/IsCallable'); +var IteratorClose = require('es-abstract/2022/IteratorClose'); +var IteratorStep = require('es-abstract/2022/IteratorStep'); +var IteratorValue = require('es-abstract/2022/IteratorValue'); + +var GetIteratorDirect = require('../aos/GetIteratorDirect'); +var CreateIteratorFromClosure = require('../aos/CreateIteratorFromClosure'); + +var GetIteratorFlattenable = require('../aos/GetIteratorFlattenable'); +var iterHelperProto = require('../IteratorHelperPrototype'); + +var SLOT = require('internal-slot'); + +module.exports = function flatMap(mapper) { + var iterated = GetIteratorDirect(this); // step 1 + + if (!IsCallable(mapper)) { + throw new $TypeError('`mapper` must be a function'); // step 2 + } + + var sentinel = {}; + var counter = 0; // step 3.a + var closure = function () { + // while (true) { // step 3.b + var next = IteratorStep(iterated['[[Iterator]]']); // step 3.b.i + if (!next) { + // return void undefined; // step 3.b.ii + return sentinel; + } + var value = IteratorValue(next); // step 3.b.iii + var mapped; + var innerIterator; + try { + try { + mapped = Call(mapper, void undefined, [value, counter]); // step 3.b.iv + // yield mapped // step 3.b.vi + innerIterator = GetIteratorFlattenable(mapped, 'sync'); // step 3.b.vi + } catch (e) { + IteratorClose( + iterated['[[Iterator]]'], + function () { throw e; } + ); // steps 3.b.v, 3.b.vii + } + var innerAlive = true; // step 3.b.viii + while (innerAlive) { // step 3.b.ix + try { + var innerNext = IteratorStep(innerIterator['[[Iterator]]']); // step 3.b.ix.1 + } catch (e) { + IteratorClose( + iterated['[[Iterator]]'], + function () { throw e; } + ); // step 3.b.ix.2 + } + if (!innerNext) { + innerAlive = false; // step 3.b.ix.3.a + } else { // step 3.b.ix.4 + var innerValue; + try { + innerValue = IteratorValue(innerNext); // step 3.b.ix.4.a + } catch (e) { + IteratorClose( + iterated['[[Iterator]]'], + function () { throw e; } + ); // step 3.b.ix.4.b + } + return innerValue; // step 3.b.ix.4.c + } + } + } finally { + counter += 1; // step 3.b.x + } + // } + return void undefined; + }; + SLOT.set(closure, '[[Sentinel]]', sentinel); // for the userland implementation + + return CreateIteratorFromClosure(closure, 'Iterator Helper', iterHelperProto); // step 4 +}; diff --git a/Iterator.prototype.flatMap/index.js b/Iterator.prototype.flatMap/index.js new file mode 100644 index 0000000..751f5fb --- /dev/null +++ b/Iterator.prototype.flatMap/index.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var callBind = require('call-bind'); + +var implementation = require('./implementation'); +var getPolyfill = require('./polyfill'); +var shim = require('./shim'); + +var polyfill = callBind(getPolyfill()); + +define(polyfill, { + getPolyfill: getPolyfill, + implementation: implementation, + shim: shim +}); + +module.exports = polyfill; diff --git a/Iterator.prototype.flatMap/polyfill.js b/Iterator.prototype.flatMap/polyfill.js new file mode 100644 index 0000000..7a636db --- /dev/null +++ b/Iterator.prototype.flatMap/polyfill.js @@ -0,0 +1,9 @@ +'use strict'; + +var implementation = require('./implementation'); + +module.exports = function getPolyfill() { + return typeof Iterator === 'function' && typeof Iterator.prototype.flatMap === 'function' + ? Iterator.prototype.flatMap + : implementation; +}; diff --git a/Iterator.prototype.flatMap/shim.js b/Iterator.prototype.flatMap/shim.js new file mode 100644 index 0000000..44017a9 --- /dev/null +++ b/Iterator.prototype.flatMap/shim.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var getPolyfill = require('./polyfill'); + +var $IteratorPrototype = require('iterator.prototype'); + +module.exports = function shimIteratorPrototypeFlatMap() { + var polyfill = getPolyfill(); + + define( + $IteratorPrototype, + { flatMap: polyfill }, + { flatMap: function () { return $IteratorPrototype.flatMap !== polyfill; } } + ); + + return polyfill; +}; diff --git a/Iterator.prototype.forEach/auto.js b/Iterator.prototype.forEach/auto.js new file mode 100644 index 0000000..8ebf606 --- /dev/null +++ b/Iterator.prototype.forEach/auto.js @@ -0,0 +1,3 @@ +'use strict'; + +require('./shim')(); diff --git a/Iterator.prototype.forEach/implementation.js b/Iterator.prototype.forEach/implementation.js new file mode 100644 index 0000000..bc36151 --- /dev/null +++ b/Iterator.prototype.forEach/implementation.js @@ -0,0 +1,43 @@ +'use strict'; + +var GetIntrinsic = require('get-intrinsic'); + +var $TypeError = GetIntrinsic('%TypeError%'); + +var Call = require('es-abstract/2022/Call'); +var IsCallable = require('es-abstract/2022/IsCallable'); +var IteratorClose = require('es-abstract/2022/IteratorClose'); +var IteratorStep = require('es-abstract/2022/IteratorStep'); +var IteratorValue = require('es-abstract/2022/IteratorValue'); + +var GetIteratorDirect = require('../aos/GetIteratorDirect'); + +module.exports = function forEach(fn) { + var iterated = GetIteratorDirect(this); // step 1 + + if (!IsCallable(fn)) { + throw new $TypeError('`fn` must be a function'); // step 2 + } + + var counter = 0; // step 3 + + // eslint-disable-next-line no-constant-condition + while (true) { // step 4 + var next = IteratorStep(iterated['[[Iterator]]']); // step 4.a + if (!next) { + return void undefined; // step 4.b + } + var value = IteratorValue(next); // step 4.c + try { + Call(fn, void undefined, [value, counter]); // step 4.d + } catch (e) { + IteratorClose( + iterated['[[Iterator]]'], + function () { throw e; } + ); // steps 4.e + throw e; + } finally { + counter += 1; // step 4.f + } + } +}; diff --git a/Iterator.prototype.forEach/index.js b/Iterator.prototype.forEach/index.js new file mode 100644 index 0000000..751f5fb --- /dev/null +++ b/Iterator.prototype.forEach/index.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var callBind = require('call-bind'); + +var implementation = require('./implementation'); +var getPolyfill = require('./polyfill'); +var shim = require('./shim'); + +var polyfill = callBind(getPolyfill()); + +define(polyfill, { + getPolyfill: getPolyfill, + implementation: implementation, + shim: shim +}); + +module.exports = polyfill; diff --git a/Iterator.prototype.forEach/polyfill.js b/Iterator.prototype.forEach/polyfill.js new file mode 100644 index 0000000..ad11ec7 --- /dev/null +++ b/Iterator.prototype.forEach/polyfill.js @@ -0,0 +1,9 @@ +'use strict'; + +var implementation = require('./implementation'); + +module.exports = function getPolyfill() { + return typeof Iterator === 'function' && typeof Iterator.prototype.forEach === 'function' + ? Iterator.prototype.forEach + : implementation; +}; diff --git a/Iterator.prototype.forEach/shim.js b/Iterator.prototype.forEach/shim.js new file mode 100644 index 0000000..6078502 --- /dev/null +++ b/Iterator.prototype.forEach/shim.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var getPolyfill = require('./polyfill'); + +var $IteratorPrototype = require('iterator.prototype'); + +module.exports = function shimIteratorPrototypeForEach() { + var polyfill = getPolyfill(); + + define( + $IteratorPrototype, + { forEach: polyfill }, + { forEach: function () { return $IteratorPrototype.forEach !== polyfill; } } + ); + + return polyfill; +}; diff --git a/Iterator.prototype.map/auto.js b/Iterator.prototype.map/auto.js new file mode 100644 index 0000000..8ebf606 --- /dev/null +++ b/Iterator.prototype.map/auto.js @@ -0,0 +1,3 @@ +'use strict'; + +require('./shim')(); diff --git a/Iterator.prototype.map/implementation.js b/Iterator.prototype.map/implementation.js new file mode 100644 index 0000000..acf53f0 --- /dev/null +++ b/Iterator.prototype.map/implementation.js @@ -0,0 +1,57 @@ +'use strict'; + +var GetIntrinsic = require('get-intrinsic'); + +var $TypeError = GetIntrinsic('%TypeError%'); + +var Call = require('es-abstract/2022/Call'); +var IsCallable = require('es-abstract/2022/IsCallable'); +var IteratorClose = require('es-abstract/2022/IteratorClose'); +var IteratorStep = require('es-abstract/2022/IteratorStep'); +var IteratorValue = require('es-abstract/2022/IteratorValue'); + +var GetIteratorDirect = require('../aos/GetIteratorDirect'); +var CreateIteratorFromClosure = require('../aos/CreateIteratorFromClosure'); + +var iterHelperProto = require('../IteratorHelperPrototype'); + +var SLOT = require('internal-slot'); + +module.exports = function map(mapper) { + var iterated = GetIteratorDirect(this); // step 1 + + if (!IsCallable(mapper)) { + throw new $TypeError('`mapper` must be a function'); // step 2 + } + + var sentinel = {}; + var closure = function () { + var counter = 0; // step 3.a + // while (true) { // step 3.b + var next = IteratorStep(iterated['[[Iterator]]']); // step 3.b.i + if (!next) { + // return void undefined; // step 3.b.ii + return sentinel; + } + var value = IteratorValue(next); // step 3.b.iii + var mapped; + try { + mapped = Call(mapper, void undefined, [value, counter]); // step 3.b.iv + // yield mapped // step 3.b.vi + return mapped; + } catch (e) { + // close iterator // step 3.b.v, 3.b.vii + IteratorClose( + iterated['[[Iterator]]'], + function () { throw e; } + ); + throw e; + } finally { + counter += 1; // step 3.b.viii + } + // } + }; + SLOT.set(closure, '[[Sentinel]]', sentinel); // for the userland implementation + + return CreateIteratorFromClosure(closure, 'Iterator Helper', iterHelperProto); // step 4 +}; diff --git a/Iterator.prototype.map/index.js b/Iterator.prototype.map/index.js new file mode 100644 index 0000000..751f5fb --- /dev/null +++ b/Iterator.prototype.map/index.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var callBind = require('call-bind'); + +var implementation = require('./implementation'); +var getPolyfill = require('./polyfill'); +var shim = require('./shim'); + +var polyfill = callBind(getPolyfill()); + +define(polyfill, { + getPolyfill: getPolyfill, + implementation: implementation, + shim: shim +}); + +module.exports = polyfill; diff --git a/Iterator.prototype.map/polyfill.js b/Iterator.prototype.map/polyfill.js new file mode 100644 index 0000000..0ee18c6 --- /dev/null +++ b/Iterator.prototype.map/polyfill.js @@ -0,0 +1,9 @@ +'use strict'; + +var implementation = require('./implementation'); + +module.exports = function getPolyfill() { + return typeof Iterator === 'function' && typeof Iterator.prototype.map === 'function' + ? Iterator.prototype.map + : implementation; +}; diff --git a/Iterator.prototype.map/shim.js b/Iterator.prototype.map/shim.js new file mode 100644 index 0000000..ad9b0c0 --- /dev/null +++ b/Iterator.prototype.map/shim.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var getPolyfill = require('./polyfill'); + +var $IteratorPrototype = require('iterator.prototype'); + +module.exports = function shimIteratorPrototypeMap() { + var polyfill = getPolyfill(); + + define( + $IteratorPrototype, + { map: polyfill }, + { map: function () { return $IteratorPrototype.map !== polyfill; } } + ); + + return polyfill; +}; diff --git a/Iterator.prototype.reduce/auto.js b/Iterator.prototype.reduce/auto.js new file mode 100644 index 0000000..8ebf606 --- /dev/null +++ b/Iterator.prototype.reduce/auto.js @@ -0,0 +1,3 @@ +'use strict'; + +require('./shim')(); diff --git a/Iterator.prototype.reduce/implementation.js b/Iterator.prototype.reduce/implementation.js new file mode 100644 index 0000000..4089a9d --- /dev/null +++ b/Iterator.prototype.reduce/implementation.js @@ -0,0 +1,56 @@ +'use strict'; + +var GetIntrinsic = require('get-intrinsic'); + +var $TypeError = GetIntrinsic('%TypeError%'); + +var Call = require('es-abstract/2022/Call'); +var IsCallable = require('es-abstract/2022/IsCallable'); +var IteratorClose = require('es-abstract/2022/IteratorClose'); +var IteratorStep = require('es-abstract/2022/IteratorStep'); +var IteratorValue = require('es-abstract/2022/IteratorValue'); + +var GetIteratorDirect = require('../aos/GetIteratorDirect'); + +module.exports = function reduce(reducer) { + var iterated = GetIteratorDirect(this); // step 1 + + if (!IsCallable(reducer)) { + throw new $TypeError('`reducer` must be a function'); // step 2 + } + + var accumulator; + var counter; + var next; + if (arguments.length < 2) { // step 3 + next = IteratorStep(iterated['[[Iterator]]']); // step 3.a + if (!next) { + throw new $TypeError('Reduce of empty iterator with no initial value'); // step 3.b + } + accumulator = IteratorValue(next); // step 3.c + counter = 1; + } else { // step 4 + accumulator = arguments[1]; // step 4.a + counter = 0; + } + + // eslint-disable-next-line no-constant-condition + while (true) { // step 5 + next = IteratorStep(iterated['[[Iterator]]']); // step 5.a + if (!next) { + return accumulator; // step 5.b + } + var value = IteratorValue(next); // step 5.c + try { + var result = Call(reducer, void undefined, [accumulator, value, counter]); // step 5.d + accumulator = result; // step 5.f + } catch (e) { + // close iterator // step 5.e + IteratorClose( + iterated['[[Iterator]]'], + function () { throw e; } + ); + } + counter += 1; // step 5.g + } +}; diff --git a/Iterator.prototype.reduce/index.js b/Iterator.prototype.reduce/index.js new file mode 100644 index 0000000..751f5fb --- /dev/null +++ b/Iterator.prototype.reduce/index.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var callBind = require('call-bind'); + +var implementation = require('./implementation'); +var getPolyfill = require('./polyfill'); +var shim = require('./shim'); + +var polyfill = callBind(getPolyfill()); + +define(polyfill, { + getPolyfill: getPolyfill, + implementation: implementation, + shim: shim +}); + +module.exports = polyfill; diff --git a/Iterator.prototype.reduce/polyfill.js b/Iterator.prototype.reduce/polyfill.js new file mode 100644 index 0000000..9597e1a --- /dev/null +++ b/Iterator.prototype.reduce/polyfill.js @@ -0,0 +1,9 @@ +'use strict'; + +var implementation = require('./implementation'); + +module.exports = function getPolyfill() { + return typeof Iterator === 'function' && typeof Iterator.prototype.reduce === 'function' + ? Iterator.prototype.reduce + : implementation; +}; diff --git a/Iterator.prototype.reduce/shim.js b/Iterator.prototype.reduce/shim.js new file mode 100644 index 0000000..fda3776 --- /dev/null +++ b/Iterator.prototype.reduce/shim.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var getPolyfill = require('./polyfill'); + +var $IteratorPrototype = require('iterator.prototype'); + +module.exports = function shimIteratorPrototypeReduce() { + var polyfill = getPolyfill(); + + define( + $IteratorPrototype, + { reduce: polyfill }, + { reduce: function () { return $IteratorPrototype.reduce !== polyfill; } } + ); + + return polyfill; +}; diff --git a/Iterator.prototype.some/auto.js b/Iterator.prototype.some/auto.js new file mode 100644 index 0000000..8ebf606 --- /dev/null +++ b/Iterator.prototype.some/auto.js @@ -0,0 +1,3 @@ +'use strict'; + +require('./shim')(); diff --git a/Iterator.prototype.some/implementation.js b/Iterator.prototype.some/implementation.js new file mode 100644 index 0000000..6a008de --- /dev/null +++ b/Iterator.prototype.some/implementation.js @@ -0,0 +1,51 @@ +'use strict'; + +var GetIntrinsic = require('get-intrinsic'); + +var $TypeError = GetIntrinsic('%TypeError%'); + +var Call = require('es-abstract/2022/Call'); +var IsCallable = require('es-abstract/2022/IsCallable'); +var IteratorClose = require('es-abstract/2022/IteratorClose'); +var IteratorStep = require('es-abstract/2022/IteratorStep'); +var IteratorValue = require('es-abstract/2022/IteratorValue'); +var ToBoolean = require('es-abstract/2022/ToBoolean'); + +var GetIteratorDirect = require('../aos/GetIteratorDirect'); + +module.exports = function some(predicate) { + var iterated = GetIteratorDirect(this); // step 1 + + if (!IsCallable(predicate)) { + throw new $TypeError('`predicate` must be a function'); // step 2 + } + + var counter = 0; // step 3 + + // eslint-disable-next-line no-constant-condition + while (true) { // step 4 + var next = IteratorStep(iterated['[[Iterator]]']); // step 4.a + if (!next) { + return false; // step 4.b + } + var value = IteratorValue(next); // step 4.c + var result; + try { + result = Call(predicate, void undefined, [value, counter]); // step 4.d + } catch (e) { + // close iterator // step 4.e + IteratorClose( + iterated['[[Iterator]]'], + function () { throw e; } + ); + } finally { + counter += 1; // step 4.g + } + if (ToBoolean(result)) { + return IteratorClose( + iterated['[[Iterator]]'], + function () { return true; } + ); // step 4.f + } + } +}; diff --git a/Iterator.prototype.some/index.js b/Iterator.prototype.some/index.js new file mode 100644 index 0000000..751f5fb --- /dev/null +++ b/Iterator.prototype.some/index.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var callBind = require('call-bind'); + +var implementation = require('./implementation'); +var getPolyfill = require('./polyfill'); +var shim = require('./shim'); + +var polyfill = callBind(getPolyfill()); + +define(polyfill, { + getPolyfill: getPolyfill, + implementation: implementation, + shim: shim +}); + +module.exports = polyfill; diff --git a/Iterator.prototype.some/polyfill.js b/Iterator.prototype.some/polyfill.js new file mode 100644 index 0000000..2e4c7af --- /dev/null +++ b/Iterator.prototype.some/polyfill.js @@ -0,0 +1,9 @@ +'use strict'; + +var implementation = require('./implementation'); + +module.exports = function getPolyfill() { + return typeof Iterator === 'function' && typeof Iterator.prototype.some === 'function' + ? Iterator.prototype.some + : implementation; +}; diff --git a/Iterator.prototype.some/shim.js b/Iterator.prototype.some/shim.js new file mode 100644 index 0000000..61f41d6 --- /dev/null +++ b/Iterator.prototype.some/shim.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var getPolyfill = require('./polyfill'); + +var $IteratorPrototype = require('iterator.prototype'); + +module.exports = function shimIteratorPrototypeSome() { + var polyfill = getPolyfill(); + + define( + $IteratorPrototype, + { some: polyfill }, + { some: function () { return $IteratorPrototype.some !== polyfill; } } + ); + + return polyfill; +}; diff --git a/Iterator.prototype.take/auto.js b/Iterator.prototype.take/auto.js new file mode 100644 index 0000000..8ebf606 --- /dev/null +++ b/Iterator.prototype.take/auto.js @@ -0,0 +1,3 @@ +'use strict'; + +require('./shim')(); diff --git a/Iterator.prototype.take/implementation.js b/Iterator.prototype.take/implementation.js new file mode 100644 index 0000000..dff6444 --- /dev/null +++ b/Iterator.prototype.take/implementation.js @@ -0,0 +1,62 @@ +'use strict'; + +var GetIntrinsic = require('get-intrinsic'); + +var $RangeError = GetIntrinsic('%RangeError%'); + +var IteratorClose = require('es-abstract/2022/IteratorClose'); +var IteratorStep = require('es-abstract/2022/IteratorStep'); +var IteratorValue = require('es-abstract/2022/IteratorValue'); +var ToIntegerOrInfinity = require('es-abstract/2022/ToIntegerOrInfinity'); +var ToNumber = require('es-abstract/2022/ToNumber'); + +var GetIteratorDirect = require('../aos/GetIteratorDirect'); +var CreateIteratorFromClosure = require('../aos/CreateIteratorFromClosure'); + +var iterHelperProto = require('../IteratorHelperPrototype'); + +var isNaN = require('es-abstract/helpers/isNaN'); + +var SLOT = require('internal-slot'); + +module.exports = function take(limit) { + var iterated = GetIteratorDirect(this); // step 1 + + var numLimit = ToNumber(limit); // step 2 + if (isNaN(numLimit)) { + throw new $RangeError('`limit` must be a non-NaN number'); // step 3 + } + + var integerLimit = ToIntegerOrInfinity(numLimit); // step 4 + if (integerLimit < 0) { + throw new $RangeError('`limit` must be a >= 0'); // step 5 + } + + var sentinel = {}; + var remaining = integerLimit; // step 6.a + var closure = function () { // step 6 + // while (true) { // step 6.b + if (remaining === 0) { // step 6.b.i + return IteratorClose( // step 6.b.i.1 + iterated, + function () { return sentinel; } + ); + } + if (remaining !== Infinity) { // step 6.b.ii + remaining -= 1; // step 6.b.ii.1 + } + + var next = IteratorStep(iterated['[[Iterator]]']); // step 6.b.iii + if (!next) { + // return void undefined; // step 6.b.iv + return sentinel; + } + + var value = IteratorValue(next); // step 3.b.iii + return value; // step 3.b.iv + // } + }; + SLOT.set(closure, '[[Sentinel]]', sentinel); // for the userland implementation + + return CreateIteratorFromClosure(closure, 'Iterator Helper', iterHelperProto); // step 4 +}; diff --git a/Iterator.prototype.take/index.js b/Iterator.prototype.take/index.js new file mode 100644 index 0000000..751f5fb --- /dev/null +++ b/Iterator.prototype.take/index.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var callBind = require('call-bind'); + +var implementation = require('./implementation'); +var getPolyfill = require('./polyfill'); +var shim = require('./shim'); + +var polyfill = callBind(getPolyfill()); + +define(polyfill, { + getPolyfill: getPolyfill, + implementation: implementation, + shim: shim +}); + +module.exports = polyfill; diff --git a/Iterator.prototype.take/polyfill.js b/Iterator.prototype.take/polyfill.js new file mode 100644 index 0000000..3d37a44 --- /dev/null +++ b/Iterator.prototype.take/polyfill.js @@ -0,0 +1,9 @@ +'use strict'; + +var implementation = require('./implementation'); + +module.exports = function getPolyfill() { + return typeof Iterator === 'function' && typeof Iterator.prototype.take === 'function' + ? Iterator.prototype.take + : implementation; +}; diff --git a/Iterator.prototype.take/shim.js b/Iterator.prototype.take/shim.js new file mode 100644 index 0000000..50cf667 --- /dev/null +++ b/Iterator.prototype.take/shim.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var getPolyfill = require('./polyfill'); + +var $IteratorPrototype = require('iterator.prototype'); + +module.exports = function shimIteratorPrototypeTake() { + var polyfill = getPolyfill(); + + define( + $IteratorPrototype, + { take: polyfill }, + { take: function () { return $IteratorPrototype.take !== polyfill; } } + ); + + return polyfill; +}; diff --git a/Iterator.prototype.toArray/auto.js b/Iterator.prototype.toArray/auto.js new file mode 100644 index 0000000..8ebf606 --- /dev/null +++ b/Iterator.prototype.toArray/auto.js @@ -0,0 +1,3 @@ +'use strict'; + +require('./shim')(); diff --git a/Iterator.prototype.toArray/implementation.js b/Iterator.prototype.toArray/implementation.js new file mode 100644 index 0000000..eb0e26f --- /dev/null +++ b/Iterator.prototype.toArray/implementation.js @@ -0,0 +1,26 @@ +'use strict'; + +var IteratorStep = require('es-abstract/2022/IteratorStep'); +var IteratorValue = require('es-abstract/2022/IteratorValue'); + +var GetIteratorDirect = require('../aos/GetIteratorDirect'); + +var callBound = require('call-bind/callBound'); + +var $push = callBound('Array.prototype.push'); + +module.exports = function toArray() { + var iterated = GetIteratorDirect(this); // step 1 + + var items = []; // step 2 + + // eslint-disable-next-line no-constant-condition + while (true) { // step 3 + var next = IteratorStep(iterated['[[Iterator]]']); // step 3.a + if (!next) { + return items; // step 3.b + } + var value = IteratorValue(next); // step 3.c + $push(items, value); // step 3.d + } +}; diff --git a/Iterator.prototype.toArray/index.js b/Iterator.prototype.toArray/index.js new file mode 100644 index 0000000..751f5fb --- /dev/null +++ b/Iterator.prototype.toArray/index.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var callBind = require('call-bind'); + +var implementation = require('./implementation'); +var getPolyfill = require('./polyfill'); +var shim = require('./shim'); + +var polyfill = callBind(getPolyfill()); + +define(polyfill, { + getPolyfill: getPolyfill, + implementation: implementation, + shim: shim +}); + +module.exports = polyfill; diff --git a/Iterator.prototype.toArray/polyfill.js b/Iterator.prototype.toArray/polyfill.js new file mode 100644 index 0000000..5bb92ba --- /dev/null +++ b/Iterator.prototype.toArray/polyfill.js @@ -0,0 +1,9 @@ +'use strict'; + +var implementation = require('./implementation'); + +module.exports = function getPolyfill() { + return typeof Iterator === 'function' && typeof Iterator.prototype.toArray === 'function' + ? Iterator.prototype.toArray + : implementation; +}; diff --git a/Iterator.prototype.toArray/shim.js b/Iterator.prototype.toArray/shim.js new file mode 100644 index 0000000..a8b5cf1 --- /dev/null +++ b/Iterator.prototype.toArray/shim.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var getPolyfill = require('./polyfill'); + +var $IteratorPrototype = require('iterator.prototype'); + +module.exports = function shimIteratorPrototypeToArray() { + var polyfill = getPolyfill(); + + define( + $IteratorPrototype, + { toArray: polyfill }, + { toArray: function () { return $IteratorPrototype.toArray !== polyfill; } } + ); + + return polyfill; +}; diff --git a/Iterator/auto.js b/Iterator/auto.js new file mode 100644 index 0000000..8ebf606 --- /dev/null +++ b/Iterator/auto.js @@ -0,0 +1,3 @@ +'use strict'; + +require('./shim')(); diff --git a/Iterator/implementation.js b/Iterator/implementation.js new file mode 100644 index 0000000..79decab --- /dev/null +++ b/Iterator/implementation.js @@ -0,0 +1,11 @@ +'use strict'; + +var iterProto = require('iterator.prototype'); + +var $Iterator = typeof Iterator === 'function' ? Iterator : function Iterator() {}; + +if ($Iterator.prototype !== iterProto) { + $Iterator.prototype = iterProto; +} + +module.exports = $Iterator; diff --git a/Iterator/index.js b/Iterator/index.js new file mode 100644 index 0000000..751f5fb --- /dev/null +++ b/Iterator/index.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var callBind = require('call-bind'); + +var implementation = require('./implementation'); +var getPolyfill = require('./polyfill'); +var shim = require('./shim'); + +var polyfill = callBind(getPolyfill()); + +define(polyfill, { + getPolyfill: getPolyfill, + implementation: implementation, + shim: shim +}); + +module.exports = polyfill; diff --git a/Iterator/polyfill.js b/Iterator/polyfill.js new file mode 100644 index 0000000..e89c026 --- /dev/null +++ b/Iterator/polyfill.js @@ -0,0 +1,8 @@ +'use strict'; + +var globalThis = require('globalthis')(); +var implementation = require('./implementation'); + +module.exports = function getPolyfill() { + return typeof globalThis.Iterator === 'function' ? globalThis.Iterator : implementation; +}; diff --git a/Iterator/shim.js b/Iterator/shim.js new file mode 100644 index 0000000..be51a07 --- /dev/null +++ b/Iterator/shim.js @@ -0,0 +1,18 @@ +'use strict'; + +var define = require('define-properties'); +var globalThis = require('globalthis')(); + +var getPolyfill = require('./polyfill'); + +module.exports = function shimIterator() { + var polyfill = getPolyfill(); + + define( + globalThis, + { Iterator: polyfill }, + { Iterator: function () { return Iterator !== polyfill; } } + ); + + return polyfill; +}; diff --git a/IteratorHelperPrototype/index.js b/IteratorHelperPrototype/index.js new file mode 100644 index 0000000..bb18102 --- /dev/null +++ b/IteratorHelperPrototype/index.js @@ -0,0 +1,39 @@ +'use strict'; + +var setToStringTag = require('es-set-tostringtag'); +var hasProto = require('has-proto')(); + +var CompletionRecord = require('es-abstract/2022/CompletionRecord'); +var GeneratorResume = require('../aos/GeneratorResume'); +var GeneratorResumeAbrupt = require('../aos/GeneratorResumeAbrupt'); + +var iterProto = require('iterator.prototype'); + +var implementation; +if (hasProto) { + implementation = { + __proto__: iterProto, + next: function next() { + return GeneratorResume(this, void undefined, 'Iterator Helper'); + }, + 'return': function () { + var C = new CompletionRecord('return', void undefined); // step 1 + return GeneratorResumeAbrupt(this, C, 'Iterator Helper'); + } + }; + setToStringTag(implementation, 'Iterator Helper'); +} else { + var IteratorHelper = function IteratorHelper() {}; + IteratorHelper.prototype = iterProto; + implementation = new IteratorHelper(); + delete implementation.constructor; + implementation.next = function next() { + return GeneratorResume(this, void undefined, 'Iterator Helper'); + }; + implementation['return'] = function () { + var C = function () {}; // step 1 + return GeneratorResumeAbrupt(this, C, 'Iterator Helper'); + }; +} + +module.exports = implementation; diff --git a/README.md b/README.md index 3ebe7ef..f986698 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,87 @@ -# iterator-helpers -An ESnext spec-compliant iterator helpers shim/polyfill/replacement that works as far down as ES3. +# es-iterator-helpers [![Version Badge][npm-version-svg]][package-url] + +[![github actions][actions-image]][actions-url] +[![coverage][codecov-image]][codecov-url] +[![License][license-image]][license-url] +[![Downloads][downloads-image]][downloads-url] + +[![npm badge][npm-badge-png]][package-url] + +An ESnext spec-compliant sync iterator helpers shim/polyfill/replacement that works as far down as ES3. + +This package implements the [es-shim API](https://github.com/es-shims/api) “multi” interface. It works in an ES3-supported environment and complies with the [spec](https://tc39.es/ecma262/#sec-additional-properties-of-the-string.prototype-object). + +Because the `Iterator.prototype` methods depend on a receiver (the `this` value), the main export in each subdirectory takes the string to operate on as the first argument. + +The main export of the package itself is simply an array of the available directory names. It’s sole intended use is for build tooling and testing. + +## Supported things + + - [`Iterator` constructor](https://tc39.es/proposal-iterator-helpers/#sec-iterator-constructor) + - [`Iterator.from`](https://tc39.es/proposal-iterator-helpers/#sec-iterator.from) + - [`Iterator.prototype.constructor`](https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.constructor) + - [`Iterator.prototype.drop`](https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.drop) + - [`Iterator.prototype.every`](https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.every) + - [`Iterator.prototype.filter`](https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.filter) + - [`Iterator.prototype.find`](https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.find) + - [`Iterator.prototype.flatMap`](https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.flatmap) + - [`Iterator.prototype.forEach`](https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.foreach) + - [`Iterator.prototype.map`](https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.map) + - [`Iterator.prototype.reduce`](https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.reduce) + - [`Iterator.prototype.some`](https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.some) + - [`Iterator.prototype.take`](https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.take) + - [`Iterator.prototype.toArray`](https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.toarray) + +## Getting started + +```sh +npm install --save es-iterator-helpers +``` + +## Usage/Examples + +```js +const map = require('es-iterator-helpers/Iterator.prototype.map'); +const toArray = require('es-iterator-helpers/Iterator.prototype.toArray'); +const assert = require('assert'); + +const iterator = [1, 2, 3].values(); + +const mapped = map(iterator, (x) => x + 10); +assert.deepEqual( + mapped.next(), + { + done: false, + value: 11, + } +); +assert.deepEqual( + toArray(mapped), + [12, 13] +); +``` + +```js +require('./auto'); // shim all of the methods + +require('./Iterator.prototype.map/auto'); // shim the “map” method +``` + +## Tests +Simply clone the repo, `npm install`, and run `npm test` + +[package-url]: https://npmjs.org/package/es-iterator-helpers +[npm-version-svg]: https://versionbadg.es/es-shims/iterator-helpers.svg +[deps-svg]: https://david-dm.org/es-shims/iterator-helpers.svg +[deps-url]: https://david-dm.org/es-shims/iterator-helpers +[dev-deps-svg]: https://david-dm.org/es-shims/iterator-helpers/dev-status.svg +[dev-deps-url]: https://david-dm.org/es-shims/iterator-helpers#info=devDependencies +[npm-badge-png]: https://nodei.co/npm/es-iterator-helpers.png?downloads=true&stars=true +[license-image]: https://img.shields.io/npm/l/es-iterator-helpers.svg +[license-url]: LICENSE +[downloads-image]: https://img.shields.io/npm/dm/es-iterator-helpers.svg +[downloads-url]: https://npm-stat.com/charts.html?package=es-iterator-helpers +[codecov-image]: https://codecov.io/gh/es-shims/iterator-helpers/branch/main/graphs/badge.svg +[codecov-url]: https://app.codecov.io/gh/es-shims/iterator-helpers/ +[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/es-shims/iterator-helpers +[actions-url]: https://github.com/es-shims/iterator-helpers/actions diff --git a/WrapForValidIteratorPrototype/index.js b/WrapForValidIteratorPrototype/index.js new file mode 100644 index 0000000..fac3992 --- /dev/null +++ b/WrapForValidIteratorPrototype/index.js @@ -0,0 +1,48 @@ +'use strict'; + +var GetIntrinsic = require('get-intrinsic'); + +var $TypeError = GetIntrinsic('%TypeError%'); + +var Call = require('es-abstract/2022/Call'); +var CreateIterResultObject = require('es-abstract/2022/CreateIterResultObject'); +var GetMethod = require('es-abstract/2022/GetMethod'); +var Type = require('es-abstract/2022/Type'); + +var SLOT = require('internal-slot'); +var iterProto = require('iterator.prototype'); + +// https://tc39.es/proposal-iterator-helpers/#sec-wrapforvaliditeratorprototype-object + +module.exports = /* GetIntrinsic('%WrapForValidIteratorPrototype%', true) || */ { + __proto__: iterProto, + next: function next() { + var O = this; // step 1 + + // RequireInternalSlot(O, [[Iterated]]); // step 2 + SLOT.assert(O, '[[Iterated]]'); + + var iteratorRecord = SLOT.get(O, '[[Iterated]]'); // step 3 + + return Call(iteratorRecord['[[NextMethod]]'], iteratorRecord['[[Iterator]]']); // step 4 + }, + 'return': function () { + var O = this; // step 1 + + // RequireInternalSlot(O, [[Iterated]]); // step 2 + SLOT.assert(O, '[[Iterated]]'); + + var iterator = SLOT.get(O, '[[Iterated]]')['[[Iterator]]']; // step 3 + + if (Type(iterator) !== 'Object') { + throw new $TypeError('iterator must be an Object'); // step 4 + } + + var returnMethod = GetMethod(iterator, 'return'); // step 5 + + if (typeof returnMethod === 'undefined') { // step 6 + return CreateIterResultObject(undefined, true); // step 6.a + } + return Call(returnMethod, iterator); // step 7 + } +}; diff --git a/aos/CreateIteratorFromClosure.js b/aos/CreateIteratorFromClosure.js new file mode 100644 index 0000000..3f0a6eb --- /dev/null +++ b/aos/CreateIteratorFromClosure.js @@ -0,0 +1,27 @@ +'use strict'; + +var GetIntrinsic = require('get-intrinsic'); + +var $TypeError = GetIntrinsic('%TypeError%'); + +var IsCallable = require('es-abstract/2022/IsCallable'); +var OrdinaryObjectCreate = require('es-abstract/2022/OrdinaryObjectCreate'); + +var GeneratorStart = require('./GeneratorStart'); + +var SLOT = require('internal-slot'); + +module.exports = function CreateIteratorFromClosure(closure, brand, proto) { + if (!IsCallable(closure)) { + throw new $TypeError('`closure` must be a function'); + } + var generator = OrdinaryObjectCreate(proto, ['[[GeneratorContext]]', '[[GeneratorBrand]]', '[[GeneratorState]]']); // steps 3, 5 + SLOT.set(generator, '[[GeneratorBrand]]', brand); // step 4 + + SLOT.assert(closure, '[[Sentinel]]'); // our userland slot + SLOT.set(generator, '[[Sentinel]]'); // our userland slot + + GeneratorStart(generator, closure); // step 13 + + return generator; // step 15 +}; diff --git a/aos/GeneratorResume.js b/aos/GeneratorResume.js new file mode 100644 index 0000000..96522b7 --- /dev/null +++ b/aos/GeneratorResume.js @@ -0,0 +1,30 @@ +'use strict'; + +var GetIntrinsic = require('get-intrinsic'); + +var $TypeError = GetIntrinsic('%TypeError%'); + +var CreateIterResultObject = require('es-abstract/2022/CreateIterResultObject'); + +var GeneratorValidate = require('./GeneratorValidate'); + +var SLOT = require('internal-slot'); + +module.exports = function GeneratorResume(generator, value, generatorBrand) { + var state = GeneratorValidate(generator, generatorBrand); // step 1 + if (state === 'completed') { + return CreateIterResultObject(void undefined, true); // step 2 + } + + if (state !== 'suspendedStart' && state !== 'suspendedYield') { + throw new $TypeError('Assertion failed: generator state is unexpected: ' + state); // step 3 + } + + var genContext = SLOT.get(generator, '[[GeneratorContext]]'); + + SLOT.set(generator, '[[GeneratorState]]', 'executing'); // step 7 + + var result = genContext(value); // steps 5-6, 8-10 + + return result; +}; diff --git a/aos/GeneratorResumeAbrupt.js b/aos/GeneratorResumeAbrupt.js new file mode 100644 index 0000000..d703a3a --- /dev/null +++ b/aos/GeneratorResumeAbrupt.js @@ -0,0 +1,44 @@ +'use strict'; + +var GetIntrinsic = require('get-intrinsic'); + +var $TypeError = GetIntrinsic('%TypeError%'); + +var CompletionRecord = require('es-abstract/2022/CompletionRecord'); +var CreateIterResultObject = require('es-abstract/2022/CreateIterResultObject'); + +var GeneratorValidate = require('./GeneratorValidate'); + +var SLOT = require('internal-slot'); + +module.exports = function GeneratorResumeAbrupt(generator, abruptCompletion, generatorBrand) { + if (!(abruptCompletion instanceof CompletionRecord)) { + throw new $TypeError('Assertion failed: abruptCompletion must be a Completion Record'); + } + + var state = GeneratorValidate(generator, generatorBrand); // step 1 + + if (state === 'suspendedStart') { // step 2 + SLOT.set(generator, '[[GeneratorState]]', 'completed'); // step 3.a + SLOT.set(generator, '[[GeneratorContext]]', null); // step 3.b + state = 'completed'; // step 3.c + } + + var value = abruptCompletion.value(); + + if (state === 'completed') { // step 3 + return CreateIterResultObject(value, true); // steps 3.a-b + } + + if (state !== 'suspendedYield') { + throw new $TypeError('Assertion failed: generator state is unexpected: ' + state); // step 4 + } + + var genContext = SLOT.get(generator, '[[GeneratorContext]]'); // step 5 + + SLOT.set(generator, '[[GeneratorState]]', 'executing'); // step 8 + + var result = genContext(value); // steps 6-7, 8-11 + + return result; // step 12 +}; diff --git a/aos/GeneratorStart.js b/aos/GeneratorStart.js new file mode 100644 index 0000000..da4341d --- /dev/null +++ b/aos/GeneratorStart.js @@ -0,0 +1,45 @@ +'use strict'; + +var GetIntrinsic = require('get-intrinsic'); + +var $TypeError = GetIntrinsic('%TypeError%'); + +var CreateIterResultObject = require('es-abstract/2022/CreateIterResultObject'); +var IsCallable = require('es-abstract/2022/IsCallable'); +var Type = require('es-abstract/2022/Type'); + +var SLOT = require('internal-slot'); + +module.exports = function GeneratorStart(generator, closure) { + SLOT.assert(generator, '[[GeneratorState]]'); + SLOT.assert(generator, '[[GeneratorContext]]'); + SLOT.assert(generator, '[[GeneratorBrand]]'); + SLOT.assert(generator, '[[Sentinel]]'); // our userland slot + + if (!IsCallable(closure) || closure.length !== 0) { + throw new $TypeError('`closure` must be a function that takes no arguments'); + } + + var sentinel = SLOT.get(closure, '[[Sentinel]]'); + if (Type(sentinel) !== 'Object') { + throw new $TypeError('`closure.[[Sentinel]]` must be an object'); + } + SLOT.set(generator, '[[GeneratorContext]]', function () { // steps 2-5 + try { + var result = closure(); + if (result === sentinel) { + SLOT.set(generator, '[[GeneratorState]]', 'completed'); + SLOT.set(generator, '[[GeneratorContext]]', null); + return CreateIterResultObject(void undefined, true); + } + SLOT.set(generator, '[[GeneratorState]]', 'suspendedYield'); + return CreateIterResultObject(result, false); + } catch (e) { + SLOT.set(generator, '[[GeneratorState]]', 'completed'); + SLOT.set(generator, '[[GeneratorContext]]', null); + throw e; + } + }); + + SLOT.set(generator, '[[GeneratorState]]', 'suspendedStart'); // step 6 +}; diff --git a/aos/GeneratorValidate.js b/aos/GeneratorValidate.js new file mode 100644 index 0000000..e02b1c3 --- /dev/null +++ b/aos/GeneratorValidate.js @@ -0,0 +1,24 @@ +'use strict'; + +var GetIntrinsic = require('get-intrinsic'); + +var $TypeError = GetIntrinsic('%TypeError%'); + +var SLOT = require('internal-slot'); + +module.exports = function GeneratorValidate(generator, generatorBrand) { + SLOT.assert(generator, '[[GeneratorState]]'); // step 1 + SLOT.assert(generator, '[[GeneratorBrand]]'); // step 2 + + var brand = SLOT.get(generator, '[[GeneratorBrand]]'); + if (brand !== generatorBrand) { + throw new $TypeError('Assertion failed: generator brand is unexpected: ' + brand); + } + SLOT.assert(generator, '[[GeneratorContext]]'); // step 4 + var state = SLOT.get(generator, '[[GeneratorState]]'); // step 5 + if (state === 'executing') { + throw new $TypeError('generator is executing'); + } + + return state; // step 7 +}; diff --git a/aos/GetIteratorDirect.js b/aos/GetIteratorDirect.js new file mode 100644 index 0000000..c010ee4 --- /dev/null +++ b/aos/GetIteratorDirect.js @@ -0,0 +1,25 @@ +'use strict'; + +var GetIntrinsic = require('get-intrinsic'); + +var $TypeError = GetIntrinsic('%TypeError%'); + +var Get = require('es-abstract/2022/Get'); +var IsCallable = require('es-abstract/2022/IsCallable'); +var Type = require('es-abstract/2022/Type'); + +module.exports = function GetIteratorDirect(obj) { + if (Type(obj) !== 'Object') { + throw new $TypeError('`obj` must be an Object'); // step 1 + } + + var nextMethod = Get(obj, 'next'); // step 2 + + if (!IsCallable(nextMethod)) { + throw new $TypeError('`nextMethod` must be a function'); // step 3 + } + + var iteratorRecord = { '[[Iterator]]': obj, '[[NextMethod]]': nextMethod, '[[Done]]': false }; // step 4 + + return iteratorRecord; // step 5 +}; diff --git a/aos/GetIteratorFlattenable.js b/aos/GetIteratorFlattenable.js new file mode 100644 index 0000000..edc1417 --- /dev/null +++ b/aos/GetIteratorFlattenable.js @@ -0,0 +1,62 @@ +'use strict'; + +var GetIntrinsic = require('get-intrinsic'); + +var $TypeError = GetIntrinsic('%TypeError%'); + +var Call = require('es-abstract/2022/Call'); +// var CreateAsyncFromSyncIterator = require('es-abstract/2022/CreateAsyncFromSyncIterator'); +var Get = require('es-abstract/2022/Get'); +var GetIterator = require('es-abstract/2022/GetIterator'); +var IsCallable = require('es-abstract/2022/IsCallable'); +var Type = require('es-abstract/2022/Type'); + +var hasSymbols = require('has-symbols/shams')(); + +module.exports = function GetIteratorFlattenable(obj, hint) { + if (Type(obj) !== 'Object') { + throw new $TypeError('obj must be an Object'); // step 1 + } + + var alreadyAsync = false; // step 2 + + var method = void undefined; // step 3 + + if (hint === 'async') { // step 4 + method = hasSymbols && Symbol.asyncIterator && obj[Symbol.asyncIterator]; // step 4.a + alreadyAsync = true; // step 4.b + } + + if (!IsCallable(method)) { // step 5 + // method = Get(obj, Symbol.iterator); // step 5.a + method = function () { + return GetIterator(obj); + }; + alreadyAsync = false; // step 5.b + } + + var iterator; + if (!IsCallable(method)) { // step 6 + iterator = obj; // step 6.a + alreadyAsync = true; // step 6.b + } else { // step 7 + iterator = Call(method, obj); // step 7.a + } + + if (Type(iterator) !== 'Object') { + throw new $TypeError('iterator must be an Object'); // step 8 + } + var nextMethod = Get(iterator, 'next'); // step 9 + + if (!IsCallable(nextMethod)) { + throw new $TypeError('nextMethod must be a function'); // step 10 + } + + var iteratorRecord = { '[[Iterator]]': iterator, '[[NextMethod]]': nextMethod, '[[Done]]': false }; // step 11 + + if (hint === 'async' && !alreadyAsync) { // step 12 + // return CreateAsyncFromSyncIterator(iteratorRecord); // step 12.a + } + + return iteratorRecord; +}; diff --git a/auto.js b/auto.js new file mode 100644 index 0000000..8ebf606 --- /dev/null +++ b/auto.js @@ -0,0 +1,3 @@ +'use strict'; + +require('./shim')(); diff --git a/index.json b/index.json new file mode 100644 index 0000000..b1b029c --- /dev/null +++ b/index.json @@ -0,0 +1,16 @@ +[ + "Iterator", + "Iterator.from", + "Iterator.prototype.constructor", + "Iterator.prototype.drop", + "Iterator.prototype.every", + "Iterator.prototype.filter", + "Iterator.prototype.find", + "Iterator.prototype.flatMap", + "Iterator.prototype.forEach", + "Iterator.prototype.map", + "Iterator.prototype.reduce", + "Iterator.prototype.some", + "Iterator.prototype.take", + "Iterator.prototype.toArray" +] diff --git a/package.json b/package.json index c24f7aa..606090e 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,97 @@ { - "name": "iterator-helpers", + "name": "es-iterator-helpers", "version": "0.0.0", "description": "An ESnext spec-compliant iterator helpers shim/polyfill/replacement that works as far down as ES3.", - "main": "index.js", + "main": "index.json", "exports": { - ".": "./index.js", + ".": "./index.json", + "./auto": "./auto.js", + "./shim": "./shim.js", + "./Iterator": "./Iterator/index.js", + "./Iterator/auto": "./Iterator/auto.js", + "./Iterator/polyfill": "./Iterator/polyfill.js", + "./Iterator/implementation": "./Iterator/implementation.js", + "./Iterator/shim": "./Iterator/shim.js", + "./Iterator.from": "./Iterator.from/index.js", + "./Iterator.from/auto": "./Iterator.from/auto.js", + "./Iterator.from/polyfill": "./Iterator.from/polyfill.js", + "./Iterator.from/implementation": "./Iterator.from/implementation.js", + "./Iterator.from/shim": "./Iterator.from/shim.js", + "./Iterator.prototype.constructor": "./Iterator.prototype.constructor/index.js", + "./Iterator.prototype.constructor/auto": "./Iterator.prototype.constructor/auto.js", + "./Iterator.prototype.constructor/polyfill": "./Iterator.prototype.constructor/polyfill.js", + "./Iterator.prototype.constructor/implementation": "./Iterator.prototype.constructor/implementation.js", + "./Iterator.prototype.constructor/shim": "./Iterator.prototype.constructor/shim.js", + "./Iterator.prototype.map": "./Iterator.prototype.map/index.js", + "./Iterator.prototype.map/auto": "./Iterator.prototype.map/auto.js", + "./Iterator.prototype.map/polyfill": "./Iterator.prototype.map/polyfill.js", + "./Iterator.prototype.map/implementation": "./Iterator.prototype.map/implementation.js", + "./Iterator.prototype.map/shim": "./Iterator.prototype.map/shim.js", + "./Iterator.prototype.filter": "./Iterator.prototype.filter/index.js", + "./Iterator.prototype.filter/auto": "./Iterator.prototype.filter/auto.js", + "./Iterator.prototype.filter/polyfill": "./Iterator.prototype.filter/polyfill.js", + "./Iterator.prototype.filter/implementation": "./Iterator.prototype.filter/implementation.js", + "./Iterator.prototype.filter/shim": "./Iterator.prototype.filter/shim.js", + "./Iterator.prototype.take": "./Iterator.prototype.take/index.js", + "./Iterator.prototype.take/auto": "./Iterator.prototype.take/auto.js", + "./Iterator.prototype.take/polyfill": "./Iterator.prototype.take/polyfill.js", + "./Iterator.prototype.take/implementation": "./Iterator.prototype.take/implementation.js", + "./Iterator.prototype.take/shim": "./Iterator.prototype.take/shim.js", + "./Iterator.prototype.drop": "./Iterator.prototype.drop/index.js", + "./Iterator.prototype.drop/auto": "./Iterator.prototype.drop/auto.js", + "./Iterator.prototype.drop/polyfill": "./Iterator.prototype.drop/polyfill.js", + "./Iterator.prototype.drop/implementation": "./Iterator.prototype.drop/implementation.js", + "./Iterator.prototype.drop/shim": "./Iterator.prototype.drop/shim.js", + "./Iterator.prototype.flatMap": "./Iterator.prototype.flatMap/index.js", + "./Iterator.prototype.flatMap/auto": "./Iterator.prototype.flatMap/auto.js", + "./Iterator.prototype.flatMap/polyfill": "./Iterator.prototype.flatMap/polyfill.js", + "./Iterator.prototype.flatMap/implementation": "./Iterator.prototype.flatMap/implementation.js", + "./Iterator.prototype.flatMap/shim": "./Iterator.prototype.flatMap/shim.js", + "./Iterator.prototype.reduce": "./Iterator.prototype.reduce/index.js", + "./Iterator.prototype.reduce/auto": "./Iterator.prototype.reduce/auto.js", + "./Iterator.prototype.reduce/polyfill": "./Iterator.prototype.reduce/polyfill.js", + "./Iterator.prototype.reduce/implementation": "./Iterator.prototype.reduce/implementation.js", + "./Iterator.prototype.reduce/shim": "./Iterator.prototype.reduce/shim.js", + "./Iterator.prototype.toArray": "./Iterator.prototype.toArray/index.js", + "./Iterator.prototype.toArray/auto": "./Iterator.prototype.toArray/auto.js", + "./Iterator.prototype.toArray/polyfill": "./Iterator.prototype.toArray/polyfill.js", + "./Iterator.prototype.toArray/implementation": "./Iterator.prototype.toArray/implementation.js", + "./Iterator.prototype.toArray/shim": "./Iterator.prototype.toArray/shim.js", + "./Iterator.prototype.forEach": "./Iterator.prototype.forEach/index.js", + "./Iterator.prototype.forEach/auto": "./Iterator.prototype.forEach/auto.js", + "./Iterator.prototype.forEach/polyfill": "./Iterator.prototype.forEach/polyfill.js", + "./Iterator.prototype.forEach/implementation": "./Iterator.prototype.forEach/implementation.js", + "./Iterator.prototype.forEach/shim": "./Iterator.prototype.forEach/shim.js", + "./Iterator.prototype.some": "./Iterator.prototype.some/index.js", + "./Iterator.prototype.some/auto": "./Iterator.prototype.some/auto.js", + "./Iterator.prototype.some/polyfill": "./Iterator.prototype.some/polyfill.js", + "./Iterator.prototype.some/implementation": "./Iterator.prototype.some/implementation.js", + "./Iterator.prototype.some/shim": "./Iterator.prototype.some/shim.js", + "./Iterator.prototype.every": "./Iterator.prototype.every/index.js", + "./Iterator.prototype.every/auto": "./Iterator.prototype.every/auto.js", + "./Iterator.prototype.every/polyfill": "./Iterator.prototype.every/polyfill.js", + "./Iterator.prototype.every/implementation": "./Iterator.prototype.every/implementation.js", + "./Iterator.prototype.every/shim": "./Iterator.prototype.every/shim.js", + "./Iterator.prototype.find": "./Iterator.prototype.find/index.js", + "./Iterator.prototype.find/auto": "./Iterator.prototype.find/auto.js", + "./Iterator.prototype.find/polyfill": "./Iterator.prototype.find/polyfill.js", + "./Iterator.prototype.find/implementation": "./Iterator.prototype.find/implementation.js", + "./Iterator.prototype.find/shim": "./Iterator.prototype.find/shim.js", "./package.json": "./package.json" }, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "prepack": "npmignore --auto --commentLines=autogenerated", + "prepublishOnly": "safe-publish-latest", + "prepublish": "not-in-publish || npm run prepublishOnly", + "prelint": "es-shim-api --bound --multi --ignore-dirs=WrapForValidIteratorPrototype,IteratorHelperPrototype", + "lint": "eslint --ext=js,mjs .", + "postlint": "evalmd README.md", + "pretest": "npm run lint", + "test": "npm run tests-only", + "tests-only": "nyc tape 'test/**/*.js'", + "posttest": "aud --production", + "version": "auto-changelog && git add CHANGELOG.md", + "postversion": "auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\"" }, "repository": { "type": "git", @@ -22,7 +105,6 @@ "es-shims", "es-shim API", "Iterator", - "AsyncIterator", "helpers", "from", "map", @@ -32,7 +114,6 @@ "flatMap", "reduce", "toArray", - "toAsync", "forEach", "some", "every", @@ -43,5 +124,48 @@ "bugs": { "url": "https://github.com/es-shims/iterator-helpers/issues" }, - "homepage": "https://github.com/es-shims/iterator-helpers#readme" + "homepage": "https://github.com/es-shims/iterator-helpers#readme", + "dependencies": { + "asynciterator.prototype": "^1.0.0", + "es-abstract": "^1.21.1", + "es-set-tostringtag": "^2.0.1", + "function-bind": "^1.1.1", + "globalthis": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.4", + "iterator.prototype": "^1.1.0" + }, + "devDependencies": { + "@es-shims/api": "^2.3.0", + "@ljharb/eslint-config": "^21.0.1", + "aud": "^2.0.2", + "auto-changelog": "^2.4.0", + "es-value-fixtures": "^1.4.2", + "eslint": "=8.8.0", + "evalmd": "^0.0.19", + "for-each": "^0.3.3", + "functions-have-names": "^1.2.3", + "has-strict-mode": "^1.0.1", + "in-publish": "^2.0.1", + "iterate-iterator": "^1.0.2", + "npmignore": "^0.3.0", + "nyc": "^10.3.2", + "object-inspect": "^1.12.3", + "safe-publish-latest": "^2.0.0", + "tape": "^5.6.3" + }, + "auto-changelog": { + "output": "CHANGELOG.md", + "template": "keepachangelog", + "unreleased": false, + "commitLimit": false, + "backfillLimit": false, + "hideCredit": true + }, + "publishConfig": { + "ignore": [ + ".github/workflows" + ] + } } diff --git a/shim.js b/shim.js new file mode 100644 index 0000000..a0a3c27 --- /dev/null +++ b/shim.js @@ -0,0 +1,33 @@ +'use strict'; + +var shimIterator = require('./Iterator/shim'); +var shimIteratorFrom = require('./Iterator.from/shim'); +var shimIteratorCtor = require('./Iterator.prototype.constructor/shim'); +var shimIteratorDrop = require('./Iterator.prototype.drop/shim'); +var shimIteratorEvery = require('./Iterator.prototype.every/shim'); +var shimIteratorFilter = require('./Iterator.prototype.filter/shim'); +var shimIteratorFind = require('./Iterator.prototype.find/shim'); +var shimIteratorFlatMap = require('./Iterator.prototype.flatMap/shim'); +var shimIteratorForEach = require('./Iterator.prototype.forEach/shim'); +var shimIteratorMap = require('./Iterator.prototype.map/shim'); +var shimIteratorReduce = require('./Iterator.prototype.reduce/shim'); +var shimIteratorSome = require('./Iterator.prototype.some/shim'); +var shimIteratorTake = require('./Iterator.prototype.take/shim'); +var shimIteratorToArray = require('./Iterator.prototype.toArray/shim'); + +module.exports = function shimIteratorHelpers() { + shimIterator(); + shimIteratorFrom(); + shimIteratorCtor(); + shimIteratorDrop(); + shimIteratorEvery(); + shimIteratorFilter(); + shimIteratorFind(); + shimIteratorFlatMap(); + shimIteratorForEach(); + shimIteratorMap(); + shimIteratorReduce(); + shimIteratorSome(); + shimIteratorTake(); + shimIteratorToArray(); +}; diff --git a/test/Iterator.from.js b/test/Iterator.from.js new file mode 100644 index 0000000..bb5cc0c --- /dev/null +++ b/test/Iterator.from.js @@ -0,0 +1,124 @@ +'use strict'; + +var defineProperties = require('define-properties'); +var test = require('tape'); +var callBind = require('call-bind'); +var functionsHaveNames = require('functions-have-names')(); +var hasProto = require('has-proto')(); +var forEach = require('for-each'); +var debug = require('object-inspect'); +var v = require('es-value-fixtures'); +var hasSymbols = require('has-symbols/shams')(); + +var index = require('../Iterator.from'); +var impl = require('../Iterator.from/implementation'); + +var isEnumerable = Object.prototype.propertyIsEnumerable; + +var testIterator = require('./helpers/testIterator'); + +var $Iterator = require('../Iterator/implementation'); +var iterProto = require('iterator.prototype'); + +var getCodePoints = function getCodePoints(str) { + var chars = []; + for (var i = 0; i < str.length; i++) { + var c1 = str.charCodeAt(i); + if (c1 >= 0xD800 && c1 < 0xDC00 && i + 1 < str.length) { + var c2 = str.charCodeAt(i + 1); + if (c2 >= 0xDC00 && c2 < 0xE000) { + chars.push(str.charAt(i) + str.charAt(i + 1)); + i += 1; + continue; // eslint-disable-line no-continue, no-restricted-syntax + } + } + chars.push(str.charAt(i)); + } + return chars; +}; + +module.exports = { + tests: function (from, name, t) { + forEach(v.primitives.concat(v.objects), function (nonIterator) { + if (typeof nonIterator !== 'string') { + t['throws']( + function () { from(nonIterator); }, + TypeError, + debug(nonIterator) + ' is not an iterable Object' + ); + } + }); + + t.test('actual iteration', { skip: !hasSymbols }, function (st) { + forEach(v.nonFunctions, function (nonFunction) { + var badIterable = {}; + badIterable[Symbol.iterator] = nonFunction; + st['throws']( + function () { from(badIterable); }, + TypeError, + debug(badIterable) + ' is not a function' + ); + }); + + forEach(v.strings, function (string) { + var stringIt = from(string); + testIterator(stringIt, getCodePoints(string), st, 'string iterator: ' + debug(string)); + }); + + var arrayIt = from([1, 2, 3]); + st.equal(typeof arrayIt.next, 'function', 'has a `next` function'); + + st.test('__proto__ is Iterator.prototype', { skip: !hasProto }, function (s2t) { + var fakeIterator = { + __proto__: iterProto, + next: function () {} + }; + s2t.ok(fakeIterator instanceof $Iterator, 'is an instanceof Iterator'); + s2t.equal(typeof fakeIterator.next, 'function', 'fake iterator `.next` is a function'); + s2t.equal(from(fakeIterator), fakeIterator, 'returns input when it is an instanceof Iterator'); + + s2t.end(); + }); + + st.test('real iterators', { skip: !hasSymbols }, function (s2t) { + var iter = [][Symbol.iterator](); + s2t.equal(from(iter), iter, 'array iterator becomes itself'); + + s2t.end(); + }); + + st.end(); + }); + }, + index: function () { + test('Iterator.from: index', function (t) { + module.exports.tests(index, 'Iterator.from', t); + + t.end(); + }); + }, + implementation: function () { + test('Iterator.from: implementation', function (t) { + module.exports.tests(callBind(impl, null), 'Iterator.from', t); + + t.end(); + }); + }, + shimmed: function () { + test('Iterator.from: shimmed', function (t) { + t.test('Function name', { skip: !functionsHaveNames }, function (st) { + st.equal(Iterator.from.name, 'from', 'Iterator.from has name "from"'); + st.end(); + }); + + t.test('enumerability', { skip: !defineProperties.supportsDescriptors }, function (et) { + et.equal(false, isEnumerable.call(Iterator, 'from'), 'Iterator.from is not enumerable'); + et.end(); + }); + + module.exports.tests(callBind(Iterator.from, Iterator), 'Iterator.from', t); + + t.end(); + }); + } +}; diff --git a/test/Iterator.prototype.constructor.js b/test/Iterator.prototype.constructor.js new file mode 100644 index 0000000..e40db56 --- /dev/null +++ b/test/Iterator.prototype.constructor.js @@ -0,0 +1,47 @@ +'use strict'; + +var defineProperties = require('define-properties'); +var test = require('tape'); + +var Index = require('../Iterator.prototype.constructor'); +var Impl = require('../Iterator.prototype.constructor/implementation'); + +var $Iterator = require('../Iterator/polyfill')(); + +var isEnumerable = Object.prototype.propertyIsEnumerable; + +module.exports = { + tests: function (t, constructor, name) { + t.equal(constructor, $Iterator, name + ' is Iterator'); + }, + index: function () { + test('Iterator.prototype.constructor: index', function (t) { + t.notEqual(Index, $Iterator, 'index is not Iterator itself'); + t.equal(typeof Index, 'function', 'index is a function'); + + t.ok(new Index() instanceof $Iterator, 'new index() instanceof Iterator'); + + t.end(); + }); + }, + implementation: function () { + test('Iterator.prototype.constructor: implementation', function (t) { + t.equal(Impl, $Iterator, 'implementation is Iterator itself'); + module.exports.tests(t, Impl, 'Iterator.prototype.constructor'); + + t.end(); + }); + }, + shimmed: function () { + test('Iterator.prototype.constructor: shimmed', function (t) { + module.exports.tests(t, Iterator.prototype.constructor, 'Iterator.prototype.constructor'); + + t.test('enumerability', { skip: !defineProperties.supportsDescriptors }, function (et) { + et.equal(false, isEnumerable.call(Iterator.prototype, 'constructor'), 'Iterator#constructor is not enumerable'); + et.end(); + }); + + t.end(); + }); + } +}; diff --git a/test/Iterator.prototype.drop.js b/test/Iterator.prototype.drop.js new file mode 100644 index 0000000..9ccab0a --- /dev/null +++ b/test/Iterator.prototype.drop.js @@ -0,0 +1,95 @@ +'use strict'; + +var defineProperties = require('define-properties'); +var test = require('tape'); +var callBind = require('call-bind'); +var functionsHaveNames = require('functions-have-names')(); +var hasStrictMode = require('has-strict-mode')(); +var forEach = require('for-each'); +var debug = require('object-inspect'); +var v = require('es-value-fixtures'); +var hasSymbols = require('has-symbols/shams')(); + +var index = require('../Iterator.prototype.drop'); +var impl = require('../Iterator.prototype.drop/implementation'); + +var fnName = 'drop'; + +var isEnumerable = Object.prototype.propertyIsEnumerable; + +var testIterator = require('./helpers/testIterator'); + +module.exports = { + tests: function (drop, name, t) { + forEach(v.primitives.concat(v.objects), function (nonIterator) { + t['throws']( + function () { drop(nonIterator); }, + TypeError, + debug(nonIterator) + ' is not an Object with a callable `next` method' + ); + + var badNext = { next: nonIterator }; + t['throws']( + function () { drop(badNext); }, + TypeError, + debug(badNext) + ' is not an Object with a callable `next` method' + ); + }); + + var iterator = [1, 2, 3]; + + t.test('actual iteration', { skip: !hasSymbols }, function (st) { + st['throws']( + function () { drop(iterator[Symbol.iterator](), -3); }, + RangeError, + '-3 is not >= 0' + ); + + testIterator(iterator[Symbol.iterator](), [1, 2, 3], st, 'original'); + testIterator(drop(iterator[Symbol.iterator](), 0), [1, 2, 3], st, 'drop 0'); + testIterator(drop(iterator[Symbol.iterator](), 1), [2, 3], st, 'drop 1'); + testIterator(drop(iterator[Symbol.iterator](), 2), [3], st, 'drop 2'); + testIterator(drop(iterator[Symbol.iterator](), 3), [], st, 'drop 3'); + testIterator(drop(iterator[Symbol.iterator](), Infinity), [], st, 'drop ∞'); + + st.end(); + }); + }, + index: function () { + test('Iterator.prototype.' + fnName + ': index', function (t) { + module.exports.tests(index, 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + }, + implementation: function () { + test('Iterator.prototype.' + fnName + ': implementation', function (t) { + module.exports.tests(callBind(impl), 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + }, + shimmed: function () { + test('Iterator.prototype.' + fnName + ': shimmed', function (t) { + t.test('Function name', { skip: !functionsHaveNames }, function (st) { + st.equal(Iterator.prototype[fnName].name, fnName, 'Iterator#' + fnName + ' has name "' + fnName + '"'); + st.end(); + }); + + t.test('enumerability', { skip: !defineProperties.supportsDescriptors }, function (et) { + et.equal(false, isEnumerable.call(Iterator.prototype, fnName), 'Iterator#' + fnName + ' is not enumerable'); + et.end(); + }); + + t.test('bad string/this value', { skip: !hasStrictMode }, function (st) { + st['throws'](function () { return Iterator.prototype[fnName].call(undefined, 'a'); }, TypeError, 'undefined is not an object'); + st['throws'](function () { return Iterator.prototype[fnName].call(null, 'a'); }, TypeError, 'null is not an object'); + st.end(); + }); + + module.exports.tests(callBind(Iterator.prototype[fnName]), 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + } +}; diff --git a/test/Iterator.prototype.every.js b/test/Iterator.prototype.every.js new file mode 100644 index 0000000..f014920 --- /dev/null +++ b/test/Iterator.prototype.every.js @@ -0,0 +1,96 @@ +'use strict'; + +var defineProperties = require('define-properties'); +var test = require('tape'); +var callBind = require('call-bind'); +var functionsHaveNames = require('functions-have-names')(); +var hasStrictMode = require('has-strict-mode')(); +var forEach = require('for-each'); +var debug = require('object-inspect'); +var v = require('es-value-fixtures'); +var hasSymbols = require('has-symbols/shams')(); + +var index = require('../Iterator.prototype.every'); +var impl = require('../Iterator.prototype.every/implementation'); + +var fnName = 'every'; + +var isEnumerable = Object.prototype.propertyIsEnumerable; + +var testIterator = require('./helpers/testIterator'); + +module.exports = { + tests: function (every, name, t) { + forEach(v.primitives.concat(v.objects), function (nonIterator) { + t['throws']( + function () { every(nonIterator); }, + TypeError, + debug(nonIterator) + ' is not an Object with a callable `next` method' + ); + + var badNext = { next: nonIterator }; + t['throws']( + function () { every(badNext); }, + TypeError, + debug(badNext) + ' is not an Object with a callable `next` method' + ); + }); + + forEach(v.nonFunctions, function (nonFunction) { + t['throws']( + function () { every({ next: function () {} }, nonFunction); }, + TypeError, + debug(nonFunction) + ' is not a function' + ); + }); + + t.test('actual iteration', { skip: !hasSymbols }, function (st) { + var arr = [1, 2, 3]; + var iterator = callBind(arr[Symbol.iterator], arr); + + testIterator(iterator(), [1, 2, 3], st, 'original'); + st.equal(every(iterator(), function () { return false; }), false, 'every for always-false'); + st.equal(every(iterator(), function () { return true; }), true, 'every for always-true'); + st.equal(every(iterator(), function (x, i) { return x === 2 && i === 1; }), false, 'every returns false for matching value/index'); + + st.end(); + }); + }, + index: function () { + test('Iterator.prototype.' + fnName + ': index', function (t) { + module.exports.tests(index, 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + }, + implementation: function () { + test('Iterator.prototype.' + fnName + ': implementation', function (t) { + module.exports.tests(callBind(impl), 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + }, + shimmed: function () { + test('Iterator.prototype.' + fnName + ': shimmed', function (t) { + t.test('Function name', { skip: !functionsHaveNames }, function (st) { + st.equal(Iterator.prototype[fnName].name, fnName, 'Iterator#' + fnName + ' has name "' + fnName + '"'); + st.end(); + }); + + t.test('enumerability', { skip: !defineProperties.supportsDescriptors }, function (et) { + et.equal(false, isEnumerable.call(Iterator.prototype, fnName), 'Iterator#' + fnName + ' is not enumerable'); + et.end(); + }); + + t.test('bad string/this value', { skip: !hasStrictMode }, function (st) { + st['throws'](function () { return Iterator.prototype[fnName].call(undefined, 'a'); }, TypeError, 'undefined is not an object'); + st['throws'](function () { return Iterator.prototype[fnName].call(null, 'a'); }, TypeError, 'null is not an object'); + st.end(); + }); + + module.exports.tests(callBind(Iterator.prototype[fnName]), 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + } +}; diff --git a/test/Iterator.prototype.filter.js b/test/Iterator.prototype.filter.js new file mode 100644 index 0000000..1efcc80 --- /dev/null +++ b/test/Iterator.prototype.filter.js @@ -0,0 +1,96 @@ +'use strict'; + +var defineProperties = require('define-properties'); +var test = require('tape'); +var callBind = require('call-bind'); +var functionsHaveNames = require('functions-have-names')(); +var hasStrictMode = require('has-strict-mode')(); +var forEach = require('for-each'); +var debug = require('object-inspect'); +var v = require('es-value-fixtures'); +var hasSymbols = require('has-symbols/shams')(); + +var index = require('../Iterator.prototype.filter'); +var impl = require('../Iterator.prototype.filter/implementation'); + +var fnName = 'filter'; + +var isEnumerable = Object.prototype.propertyIsEnumerable; + +var testIterator = require('./helpers/testIterator'); + +module.exports = { + tests: function (filter, name, t) { + forEach(v.primitives.concat(v.objects), function (nonIterator) { + t['throws']( + function () { filter(nonIterator); }, + TypeError, + debug(nonIterator) + ' is not an Object with a callable `next` method' + ); + + var badNext = { next: nonIterator }; + t['throws']( + function () { filter(badNext); }, + TypeError, + debug(badNext) + ' is not an Object with a callable `next` method' + ); + }); + + forEach(v.nonFunctions, function (nonFunction) { + t['throws']( + function () { filter({ next: function () {} }, nonFunction); }, + TypeError, + debug(nonFunction) + ' is not a function' + ); + }); + + t.test('actual iteration', { skip: !hasSymbols }, function (st) { + var arr = [1, 2, 3]; + var iterator = callBind(arr[Symbol.iterator], arr); + + testIterator(iterator(), [1, 2, 3], st, 'original'); + testIterator(filter(iterator(), function () { return false; }), [], st, 'filter for always-false'); + testIterator(filter(iterator(), function () { return true; }), [1, 2, 3], st, 'filter for always-true'); + testIterator(filter(iterator(), function (x, i) { return x === 2 && i === 1; }), [2], st, 'filter returns value for matching value/index'); + + st.end(); + }); + }, + index: function () { + test('Iterator.prototype.' + fnName + ': index', function (t) { + module.exports.tests(index, 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + }, + implementation: function () { + test('Iterator.prototype.' + fnName + ': implementation', function (t) { + module.exports.tests(callBind(impl), 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + }, + shimmed: function () { + test('Iterator.prototype.' + fnName + ': shimmed', function (t) { + t.test('Function name', { skip: !functionsHaveNames }, function (st) { + st.equal(Iterator.prototype[fnName].name, fnName, 'Iterator#' + fnName + ' has name "' + fnName + '"'); + st.end(); + }); + + t.test('enumerability', { skip: !defineProperties.supportsDescriptors }, function (et) { + et.equal(false, isEnumerable.call(Iterator.prototype, fnName), 'Iterator#' + fnName + ' is not enumerable'); + et.end(); + }); + + t.test('bad string/this value', { skip: !hasStrictMode }, function (st) { + st['throws'](function () { return Iterator.prototype[fnName].call(undefined, 'a'); }, TypeError, 'undefined is not an object'); + st['throws'](function () { return Iterator.prototype[fnName].call(null, 'a'); }, TypeError, 'null is not an object'); + st.end(); + }); + + module.exports.tests(callBind(Iterator.prototype[fnName]), 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + } +}; diff --git a/test/Iterator.prototype.find.js b/test/Iterator.prototype.find.js new file mode 100644 index 0000000..503399a --- /dev/null +++ b/test/Iterator.prototype.find.js @@ -0,0 +1,96 @@ +'use strict'; + +var defineProperties = require('define-properties'); +var test = require('tape'); +var callBind = require('call-bind'); +var functionsHaveNames = require('functions-have-names')(); +var hasStrictMode = require('has-strict-mode')(); +var forEach = require('for-each'); +var debug = require('object-inspect'); +var v = require('es-value-fixtures'); +var hasSymbols = require('has-symbols/shams')(); + +var index = require('../Iterator.prototype.find'); +var impl = require('../Iterator.prototype.find/implementation'); + +var fnName = 'find'; + +var isEnumerable = Object.prototype.propertyIsEnumerable; + +var testIterator = require('./helpers/testIterator'); + +module.exports = { + tests: function (find, name, t) { + forEach(v.primitives.concat(v.objects), function (nonIterator) { + t['throws']( + function () { find(nonIterator); }, + TypeError, + debug(nonIterator) + ' is not an Object with a callable `next` method' + ); + + var badNext = { next: nonIterator }; + t['throws']( + function () { find(badNext); }, + TypeError, + debug(badNext) + ' is not an Object with a callable `next` method' + ); + }); + + forEach(v.nonFunctions, function (nonFunction) { + t['throws']( + function () { find({ next: function () {} }, nonFunction); }, + TypeError, + debug(nonFunction) + ' is not a function' + ); + }); + + t.test('actual iteration', { skip: !hasSymbols }, function (st) { + var arr = [1, 2, 3]; + var iterator = callBind(arr[Symbol.iterator], arr); + + testIterator(iterator(), [1, 2, 3], st, 'original'); + st.equal(find(iterator(), function () { return false; }), undefined, 'find for always-false'); + st.equal(find(iterator(), function () { return true; }), 1, 'find for always-true'); + st.equal(find(iterator(), function (x, i) { return x === 2 && i === 1; }), 2, 'find returns value for matching value/index'); + + st.end(); + }); + }, + index: function () { + test('Iterator.prototype.' + fnName + ': index', function (t) { + module.exports.tests(index, 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + }, + implementation: function () { + test('Iterator.prototype.' + fnName + ': implementation', function (t) { + module.exports.tests(callBind(impl), 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + }, + shimmed: function () { + test('Iterator.prototype.' + fnName + ': shimmed', function (t) { + t.test('Function name', { skip: !functionsHaveNames }, function (st) { + st.equal(Iterator.prototype[fnName].name, fnName, 'Iterator#' + fnName + ' has name "' + fnName + '"'); + st.end(); + }); + + t.test('enumerability', { skip: !defineProperties.supportsDescriptors }, function (et) { + et.equal(false, isEnumerable.call(Iterator.prototype, fnName), 'Iterator#' + fnName + ' is not enumerable'); + et.end(); + }); + + t.test('bad string/this value', { skip: !hasStrictMode }, function (st) { + st['throws'](function () { return Iterator.prototype[fnName].call(undefined, 'a'); }, TypeError, 'undefined is not an object'); + st['throws'](function () { return Iterator.prototype[fnName].call(null, 'a'); }, TypeError, 'null is not an object'); + st.end(); + }); + + module.exports.tests(callBind(Iterator.prototype[fnName]), 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + } +}; diff --git a/test/Iterator.prototype.flatMap.js b/test/Iterator.prototype.flatMap.js new file mode 100644 index 0000000..d52424d --- /dev/null +++ b/test/Iterator.prototype.flatMap.js @@ -0,0 +1,106 @@ +'use strict'; + +var defineProperties = require('define-properties'); +var test = require('tape'); +var callBind = require('call-bind'); +var functionsHaveNames = require('functions-have-names')(); +var hasStrictMode = require('has-strict-mode')(); +var forEach = require('for-each'); +var debug = require('object-inspect'); +var v = require('es-value-fixtures'); +var hasSymbols = require('has-symbols/shams')(); + +var index = require('../Iterator.prototype.flatMap'); +var impl = require('../Iterator.prototype.flatMap/implementation'); + +var fnName = 'flatMap'; + +var isEnumerable = Object.prototype.propertyIsEnumerable; + +var testIterator = require('./helpers/testIterator'); + +module.exports = { + tests: function (flatMap, name, t) { + forEach(v.primitives.concat(v.objects), function (nonIterator) { + t['throws']( + function () { flatMap(nonIterator); }, + TypeError, + debug(nonIterator) + ' is not an Object with a callable `next` method' + ); + + var badNext = { next: nonIterator }; + t['throws']( + function () { flatMap(badNext); }, + TypeError, + debug(badNext) + ' is not an Object with a callable `next` method' + ); + }); + + forEach(v.nonFunctions, function (nonFunction) { + t['throws']( + function () { flatMap({ next: function () {} }, nonFunction); }, + TypeError, + debug(nonFunction) + ' is not a function' + ); + }); + + t.test('actual iteration', { skip: !hasSymbols }, function (st) { + var arr = [1, 2, 3]; + var iterator = callBind(arr[Symbol.iterator], arr); + + testIterator(iterator(), [1, 2, 3], st, 'original'); + + var nonIterableFlatMap = flatMap(iterator(), function (x) { return x; }); + st['throws']( + function () { nonIterableFlatMap.next(); }, + TypeError, + 'non-iterable return value throws' + ); + + testIterator(flatMap(iterator(), function (x) { return [x][Symbol.iterator](); }), [1, 2, 3], st, 'identity mapper in array iterator'); + testIterator(flatMap(iterator(), function (x) { return [2 * x][Symbol.iterator](); }), [2, 4, 6], st, 'doubler mapper in array iterator'); + + testIterator(flatMap(iterator(), function (x) { return [[x]][Symbol.iterator](); }), [[1], [2], [3]], st, 'identity mapper in nested array iterator'); + testIterator(flatMap(iterator(), function (x) { return [[2 * x]][Symbol.iterator](); }), [[2], [4], [6]], st, 'doubler mapper in nested array iterator'); + + st.end(); + }); + }, + index: function () { + test('Iterator.prototype.' + fnName + ': index', function (t) { + module.exports.tests(index, 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + }, + implementation: function () { + test('Iterator.prototype.' + fnName + ': implementation', function (t) { + module.exports.tests(callBind(impl), 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + }, + shimmed: function () { + test('Iterator.prototype.' + fnName + ': shimmed', function (t) { + t.test('Function name', { skip: !functionsHaveNames }, function (st) { + st.equal(Iterator.prototype[fnName].name, fnName, 'Iterator#' + fnName + ' has name "' + fnName + '"'); + st.end(); + }); + + t.test('enumerability', { skip: !defineProperties.supportsDescriptors }, function (et) { + et.equal(false, isEnumerable.call(Iterator.prototype, fnName), 'Iterator#' + fnName + ' is not enumerable'); + et.end(); + }); + + t.test('bad string/this value', { skip: !hasStrictMode }, function (st) { + st['throws'](function () { return Iterator.prototype[fnName].call(undefined, 'a'); }, TypeError, 'undefined is not an object'); + st['throws'](function () { return Iterator.prototype[fnName].call(null, 'a'); }, TypeError, 'null is not an object'); + st.end(); + }); + + module.exports.tests(callBind(Iterator.prototype[fnName]), 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + } +}; diff --git a/test/Iterator.prototype.forEach.js b/test/Iterator.prototype.forEach.js new file mode 100644 index 0000000..326c149 --- /dev/null +++ b/test/Iterator.prototype.forEach.js @@ -0,0 +1,114 @@ +'use strict'; + +var defineProperties = require('define-properties'); +var test = require('tape'); +var callBind = require('call-bind'); +var functionsHaveNames = require('functions-have-names')(); +var hasStrictMode = require('has-strict-mode')(); +var forEachNormal = require('for-each'); +var debug = require('object-inspect'); +var v = require('es-value-fixtures'); +var hasSymbols = require('has-symbols/shams')(); + +var index = require('../Iterator.prototype.forEach'); +var impl = require('../Iterator.prototype.forEach/implementation'); + +var fnName = 'forEach'; + +var isEnumerable = Object.prototype.propertyIsEnumerable; + +var testIterator = require('./helpers/testIterator'); + +module.exports = { + tests: function (forEach, name, t) { + forEachNormal(v.primitives.concat(v.objects), function (nonIterator) { + t['throws']( + function () { forEach(nonIterator); }, + TypeError, + debug(nonIterator) + ' is not an Object with a callable `next` method' + ); + + var badNext = { next: nonIterator }; + t['throws']( + function () { forEach(badNext); }, + TypeError, + debug(badNext) + ' is not an Object with a callable `next` method' + ); + }); + + forEachNormal(v.nonFunctions, function (nonFunction) { + t['throws']( + function () { forEach({ next: function () {} }, nonFunction); }, + TypeError, + debug(nonFunction) + ' is not a function' + ); + }); + + t.test('actual iteration', { skip: !hasSymbols }, function (st) { + var arr = [1, 2, 3]; + var iterator = callBind(arr[Symbol.iterator], arr); + + testIterator(iterator(), [1, 2, 3], st, 'original'); + + var results = []; + var ret = forEach( + iterator(), + function (x, i) { + // eslint-disable-next-line no-invalid-this + results.push({ value: x, count: i, 'this': this, args: arguments.length }); + } + ); + + st.equal(ret, undefined, 'returns undefined'); + + st.deepEqual( + results, + [ + { value: 1, count: 0, 'this': undefined, args: 2 }, + { value: 2, count: 1, 'this': undefined, args: 2 }, + { value: 3, count: 2, 'this': undefined, args: 2 } + ], + 'forEach callback receives the expected values' + ); + + st.end(); + }); + }, + index: function () { + test('Iterator.prototype.' + fnName + ': index', function (t) { + module.exports.tests(index, 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + }, + implementation: function () { + test('Iterator.prototype.' + fnName + ': implementation', function (t) { + module.exports.tests(callBind(impl), 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + }, + shimmed: function () { + test('Iterator.prototype.' + fnName + ': shimmed', function (t) { + t.test('Function name', { skip: !functionsHaveNames }, function (st) { + st.equal(Iterator.prototype[fnName].name, fnName, 'Iterator#' + fnName + ' has name "' + fnName + '"'); + st.end(); + }); + + t.test('enumerability', { skip: !defineProperties.supportsDescriptors }, function (et) { + et.equal(false, isEnumerable.call(Iterator.prototype, fnName), 'Iterator#' + fnName + ' is not enumerable'); + et.end(); + }); + + t.test('bad string/this value', { skip: !hasStrictMode }, function (st) { + st['throws'](function () { return Iterator.prototype[fnName].call(undefined, 'a'); }, TypeError, 'undefined is not an object'); + st['throws'](function () { return Iterator.prototype[fnName].call(null, 'a'); }, TypeError, 'null is not an object'); + st.end(); + }); + + module.exports.tests(callBind(Iterator.prototype[fnName]), 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + } +}; diff --git a/test/Iterator.prototype.map.js b/test/Iterator.prototype.map.js new file mode 100644 index 0000000..42ef49a --- /dev/null +++ b/test/Iterator.prototype.map.js @@ -0,0 +1,130 @@ +'use strict'; + +var defineProperties = require('define-properties'); +var test = require('tape'); +var callBind = require('call-bind'); +var functionsHaveNames = require('functions-have-names')(); +var hasStrictMode = require('has-strict-mode')(); +var forEach = require('for-each'); +var debug = require('object-inspect'); +var v = require('es-value-fixtures'); +var hasSymbols = require('has-symbols/shams')(); + +var index = require('../Iterator.prototype.map'); +var impl = require('../Iterator.prototype.map/implementation'); + +var fnName = 'map'; + +var isEnumerable = Object.prototype.propertyIsEnumerable; + +var testIterator = require('./helpers/testIterator'); + +module.exports = { + tests: function (map, name, t) { + forEach(v.primitives.concat(v.objects), function (nonIterator) { + t['throws']( + function () { map(nonIterator); }, + TypeError, + debug(nonIterator) + ' is not an Object with a callable `next` method' + ); + + var badNext = { next: nonIterator }; + t['throws']( + function () { map(badNext); }, + TypeError, + debug(badNext) + ' is not an Object with a callable `next` method' + ); + }); + + forEach(v.nonFunctions, function (nonFunction) { + t['throws']( + function () { map({ next: function () {} }, nonFunction); }, + TypeError, + debug(nonFunction) + ' is not a function' + ); + }); + + var sentinel = {}; + var done = false; + var fakeIterator = { + next: function () { + try { + return { + done: done, + value: sentinel + }; + } finally { + done = true; + } + } + }; + var result = {}; + testIterator( + map(fakeIterator, function (x, i) { + result.value = x; + result.counter = i; + result.receiver = this; // eslint-disable-line no-invalid-this + result.args = arguments.length; + return fakeIterator; + }), + [fakeIterator], + t, + 'fake iterator, mapped, runs as expected' + ); + t.deepEqual( + result, + { value: sentinel, counter: 0, receiver: undefined, args: 2 }, + 'callback is called with the correct arguments' + ); + + t.test('actual iteration', { skip: !hasSymbols }, function (st) { + var arr = [1, 2, 3]; + var iterator = callBind(arr[Symbol.iterator], arr); + + testIterator(iterator(), [1, 2, 3], st, 'original'); + testIterator(map(iterator(), function (x) { return x; }), [1, 2, 3], st, 'identity mapper'); + testIterator(map(iterator(), function (x) { return 2 * x; }), [2, 4, 6], st, 'doubler mapper'); + + st.end(); + }); + }, + index: function () { + test('Iterator.prototype.' + fnName + ': index', function (t) { + module.exports.tests(index, 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + }, + implementation: function () { + test('Iterator.prototype.' + fnName + ': implementation', function (t) { + module.exports.tests(callBind(impl), 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + }, + shimmed: function () { + test('Iterator.prototype.' + fnName + ': shimmed', function (t) { + t.equal(typeof Iterator.prototype[fnName], 'function', 'exists and is a function'); + + t.test('Function name', { skip: !functionsHaveNames }, function (st) { + st.equal(Iterator.prototype[fnName].name, fnName, 'Iterator#' + fnName + ' has name "' + fnName + '"'); + st.end(); + }); + + t.test('enumerability', { skip: !defineProperties.supportsDescriptors }, function (et) { + et.equal(false, isEnumerable.call(Iterator.prototype, fnName), 'Iterator#' + fnName + ' is not enumerable'); + et.end(); + }); + + t.test('bad string/this value', { skip: !hasStrictMode }, function (st) { + st['throws'](function () { return Iterator.prototype[fnName].call(undefined, 'a'); }, TypeError, 'undefined is not an object'); + st['throws'](function () { return Iterator.prototype[fnName].call(null, 'a'); }, TypeError, 'null is not an object'); + st.end(); + }); + + module.exports.tests(callBind(Iterator.prototype[fnName]), 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + } +}; diff --git a/test/Iterator.prototype.reduce.js b/test/Iterator.prototype.reduce.js new file mode 100644 index 0000000..87e8fac --- /dev/null +++ b/test/Iterator.prototype.reduce.js @@ -0,0 +1,133 @@ +'use strict'; + +var defineProperties = require('define-properties'); +var test = require('tape'); +var callBind = require('call-bind'); +var functionsHaveNames = require('functions-have-names')(); +var hasStrictMode = require('has-strict-mode')(); +var forEach = require('for-each'); +var debug = require('object-inspect'); +var v = require('es-value-fixtures'); +var hasSymbols = require('has-symbols/shams')(); + +var index = require('../Iterator.prototype.reduce'); +var impl = require('../Iterator.prototype.reduce/implementation'); + +var fnName = 'reduce'; + +var isEnumerable = Object.prototype.propertyIsEnumerable; + +var testIterator = require('./helpers/testIterator'); + +module.exports = { + tests: function (reduce, name, t) { + forEach(v.primitives.concat(v.objects), function (nonIterator) { + t['throws']( + function () { reduce(nonIterator); }, + TypeError, + debug(nonIterator) + ' is not an Object with a callable `next` method' + ); + + var badNext = { next: nonIterator }; + t['throws']( + function () { reduce(badNext); }, + TypeError, + debug(badNext) + ' is not an Object with a callable `next` method' + ); + }); + + forEach(v.nonFunctions, function (nonFunction) { + t['throws']( + function () { reduce({ next: function () {} }, nonFunction); }, + TypeError, + debug(nonFunction) + ' is not a function' + ); + }); + + t.test('actual iteration', { skip: !hasSymbols }, function (st) { + var arr = [1, 2, 3]; + var iterator = callBind(arr[Symbol.iterator], arr); + + testIterator(iterator(), [1, 2, 3], st, 'original'); + + var results = []; + var ret = reduce( + iterator(), + function (acc, x, i) { + // eslint-disable-next-line no-invalid-this + results.push({ acc: acc, value: x, count: i, 'this': this, args: arguments.length }); + return acc + x; + } + ); + st.equal(ret, 6, 'returns sum of all numbers'); + st.deepEqual( + results, + [ + { acc: 1, value: 2, count: 1, 'this': undefined, args: 3 }, + { acc: 3, value: 3, count: 2, 'this': undefined, args: 3 } + ], + 'reduce callback receives the expected values without initialValue' + ); + + var results2 = []; + var ret2 = reduce( + iterator(), + function (acc, x, i) { + // eslint-disable-next-line no-invalid-this + results2.push({ acc: acc, value: x, count: i, 'this': this, args: arguments.length }); + return acc + x; + }, + 10 + ); + st.equal(ret2, 16, 'returns sum of all numbers plus initialValue'); + st.deepEqual( + results2, + [ + { acc: 10, value: 1, count: 0, 'this': undefined, args: 3 }, + { acc: 11, value: 2, count: 1, 'this': undefined, args: 3 }, + { acc: 13, value: 3, count: 2, 'this': undefined, args: 3 } + ], + 'reduce callback receives the expected values with initialValue' + ); + + st.end(); + }); + }, + index: function () { + test('Iterator.prototype.' + fnName + ': index', function (t) { + module.exports.tests(index, 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + }, + implementation: function () { + test('Iterator.prototype.' + fnName + ': implementation', function (t) { + module.exports.tests(callBind(impl), 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + }, + shimmed: function () { + test('Iterator.prototype.' + fnName + ': shimmed', function (t) { + t.test('Function name', { skip: !functionsHaveNames }, function (st) { + st.equal(Iterator.prototype[fnName].name, fnName, 'Iterator#' + fnName + ' has name "' + fnName + '"'); + st.end(); + }); + + t.test('enumerability', { skip: !defineProperties.supportsDescriptors }, function (et) { + et.equal(false, isEnumerable.call(Iterator.prototype, fnName), 'Iterator#' + fnName + ' is not enumerable'); + et.end(); + }); + + t.test('bad string/this value', { skip: !hasStrictMode }, function (st) { + st['throws'](function () { return Iterator.prototype[fnName].call(undefined, 'a'); }, TypeError, 'undefined is not an object'); + st['throws'](function () { return Iterator.prototype[fnName].call(null, 'a'); }, TypeError, 'null is not an object'); + st.end(); + }); + + module.exports.tests(callBind(Iterator.prototype[fnName]), 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + } +}; diff --git a/test/Iterator.prototype.some.js b/test/Iterator.prototype.some.js new file mode 100644 index 0000000..6eac58c --- /dev/null +++ b/test/Iterator.prototype.some.js @@ -0,0 +1,96 @@ +'use strict'; + +var defineProperties = require('define-properties'); +var test = require('tape'); +var callBind = require('call-bind'); +var functionsHaveNames = require('functions-have-names')(); +var hasStrictMode = require('has-strict-mode')(); +var forEach = require('for-each'); +var debug = require('object-inspect'); +var v = require('es-value-fixtures'); +var hasSymbols = require('has-symbols/shams')(); + +var index = require('../Iterator.prototype.some'); +var impl = require('../Iterator.prototype.some/implementation'); + +var fnName = 'some'; + +var isEnumerable = Object.prototype.propertyIsEnumerable; + +var testIterator = require('./helpers/testIterator'); + +module.exports = { + tests: function (some, name, t) { + forEach(v.primitives.concat(v.objects), function (nonIterator) { + t['throws']( + function () { some(nonIterator); }, + TypeError, + debug(nonIterator) + ' is not an Object with a callable `next` method' + ); + + var badNext = { next: nonIterator }; + t['throws']( + function () { some(badNext); }, + TypeError, + debug(badNext) + ' is not an Object with a callable `next` method' + ); + }); + + forEach(v.nonFunctions, function (nonFunction) { + t['throws']( + function () { some({ next: function () {} }, nonFunction); }, + TypeError, + debug(nonFunction) + ' is not a function' + ); + }); + + t.test('actual iteration', { skip: !hasSymbols }, function (st) { + var arr = [1, 2, 3]; + var iterator = callBind(arr[Symbol.iterator], arr); + + testIterator(iterator(), [1, 2, 3], st, 'original'); + st.equal(some(iterator(), function () { return false; }), false, 'some for always-false'); + st.equal(some(iterator(), function () { return true; }), true, 'some for always-true'); + st.equal(some(iterator(), function (x, i) { return x === 2 && i === 1; }), true, 'some returns true for matching value/index'); + + st.end(); + }); + }, + index: function () { + test('Iterator.prototype.' + fnName + ': index', function (t) { + module.exports.tests(index, 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + }, + implementation: function () { + test('Iterator.prototype.' + fnName + ': implementation', function (t) { + module.exports.tests(callBind(impl), 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + }, + shimmed: function () { + test('Iterator.prototype.' + fnName + ': shimmed', function (t) { + t.test('Function name', { skip: !functionsHaveNames }, function (st) { + st.equal(Iterator.prototype[fnName].name, fnName, 'Iterator#' + fnName + ' has name "' + fnName + '"'); + st.end(); + }); + + t.test('enumerability', { skip: !defineProperties.supportsDescriptors }, function (et) { + et.equal(false, isEnumerable.call(Iterator.prototype, fnName), 'Iterator#' + fnName + ' is not enumerable'); + et.end(); + }); + + t.test('bad string/this value', { skip: !hasStrictMode }, function (st) { + st['throws'](function () { return Iterator.prototype[fnName].call(undefined, 'a'); }, TypeError, 'undefined is not an object'); + st['throws'](function () { return Iterator.prototype[fnName].call(null, 'a'); }, TypeError, 'null is not an object'); + st.end(); + }); + + module.exports.tests(callBind(Iterator.prototype[fnName]), 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + } +}; diff --git a/test/Iterator.prototype.take.js b/test/Iterator.prototype.take.js new file mode 100644 index 0000000..1670b0f --- /dev/null +++ b/test/Iterator.prototype.take.js @@ -0,0 +1,95 @@ +'use strict'; + +var defineProperties = require('define-properties'); +var test = require('tape'); +var callBind = require('call-bind'); +var functionsHaveNames = require('functions-have-names')(); +var hasStrictMode = require('has-strict-mode')(); +var forEach = require('for-each'); +var debug = require('object-inspect'); +var v = require('es-value-fixtures'); +var hasSymbols = require('has-symbols/shams')(); + +var index = require('../Iterator.prototype.take'); +var impl = require('../Iterator.prototype.take/implementation'); + +var fnName = 'take'; + +var isEnumerable = Object.prototype.propertyIsEnumerable; + +var testIterator = require('./helpers/testIterator'); + +module.exports = { + tests: function (take, name, t) { + forEach(v.primitives.concat(v.objects), function (nonIterator) { + t['throws']( + function () { take(nonIterator); }, + TypeError, + debug(nonIterator) + ' is not an Object with a callable `next` method' + ); + + var badNext = { next: nonIterator }; + t['throws']( + function () { take(badNext); }, + TypeError, + debug(badNext) + ' is not an Object with a callable `next` method' + ); + }); + + var iterator = [1, 2, 3]; + + t.test('actual iteration', { skip: !hasSymbols }, function (st) { + st['throws']( + function () { take(iterator[Symbol.iterator](), -3); }, + RangeError, + '-3 is not >= 0' + ); + + testIterator(iterator[Symbol.iterator](), [1, 2, 3], st, 'original'); + testIterator(take(iterator[Symbol.iterator](), 0), [], st, 'take 0'); + testIterator(take(iterator[Symbol.iterator](), 1), [1], st, 'take 1'); + testIterator(take(iterator[Symbol.iterator](), 2), [1, 2], st, 'take 2'); + testIterator(take(iterator[Symbol.iterator](), 3), [1, 2, 3], st, 'take 3'); + testIterator(take(iterator[Symbol.iterator](), Infinity), [1, 2, 3], st, 'take ∞'); + + st.end(); + }); + }, + index: function () { + test('Iterator.prototype.' + fnName + ': index', function (t) { + module.exports.tests(index, 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + }, + implementation: function () { + test('Iterator.prototype.' + fnName + ': implementation', function (t) { + module.exports.tests(callBind(impl), 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + }, + shimmed: function () { + test('Iterator.prototype.' + fnName + ': shimmed', function (t) { + t.test('Function name', { skip: !functionsHaveNames }, function (st) { + st.equal(Iterator.prototype[fnName].name, fnName, 'Iterator#' + fnName + ' has name "' + fnName + '"'); + st.end(); + }); + + t.test('enumerability', { skip: !defineProperties.supportsDescriptors }, function (et) { + et.equal(false, isEnumerable.call(Iterator.prototype, fnName), 'Iterator#' + fnName + ' is not enumerable'); + et.end(); + }); + + t.test('bad string/this value', { skip: !hasStrictMode }, function (st) { + st['throws'](function () { return Iterator.prototype[fnName].call(undefined, 'a'); }, TypeError, 'undefined is not an object'); + st['throws'](function () { return Iterator.prototype[fnName].call(null, 'a'); }, TypeError, 'null is not an object'); + st.end(); + }); + + module.exports.tests(callBind(Iterator.prototype[fnName]), 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + } +}; diff --git a/test/Iterator.prototype.toArray.js b/test/Iterator.prototype.toArray.js new file mode 100644 index 0000000..2b1be25 --- /dev/null +++ b/test/Iterator.prototype.toArray.js @@ -0,0 +1,87 @@ +'use strict'; + +var defineProperties = require('define-properties'); +var test = require('tape'); +var callBind = require('call-bind'); +var functionsHaveNames = require('functions-have-names')(); +var hasStrictMode = require('has-strict-mode')(); +var forEach = require('for-each'); +var debug = require('object-inspect'); +var v = require('es-value-fixtures'); +var hasSymbols = require('has-symbols/shams')(); + +var index = require('../Iterator.prototype.toArray'); +var impl = require('../Iterator.prototype.toArray/implementation'); + +var fnName = 'toArray'; + +var isEnumerable = Object.prototype.propertyIsEnumerable; + +var testIterator = require('./helpers/testIterator'); + +module.exports = { + tests: function (toArray, name, t) { + forEach(v.primitives.concat(v.objects), function (nonIterator) { + t['throws']( + function () { toArray(nonIterator); }, + TypeError, + debug(nonIterator) + ' is not an Object with a callable `next` method' + ); + + var badNext = { next: nonIterator }; + t['throws']( + function () { toArray(badNext); }, + TypeError, + debug(badNext) + ' is not an Object with a callable `next` method' + ); + }); + + t.test('actual iteration', { skip: !hasSymbols }, function (st) { + var arr = [1, 2, 3]; + var iterator = callBind(arr[Symbol.iterator], arr); + + testIterator(iterator(), [1, 2, 3], st, 'original'); + + st.deepEqual(toArray(iterator()), [1, 2, 3], 'toArray'); + + st.end(); + }); + }, + index: function () { + test('Iterator.prototype.' + fnName + ': index', function (t) { + module.exports.tests(index, 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + }, + implementation: function () { + test('Iterator.prototype.' + fnName + ': implementation', function (t) { + module.exports.tests(callBind(impl), 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + }, + shimmed: function () { + test('Iterator.prototype.' + fnName + ': shimmed', function (t) { + t.test('Function name', { skip: !functionsHaveNames }, function (st) { + st.equal(Iterator.prototype[fnName].name, fnName, 'Iterator#' + fnName + ' has name "' + fnName + '"'); + st.end(); + }); + + t.test('enumerability', { skip: !defineProperties.supportsDescriptors }, function (et) { + et.equal(false, isEnumerable.call(Iterator.prototype, fnName), 'Iterator#' + fnName + ' is not enumerable'); + et.end(); + }); + + t.test('bad string/this value', { skip: !hasStrictMode }, function (st) { + st['throws'](function () { return Iterator.prototype[fnName].call(undefined, 'a'); }, TypeError, 'undefined is not an object'); + st['throws'](function () { return Iterator.prototype[fnName].call(null, 'a'); }, TypeError, 'null is not an object'); + st.end(); + }); + + module.exports.tests(callBind(Iterator.prototype[fnName]), 'Iterator.prototype.' + fnName, t); + + t.end(); + }); + } +}; diff --git a/test/helpers/testIterator.js b/test/helpers/testIterator.js new file mode 100644 index 0000000..65051b4 --- /dev/null +++ b/test/helpers/testIterator.js @@ -0,0 +1,7 @@ +'use strict'; + +var iterate = require('iterate-iterator'); + +module.exports = function testIterator(iterator, expected, t, msg) { + t.deepEqual(iterate(iterator), expected, 'iterator yields expected values: ' + msg); +}; diff --git a/test/implementation.js b/test/implementation.js new file mode 100644 index 0000000..d3bf160 --- /dev/null +++ b/test/implementation.js @@ -0,0 +1,18 @@ +'use strict'; + +var test = require('tape'); +var forEach = require('for-each'); + +var shims = require('../'); + +forEach(shims, function (shim) { + var shimTests; + try { + shimTests = require('./' + shim); // eslint-disable-line global-require + } catch (e) { + test(shim + ': implementation', { todo: true }); + } + if (shimTests) { + shimTests.implementation(); + } +}); diff --git a/test/index.js b/test/index.js new file mode 100644 index 0000000..66bed98 --- /dev/null +++ b/test/index.js @@ -0,0 +1,19 @@ +'use strict'; + +var test = require('tape'); +var forEach = require('for-each'); + +var shims = require('../'); + +forEach(shims, function (shim) { + var shimTests; + try { + shimTests = require('./' + shim); // eslint-disable-line global-require + } catch (e) { + test(shim + ': index', { todo: true }); + } + if (shimTests) { + shimTests.index(); + } +}); + diff --git a/test/shimmed.js b/test/shimmed.js new file mode 100644 index 0000000..8eda2d6 --- /dev/null +++ b/test/shimmed.js @@ -0,0 +1,20 @@ +'use strict'; + +require('../auto'); + +var test = require('tape'); +var forEach = require('for-each'); + +var shims = require('../'); + +forEach(shims, function (shim) { + var shimTests; + try { + shimTests = require('./' + shim); // eslint-disable-line global-require + } catch (e) { + test(shim + ': shimmed', { todo: true }); + } + if (shimTests) { + shimTests.shimmed(); + } +}); diff --git a/test/tests.js b/test/tests.js new file mode 100644 index 0000000..7949f54 --- /dev/null +++ b/test/tests.js @@ -0,0 +1,25 @@ +'use strict'; + +var data = { + anchor: { arg: 'bar"baz"', expected: 'foo' }, + big: 'foo', + blink: 'foo', + bold: 'foo', + fixed: 'foo', + fontcolor: { arg: 'blue"red"green', expected: 'foo' }, + fontsize: { arg: '10"large"small', expected: 'foo' }, + italics: 'foo', + link: { arg: 'url"http://"', expected: 'foo' }, + small: 'foo', + strike: 'foo', + sub: 'foo', + sup: 'foo' +}; + +module.exports = function (method, name, t) { + var result = data[name] || {}; + var expected = typeof result === 'string' ? result : result.expected; + var actual = typeof result === 'string' ? method('foo') : method('foo', result.arg); + + t.equal(actual, expected, name + ': got expected result'); +};