diff --git a/.gitattributes b/.gitattributes index 391f0a4..6313b56 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1 @@ -* text=auto -*.js text eol=lf +* text=auto eol=lf diff --git a/.travis.yml b/.travis.yml index 2ae9d62..f3fa8cd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,4 +2,3 @@ language: node_js node_js: - '10' - '8' - - '6' diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..602e2b7 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,35 @@ +import {Options} from 'p-map'; + +export type Mapper = ( + element: PromiseLike | ValueType, + key: KeyType +) => MappedValueType | PromiseLike; + +/** + * Like [`Promise.all()`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) but for `Map` and `Object`. + * + * @param input - Resolves entry values that are promises. Other values are passed through. + * @param mapper - Receives the current value and key as parameters. Expected to return a `Promise` or value. + * @param options - See the [`p-map` options](https://github.com/sindresorhus/p-map#options). + * @returns A promise that is fulfilled when all promises in `input` and ones returned from `mapper` are fulfilled, or rejects if any of the promises reject. The fulfilled value is the same as `input`, but with a fulfilled version of each entry value, or the fulfilled value returned from `mapper`, if defined. + */ +export default function pProps< + KeyType extends unknown, + ValueType extends unknown, + MappedValueType = ValueType +>( + input: Map | ValueType>, + mapper?: Mapper, + options?: Options +): Promise>; +export default function pProps< + KeyType extends string, + ValueType extends unknown, + MappedValueType = ValueType +>( + input: {[key in KeyType]: PromiseLike | ValueType}, + mapper?: Mapper, + options?: Options +): Promise<{[key in KeyType]: MappedValueType}>; + +export {Options} from 'p-map'; diff --git a/index.js b/index.js index b7229f5..229705e 100644 --- a/index.js +++ b/index.js @@ -1,32 +1,35 @@ 'use strict'; + const pMap = require('p-map'); -const map = (input, mapper, options) => { - return pMap(input.entries(), ([key, value]) => mapper(value, key), options).then(values => { - const ret = new Map(); +const map = async (input, mapper, options) => { + const values = await pMap(input.entries(), ([key, value]) => mapper(value, key), options); + const result = new Map(); - for (const [i, key] of [...input.keys()].entries()) { - ret.set(key, values[i]); - } + for (const [i, key] of [...input.keys()].entries()) { + result.set(key, values[i]); + } - return ret; - }); + return result; }; -const obj = (input, mapper, options) => { - // TODO: Use `Object.entries()` when targeting Node.js 8 - return pMap(Object.keys(input), key => mapper(input[key], key), options).then(values => { - const ret = {}; +const obj = async (input, mapper, options) => { + const values = await pMap(Object.entries(input), ([key, value]) => mapper(value, key), options); + const result = {}; - for (const [i, key] of Object.keys(input).entries()) { - ret[key] = values[i]; - } + for (const [i, key] of Object.keys(input).entries()) { + result[key] = values[i]; + } - return ret; - }); + return result; }; -module.exports = (input, mapper, options) => { +const pProps = (input, mapper, options) => { mapper = mapper || (value => value); - return input instanceof Map ? map(input, mapper, options) : obj(input, mapper, options); + return input instanceof Map ? + map(input, mapper, options) : + obj(input, mapper, options); }; + +module.exports = pProps; +module.exports.default = pProps; diff --git a/index.test-d.ts b/index.test-d.ts new file mode 100644 index 0000000..46df5f1 --- /dev/null +++ b/index.test-d.ts @@ -0,0 +1,84 @@ +import {expectType} from 'tsd-check'; +import pProps from '.'; + +expectType>(pProps({foo: 'bar'})); +expectType>( + pProps({foo: 'bar'}, (value, key) => { + expectType>(value); + expectType<'foo'>(key); + return Math.random() > 0.5 ? false : Promise.resolve(true); + }) +); +expectType>( + pProps( + {foo: 'bar'}, + (value, key) => { + expectType>(value); + expectType<'foo'>(key); + return Math.random() > 0.5 ? false : Promise.resolve(true); + }, + { + concurrency: 1 + } + ) +); + +const hashMap = { + unicorn: Promise.resolve(1), + foo: 'bar' +}; + +expectType>( + pProps(hashMap) +); +expectType>( + pProps(hashMap, (value, key) => { + expectType>(value); + expectType(key); + return Math.random() > 0.5 ? false : Promise.resolve(true); + }) +); +expectType>( + pProps( + hashMap, + (value, key) => { + expectType>(value); + expectType(key); + return Math.random() > 0.5 ? false : Promise.resolve(true); + }, + { + concurrency: 1 + } + ) +); + +const map = new Map>([ + [1, Promise.resolve('1')], + [2, '2'] +]); + +pProps(map).then(result => { + expectType(result.get(1)); +}); + +expectType>>(pProps(map)); +expectType>>( + pProps(map, (value, key) => { + expectType>(value); + expectType(key); + return Math.random() > 0.5 ? false : Promise.resolve(true); + }) +); +expectType>>( + pProps( + map, + (value, key) => { + expectType>(value); + expectType(key); + return Math.random() > 0.5 ? false : Promise.resolve(true); + }, + { + concurrency: 1 + } + ) +); diff --git a/package.json b/package.json index efd8a94..a8a8624 100644 --- a/package.json +++ b/package.json @@ -10,13 +10,14 @@ "url": "sindresorhus.com" }, "engines": { - "node": ">=6" + "node": ">=8" }, "scripts": { - "test": "xo && ava" + "test": "xo && ava && tsd-check" }, "files": [ - "index.js" + "index.js", + "index.d.ts" ], "keywords": [ "promise", @@ -36,11 +37,12 @@ "bluebird" ], "dependencies": { - "p-map": "^1.2.0" + "p-map": "^2.0.0" }, "devDependencies": { - "ava": "^0.25.0", - "delay": "^2.0.0", - "xo": "^0.23.0" + "ava": "^1.3.1", + "delay": "^4.1.0", + "tsd-check": "^0.3.0", + "xo": "^0.24.0" } } diff --git a/readme.md b/readme.md index 5a7357e..a51c12e 100644 --- a/readme.md +++ b/readme.md @@ -50,7 +50,7 @@ Resolves entry values that are promises. Other values are passed through. Type: `Function` -Expected to return a `Promise` or value. +Receives the current value and key as parameters. Expected to return a `Promise` or value. #### options diff --git a/test.js b/test.js index 0fd2701..ebb352b 100644 --- a/test.js +++ b/test.js @@ -1,10 +1,10 @@ import test from 'ava'; import delay from 'delay'; -import m from '.'; +import pProps from '.'; test('main', async t => { t.deepEqual( - await m({ + await pProps({ foo: delay(100).then(() => 1), bar: Promise.resolve(2), faz: 3 @@ -19,7 +19,7 @@ test('main', async t => { test('`Map` input', async t => { t.deepEqual( - await m(new global.Map([ + await pProps(new global.Map([ ['foo', Promise.resolve(1)], ['bar', 2] ])), @@ -31,8 +31,8 @@ test('`Map` input', async t => { }); test('rejects if any of the input promises reject', async t => { - await t.throws( - m({ + await t.throwsAsync( + pProps({ foo: Promise.resolve(1), bar: Promise.reject(new Error('bar')) }), @@ -41,13 +41,13 @@ test('rejects if any of the input promises reject', async t => { }); test('handles empty object', async t => { - t.deepEqual(await m({}), {}); - t.deepEqual((await m(new global.Map([]))), new global.Map([])); + t.deepEqual(await pProps({}), {}); + t.deepEqual((await pProps(new global.Map([]))), new global.Map([])); }); test('with mapper', async t => { t.deepEqual( - await m({ + await pProps({ foo: 1, baz: Promise.resolve(2) }, (value, key) => Promise.resolve(value).then(resolvedValue => key + resolvedValue)), @@ -60,7 +60,7 @@ test('with mapper', async t => { test('`Map` input with mapper', async t => { t.deepEqual( - await m(new global.Map([ + await pProps(new global.Map([ ['foo', 1], ['bar', Promise.resolve(2)] ]), (value, key) => Promise.resolve(value).then(resolvedValue => key + resolvedValue)),