Skip to content

Commit

Permalink
Require Node.js 18
Browse files Browse the repository at this point in the history
Fixes #16
  • Loading branch information
sindresorhus committed Oct 13, 2023
1 parent baa71b8 commit 7eb631f
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 65 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ jobs:
fail-fast: false
matrix:
node-version:
- 14
- 12
- 20
- 18
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: npm install
Expand Down
31 changes: 18 additions & 13 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import {Options} from 'p-map';

export type PromiseResult<Value> = Value extends PromiseLike<infer Result> ? Result : Value;
import {type Options} from 'p-map';

export type Mapper<ValueType, KeyType, MappedValueType> = (
value: ValueType,
Expand Down Expand Up @@ -41,23 +39,30 @@ console.log(await pProps(sites));
// }
```
*/
export default function pProps<
KeyType,
ValueType,
MappedValueType = PromiseResult<ValueType>
export default function pProps< // This overload exists to get more accurate results when the mapper is not defined.
InputType extends Record<PropertyKey, unknown>,
>(
map: ReadonlyMap<KeyType, ValueType>,
mapper?: Mapper<PromiseResult<ValueType>, KeyType, MappedValueType>,
map: InputType,
mapper?: undefined,
options?: Options
): Promise<Map<KeyType, MappedValueType>>;
): Promise<{[key in keyof InputType]: Awaited<InputType[key]>}>;
export default function pProps<
InputType extends Record<string, any>,
InputType extends Record<string, unknown>,
ValueType extends InputType[keyof InputType],
MappedValueType = PromiseResult<ValueType>
MappedValueType = Awaited<ValueType>,
>(
map: InputType,
mapper?: Mapper<PromiseResult<ValueType>, keyof InputType, MappedValueType>,
mapper?: Mapper<Awaited<ValueType>, keyof InputType, MappedValueType>,
options?: Options
): Promise<{[key in keyof InputType]: MappedValueType}>;
export default function pProps<
KeyType,
ValueType,
MappedValueType = Awaited<ValueType>,
>(
map: ReadonlyMap<KeyType, ValueType>,
mapper?: Mapper<Awaited<ValueType>, KeyType, MappedValueType>,
options?: Options
): Promise<Map<KeyType, MappedValueType>>;

export {Options} from 'p-map';
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import pMap from 'p-map';

export default async function pProps(input, mapper = (value => value), options = undefined) {
const isMap = input instanceof Map;
const entries = isMap ? [...input.entries()] : Object.entries(input);
const entries = isMap ? [...input] : Object.entries(input);
const values = await pMap(entries, async ([key, value]) => mapper(await value, key), options);
const mappedEntries = entries.map(([key], index) => [key, values[index]]);
return isMap ? new Map(mappedEntries) : Object.fromEntries(mappedEntries);
Expand Down
65 changes: 41 additions & 24 deletions index.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,66 +1,83 @@
import {expectType, expectAssignable} from 'tsd';
import pProps, {Options} from './index.js';
import pProps, {type Options} from './index.js';

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const options: Options = {};

expectType<Promise<{[key in 'foo']: string}>>(pProps({foo: 'bar'}));
expectType<Promise<{[key in 'foo']: boolean}>>(
pProps({foo: 'bar'}, (value, key) => {
pProps({foo: 'bar'}, async (value, key) => {
expectType<string>(value);
expectType<'foo'>(key);
return Math.random() > 0.5 ? false : Promise.resolve(true);
})
}),
);
expectType<Promise<{[key in 'foo']: boolean}>>(
pProps(
{foo: 'bar'},
(value, key) => {
async (value, key) => {
expectType<string>(value);
expectType<'foo'>(key);
return Math.random() > 0.5 ? false : Promise.resolve(true);
},
{
concurrency: 1
}
)
concurrency: 1,
},
),
);

const hashMap = {
unicorn: Promise.resolve(1),
foo: 'bar'
foo: 'bar',
};

expectType<Promise<{[key in 'unicorn' | 'foo']: string | number}>>(
pProps(hashMap)
// TODO: Should ideally be:
// expectType<Promise<{unicorn: 1; foo: 'bar'}>>(
expectType<Promise<{unicorn: number; foo: string}>>(
pProps(hashMap),
);

const hashMap2 = {
unicorn: Promise.resolve(1),
foo: 'bar',
} as const;

// TODO: Should ideally be:
// expectType<Promise<{readonly unicorn: 1; readonly foo: 'bar'}>>(
expectType<Promise<{readonly unicorn: number; readonly foo: 'bar'}>>(
pProps(hashMap2),
);

expectType<{bar: 'baz'; thud: 'qux'}>(
await pProps({bar: 'baz' as const, thud: 'qux' as const}),
);

expectType<Promise<{[key in 'unicorn' | 'foo']: boolean}>>(
pProps(hashMap, (value, key) => {
pProps(hashMap, async (value, key) => {
expectType<string | number>(value);
expectAssignable<string>(key);
return Math.random() > 0.5 ? false : Promise.resolve(true);
})
}),
);
expectType<Promise<{[key in 'unicorn' | 'foo']: boolean}>>(
pProps(
hashMap,
(value, key) => {
async (value, key) => {
expectType<string | number>(value);
expectAssignable<string>(key);
return Math.random() > 0.5 ? false : Promise.resolve(true);
},
{
concurrency: 1
}
)
concurrency: 1,
},
),
);

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

const map = new Map<number, string | Promise<string>>([
[1, Promise.resolve('1')],
[2, '2']
[2, '2'],
]);

const result = await pProps(map);
Expand All @@ -69,22 +86,22 @@ expectType<string | undefined>(result.get(1));

expectType<Promise<Map<number, string>>>(pProps(map));
expectType<Promise<Map<number, number>>>(
pProps(map, (value, key) => {
pProps(map, async (value, key) => {
expectType<string>(value);
expectType<number>(key);
return Math.random() > 0.5 ? 1 : Promise.resolve(2);
})
}),
);
expectType<Promise<Map<number, number>>>(
pProps(
map,
(value, key) => {
async (value, key) => {
expectType<string>(value);
expectType<number>(key);
return Math.random() > 0.5 ? 1 : Promise.resolve(2);
},
{
concurrency: 1
}
)
concurrency: 1,
},
),
);
17 changes: 10 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@
"url": "https://sindresorhus.com"
},
"type": "module",
"exports": "./index.js",
"exports": {
"types": "./index.d.ts",
"default": "./index.js"
},
"engines": {
"node": ">=12.20"
"node": ">=18"
},
"scripts": {
"test": "xo && ava && tsd"
Expand All @@ -39,12 +42,12 @@
"bluebird"
],
"dependencies": {
"p-map": "^5.0.0"
"p-map": "^6.0.0"
},
"devDependencies": {
"ava": "^3.15.0",
"delay": "^5.0.0",
"tsd": "^0.17.0",
"xo": "^0.40.2"
"ava": "^5.3.1",
"delay": "^6.0.0",
"tsd": "^0.29.0",
"xo": "^0.56.0"
}
}
4 changes: 2 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ Useful when you need to run multiple promises concurrently and keep track of the

## Install

```
$ npm install p-props
```sh
npm install p-props
```

## Usage
Expand Down
28 changes: 14 additions & 14 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,36 @@ test('main', async t => {
await pProps({
foo: delay(100).then(() => 1),
bar: Promise.resolve(2),
faz: 3
faz: 3,
}),
{
foo: 1,
bar: 2,
faz: 3
}
faz: 3,
},
);
});

test('`Map` input', async t => {
t.deepEqual(
await pProps(new Map([
['foo', Promise.resolve(1)],
['bar', 2]
['bar', 2],
])),
new Map([
['foo', 1],
['bar', 2]
])
['bar', 2],
]),
);
});

test('rejects if any of the input promises reject', async t => {
await t.throwsAsync(
pProps({
foo: Promise.resolve(1),
bar: Promise.reject(new Error('bar'))
bar: Promise.reject(new Error('bar')),
}),
{message: 'bar'}
{message: 'bar'},
);
});

Expand All @@ -51,7 +51,7 @@ test('with mapper', async t => {
t.deepEqual(
await pProps({
foo: 1,
baz: Promise.resolve(2)
baz: Promise.resolve(2),
}, async (value, key) => {
if (key === 'foo') {
t.is(value, 1);
Expand All @@ -65,8 +65,8 @@ test('with mapper', async t => {
}),
{
foo: 'foo1',
baz: 'baz2'
}
baz: 'baz2',
},
);
});

Expand All @@ -76,7 +76,7 @@ test('`Map` input with mapper', async t => {
t.deepEqual(
await pProps(new Map([
['foo', 1],
['bar', Promise.resolve(2)]
['bar', Promise.resolve(2)],
]), async (value, key) => {
if (key === 'foo') {
t.is(value, 1);
Expand All @@ -90,7 +90,7 @@ test('`Map` input with mapper', async t => {
}),
new Map([
['foo', 'foo1'],
['bar', 'bar2']
])
['bar', 'bar2'],
]),
);
});

0 comments on commit 7eb631f

Please sign in to comment.