Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Await values before passing them to mapper, refactor TS definition to CJS compatible export #12

Merged
merged 1 commit into from
Apr 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
language: node_js
node_js:
- '12'
- '10'
- '8'
80 changes: 56 additions & 24 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,69 @@
import {Options} from 'p-map';
import {Options as PMapOptions} from 'p-map';

export type PromiseResult<Value> = Value extends PromiseLike<infer Result> ? Result : Value;
declare namespace pProps {
type Options = PMapOptions;

export type Mapper<ValueType, KeyType, MappedValueType> = (
element: ValueType,
key: KeyType
) => MappedValueType | PromiseLike<MappedValueType>;
type PromiseResult<Value> = Value extends PromiseLike<infer Result> ? Result : Value;

type Mapper<ValueType, KeyType, MappedValueType> = (
value: ValueType,
key: KeyType
) => MappedValueType | PromiseLike<MappedValueType>;
}

/**
* 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<
Like [`Promise.all()`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) but for `Map` and `Object`.

@param map - 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 `map` and ones returned from `mapper` are fulfilled, or rejects if any of the promises reject. The fulfilled value is the same as `map`, but with a fulfilled version of each entry value, or the fulfilled value returned from `mapper`, if defined.

@example
```
import pProps = require('p-props');
import got = require('got');

(async () => {
const fetch = async url => {
const {body} = await got(url);
return body;
};

const sites = {
ava: fetch('ava.li'),
todomvc: fetch('todomvc.com'),
github: fetch('github.com'),
foo: 'bar'
};

console.log(await pProps(sites));
// {
// ava: '<!doctype …',
// todomvc: '<!doctype …',
// github: '<!doctype …',
// foo: 'bar'
// }
})();
```
*/
declare function pProps<
KeyType extends unknown,
ValueType extends unknown,
MappedValueType = PromiseResult<ValueType>
MappedValueType = pProps.PromiseResult<ValueType>
>(
input: Map<KeyType, ValueType>,
mapper?: Mapper<ValueType, KeyType, MappedValueType>,
options?: Options
map: Map<KeyType, ValueType>,
mapper?: pProps.Mapper<pProps.PromiseResult<ValueType>, KeyType, MappedValueType>,
options?: pProps.Options
): Promise<Map<KeyType, MappedValueType>>;
export default function pProps<
declare function pProps<
InputType extends {[key: string]: unknown},
ValueType extends InputType[keyof InputType],
MappedValueType = PromiseResult<ValueType>
MappedValueType = pProps.PromiseResult<ValueType>
>(
input: InputType,
mapper?: Mapper<ValueType, keyof InputType, MappedValueType>,
options?: Options
map: InputType,
mapper?: pProps.Mapper<pProps.PromiseResult<ValueType>, keyof InputType, MappedValueType>,
options?: pProps.Options
): Promise<{[key in keyof InputType]: MappedValueType}>;

export {Options} from 'p-map';
export = pProps;
15 changes: 8 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
'use strict';
const pMap = require('p-map');

const map = async (input, mapper, options) => {
const values = await pMap(input.entries(), ([key, value]) => mapper(value, key), options);
const map = async (map, mapper, options) => {
const awaitedEntries = [...map.entries()].map(async ([key, value]) => [key, await value]);
const values = await pMap(awaitedEntries, ([key, value]) => mapper(value, key), options);
const result = new Map();

for (const [index, key] of [...input.keys()].entries()) {
for (const [index, key] of [...map.keys()].entries()) {
result.set(key, values[index]);
}

return result;
};

const object = async (input, mapper, options) => {
const values = await pMap(Object.entries(input), ([key, value]) => mapper(value, key), options);
const object = async (map, mapper, options) => {
const awaitedEntries = Object.entries(map).map(async ([key, value]) => [key, await value]);
const values = await pMap(awaitedEntries, ([key, value]) => mapper(value, key), options);
const result = {};

for (const [index, key] of Object.keys(input).entries()) {
for (const [index, key] of Object.keys(map).entries()) {
result[key] = values[index];
}

Expand All @@ -30,4 +32,3 @@ const pProps = (input, mapper = (value => value), options) => {
};

module.exports = pProps;
module.exports.default = pProps;
16 changes: 10 additions & 6 deletions index.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {expectType} from 'tsd-check';
import pProps from '.';
import {expectType} from 'tsd';
import pProps = require('.');

const options: pProps.Options = {};

expectType<Promise<{[key in 'foo']: string}>>(pProps({foo: 'bar'}));
expectType<Promise<{[key in 'foo']: boolean}>>(
Expand Down Expand Up @@ -28,10 +30,12 @@ const hashMap = {
foo: 'bar'
};

expectType<Promise<{[key in 'unicorn' | 'foo']: string | number}>>(pProps(hashMap));
expectType<Promise<{[key in 'unicorn' | 'foo']: string | number}>>(
pProps(hashMap)
);
expectType<Promise<{[key in 'unicorn' | 'foo']: boolean}>>(
pProps(hashMap, (value, key) => {
expectType<string | Promise<number>>(value);
expectType<string | number>(value);
expectType<string>(key);
return Math.random() > 0.5 ? false : Promise.resolve(true);
})
Expand All @@ -40,7 +44,7 @@ expectType<Promise<{[key in 'unicorn' | 'foo']: boolean}>>(
pProps(
hashMap,
(value, key) => {
expectType<string | Promise<number>>(value);
expectType<string | number>(value);
expectType<string>(key);
return Math.random() > 0.5 ? false : Promise.resolve(true);
},
Expand All @@ -50,7 +54,7 @@ expectType<Promise<{[key in 'unicorn' | 'foo']: boolean}>>(
)
);

const partialMap: {foo?: Promise<string>} = {}
const partialMap: {foo?: Promise<string>} = {};
expectType<Promise<{foo?: string}>>(pProps(partialMap));

const map = new Map<number, string | Promise<string>>([
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"node": ">=8"
},
"scripts": {
"test": "xo && ava && tsd-check"
"test": "xo && ava && tsd"
},
"files": [
"index.js",
Expand All @@ -39,9 +39,9 @@
"p-map": "^2.0.0"
},
"devDependencies": {
"ava": "^1.3.1",
"delay": "^4.1.0",
"tsd-check": "^0.3.0",
"ava": "^1.4.1",
"delay": "^4.2.0",
"tsd": "^0.7.2",
"xo": "^0.24.0"
}
}
14 changes: 7 additions & 7 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,25 +46,25 @@ const got = require('got');

## API

### pProps(input, [mapper], [options])
### pProps(map, [mapper], [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.
Returns a `Promise` that is fulfilled when all promises in `map` and ones returned from `mapper` are fulfilled, or rejects if any of the promises reject. The fulfilled value is the same as `map`, but with a fulfilled version of each entry value, or the fulfilled value returned from `mapper`, if defined.

#### input
#### map

Type: `Map` `Object`
Type: `Map | object`

Resolves entry values that are promises. Other values are passed through.

#### mapper(element, index)
#### mapper(value, key)

Type: `Function`

Receives the current value and key as parameters. Expected to return a `Promise` or value.
Receives the current value and key as parameters. If a value is a `Promise`, `mapper` will receive the value this `Promise` resolves to. Expected to return a `Promise` or value.

#### options

Type: `Object`
Type: `object`

See the [`p-map` options](https://github.com/sindresorhus/p-map#options).

Expand Down
38 changes: 31 additions & 7 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ test('main', async t => {

test('`Map` input', async t => {
t.deepEqual(
await pProps(new global.Map([
await pProps(new Map([
['foo', Promise.resolve(1)],
['bar', 2]
])),
new global.Map([
new Map([
['foo', 1],
['bar', 2]
])
Expand All @@ -42,15 +42,27 @@ test('rejects if any of the input promises reject', async t => {

test('handles empty object', async t => {
t.deepEqual(await pProps({}), {});
t.deepEqual((await pProps(new global.Map([]))), new global.Map([]));
t.deepEqual((await pProps(new Map([]))), new Map([]));
});

test('with mapper', async t => {
t.plan(3);

t.deepEqual(
await pProps({
foo: 1,
baz: Promise.resolve(2)
}, (value, key) => Promise.resolve(value).then(resolvedValue => key + resolvedValue)),
}, async (value, key) => {
if (key === 'foo') {
t.is(value, 1);
}

if (key === 'baz') {
t.is(value, 2);
}

return key + value;
}),
{
foo: 'foo1',
baz: 'baz2'
Expand All @@ -59,12 +71,24 @@ test('with mapper', async t => {
});

test('`Map` input with mapper', async t => {
t.plan(3);

t.deepEqual(
await pProps(new global.Map([
await pProps(new Map([
['foo', 1],
['bar', Promise.resolve(2)]
]), (value, key) => Promise.resolve(value).then(resolvedValue => key + resolvedValue)),
new global.Map([
]), async (value, key) => {
if (key === 'foo') {
t.is(value, 1);
}

if (key === 'bar') {
t.is(value, 2);
}

return key + value;
}),
new Map([
['foo', 'foo1'],
['bar', 'bar2']
])
Expand Down