diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c1870cf..d50ada6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,12 +10,12 @@ jobs: fail-fast: false matrix: node-version: + - 18 + - 16 - 14 - - 12 - - 10 steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - run: npm install diff --git a/index.js b/index.js index a3877e1..6a32c79 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,4 @@ -'use strict'; - -const processFn = (fn, options, proxy, unwrapped) => function (...arguments_) { +const processFunction = (function_, options, proxy, unwrapped) => function (...arguments_) { const P = options.promiseModule; return new P((resolve, reject) => { @@ -30,18 +28,18 @@ const processFn = (fn, options, proxy, unwrapped) => function (...arguments_) { } const self = this === proxy ? unwrapped : this; - Reflect.apply(fn, self, arguments_); + Reflect.apply(function_, self, arguments_); }); }; const filterCache = new WeakMap(); -module.exports = (input, options) => { +export default function pify(input, options) { options = { exclude: [/.+(?:Sync|Stream)$/], errorFirst: true, promiseModule: Promise, - ...options + ...options, }; const objectType = typeof input; @@ -62,9 +60,9 @@ module.exports = (input, options) => { } const match = pattern => (typeof pattern === 'string' || typeof key === 'symbol') ? key === pattern : pattern.test(key); - const desc = Reflect.getOwnPropertyDescriptor(target, key); - const writableOrConfigurableOwn = (desc === undefined || desc.writable || desc.configurable); - const included = options.include ? options.include.some(match) : !options.exclude.some(match); + const descriptor = Reflect.getOwnPropertyDescriptor(target, key); + const writableOrConfigurableOwn = (descriptor === undefined || descriptor.writable || descriptor.configurable); + const included = options.include ? options.include.some(element => match(element)) : !options.exclude.some(element => match(element)); const shouldFilter = included && writableOrConfigurableOwn; cached[key] = shouldFilter; return shouldFilter; @@ -80,7 +78,7 @@ module.exports = (input, options) => { return Reflect.apply(cached, thisArg, args); } - const pified = options.excludeMain ? target : processFn(target, options, proxy, target); + const pified = options.excludeMain ? target : processFunction(target, options, proxy, target); cache.set(target, pified); return Reflect.apply(pified, thisArg, args); }, @@ -100,14 +98,14 @@ module.exports = (input, options) => { } if (typeof property === 'function') { - const pified = processFn(property, options, proxy, target); + const pified = processFunction(property, options, proxy, target); cache.set(property, pified); return pified; } return property; - } + }, }); return proxy; -}; +} diff --git a/optimization-test.js b/optimization-test.js index 38abd5b..31d6c1d 100644 --- a/optimization-test.js +++ b/optimization-test.js @@ -1,8 +1,8 @@ /* eslint-disable no-fallthrough */ -'use strict'; -const assert = require('assert'); -const v8 = require('v8-natives'); -const pify = require('.'); +import process from 'node:process'; +import assert from 'node:assert'; +import v8 from 'v8-natives'; +import pify from './index.js'; function assertOptimized(fn, name) { const status = v8.getOptimizationStatus(fn); @@ -27,19 +27,17 @@ function assertOptimized(fn, name) { } const fn = pify({ - unicorn: callback => { + unicorn(callback) { callback(null, 'unicorn'); - } + }, }); -(async () => { - try { - await fn.unicorn(); - v8.optimizeFunctionOnNextCall(fn.unicorn); - await fn.unicorn(); - assertOptimized(fn.unicorn, 'unicorn'); - } catch (error) { - console.error(error); - process.exit(1); // eslint-disable-line unicorn/no-process-exit - } -})(); +try { + await fn.unicorn(); + v8.optimizeFunctionOnNextCall(fn.unicorn); + await fn.unicorn(); + assertOptimized(fn.unicorn, 'unicorn'); +} catch (error) { + console.error(error); + process.exit(1); // eslint-disable-line unicorn/no-process-exit +} diff --git a/package.json b/package.json index ad75f0a..e8867e5 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,10 @@ "email": "sindresorhus@gmail.com", "url": "https://sindresorhus.com" }, + "type": "module", + "exports": "./index.js", "engines": { - "node": ">=10" + "node": ">=14.16" }, "scripts": { "test": "xo && ava", @@ -41,9 +43,9 @@ "bluebird" ], "devDependencies": { - "ava": "^2.4.0", - "pinkie-promise": "^2.0.0", - "v8-natives": "^1.1.0", - "xo": "^0.26.1" + "ava": "^4.3.0", + "pinkie-promise": "^2.0.1", + "v8-natives": "^1.2.5", + "xo": "^0.49.0" } } diff --git a/readme.md b/readme.md index cafb744..76becda 100644 --- a/readme.md +++ b/readme.md @@ -4,27 +4,25 @@ ## Install -``` -$ npm install pify +```sh +npm install pify ``` ## Usage ```js -const fs = require('fs'); -const pify = require('pify'); - -(async () => { - // Promisify a single function. - const data = await pify(fs.readFile)('package.json', 'utf8'); - console.log(JSON.parse(data).name); - //=> 'pify' - - // Promisify all methods in a module. - const data2 = await pify(fs).readFile('package.json', 'utf8'); - console.log(JSON.parse(data2).name); - //=> 'pify' -})(); +import fs from 'fs'; +import pify from 'pify'; + +// Promisify a single function. +const data = await pify(fs.readFile)('package.json', 'utf8'); +console.log(JSON.parse(data).name); +//=> 'pify' + +// Promisify all methods in a module. +const data2 = await pify(fs).readFile('package.json', 'utf8'); +console.log(JSON.parse(data2).name); +//=> 'pify' ``` ## API @@ -51,14 +49,12 @@ Default: `false` By default, the promisified function will only return the second argument from the callback, which works fine for most APIs. This option can be useful for modules like `request` that return multiple arguments. Turning this on will make it return an array of all arguments from the callback, excluding the error argument, instead of just the second argument. This also applies to rejections, where it returns an array of all the callback arguments, including the error. ```js -const request = require('request'); -const pify = require('pify'); +import request from 'request'; +import pify from 'pify'; const pRequest = pify(request, {multiArgs: true}); -(async () => { - const [httpResponse, body] = await pRequest('https://sindresorhus.com'); -})(); +const [httpResponse, body] = await pRequest('https://sindresorhus.com'); ``` ##### include @@ -82,7 +78,7 @@ Default: `false` If the given module is a function itself, it will be promisified. Enable this option if you want to promisify only methods of the module. ```js -const pify = require('pify'); +import pify from 'pify'; function fn() { return true; @@ -94,14 +90,12 @@ fn.method = (data, callback) => { }); }; -(async () => { - // Promisify methods but not `fn()`. - const promiseFn = pify(fn, {excludeMain: true}); +// Promisify methods but not `fn()`. +const promiseFn = pify(fn, {excludeMain: true}); - if (promiseFn()) { - console.log(await promiseFn.method('hi')); - } -})(); +if (promiseFn()) { + console.log(await promiseFn.method('hi')); +} ``` ##### errorFirst @@ -132,8 +126,8 @@ Custom promise module to use instead of the native one. Class methods are not bound, so when they're not called on the class itself, they don't have any context. You can either promisify the whole class or use `.bind()`. ```js -const pify = require('pify'); -const SomeClass = require('./some-class'); +import pify from 'pify'; +import SomeClass from './some-class.js'; const someInstance = new SomeClass(); diff --git a/test.js b/test.js index 06c19a7..56bd341 100644 --- a/test.js +++ b/test.js @@ -1,10 +1,9 @@ -/* eslint-disable promise/prefer-await-to-then */ -import util from 'util'; -import fs from 'fs'; -import stream from 'stream'; +import util from 'node:util'; +import fs from 'node:fs'; +import stream from 'node:stream'; import test from 'ava'; import pinkiePromise from 'pinkie-promise'; -import pify from '.'; +import pify from './index.js'; const fixture = callback => setImmediate(() => { callback(null, 'unicorn'); @@ -38,7 +37,7 @@ const fixture5 = () => 'rainbow'; const fixtureModule = { method1: fixture, method2: fixture, - method3: fixture5 + method3: fixture5, }; function FixtureGrandparent() {} @@ -120,8 +119,8 @@ test('module support - doesn\'t transform *Stream methods by default', t => { test('module support - preserves non-function members', t => { const module = { - method: () => {}, - nonMethod: 3 + method() {}, + nonMethod: 3, }; t.deepEqual(Object.keys(module), Object.keys(pify(module))); @@ -129,7 +128,7 @@ test('module support - preserves non-function members', t => { test('module support - transforms only members in options.include', t => { const pModule = pify(fixtureModule, { - include: ['method1', 'method2'] + include: ['method1', 'method2'], }); t.is(typeof pModule.method1().then, 'function'); @@ -139,7 +138,7 @@ test('module support - transforms only members in options.include', t => { test('module support - doesn\'t transform members in options.exclude', t => { const pModule = pify(fixtureModule, { - exclude: ['method3'] + exclude: ['method3'], }); t.is(typeof pModule.method1().then, 'function'); @@ -150,7 +149,7 @@ test('module support - doesn\'t transform members in options.exclude', t => { test('module support - options.include over options.exclude', t => { const pModule = pify(fixtureModule, { include: ['method1', 'method2'], - exclude: ['method2', 'method3'] + exclude: ['method2', 'method3'], }); t.is(typeof pModule.method1().then, 'function'); @@ -167,7 +166,7 @@ test('module support — function modules', t => { test('module support — function modules exclusion', t => { const pModule = pify(fixture4, { - excludeMain: true + excludeMain: true, }); t.is(typeof pModule.meow().then, 'function'); @@ -189,22 +188,22 @@ test('`errorFirst` option and `multiArgs`', async t => { t.deepEqual(await pify(fixture, { errorFirst: false, - multiArgs: true + multiArgs: true, })('🦄', '🌈'), ['🦄', '🌈']); }); test('class support - does not create a copy', async t => { - const obj = { + const object = { x: 'foo', y(callback) { setImmediate(() => { callback(null, this.x); }); - } + }, }; - const pified = pify(obj); - obj.x = 'bar'; + const pified = pify(object); + object.x = 'bar'; t.is(await pified.y(), 'bar'); t.is(pified.x, 'bar'); @@ -239,7 +238,7 @@ test('class support — respects inheritance order', async t => { test('class support - transforms only members in options.include, copies all', t => { const instance = new FixtureClass(); const pInstance = pify(instance, { - include: ['parentMethod1'] + include: ['parentMethod1'], }); t.is(typeof pInstance.parentMethod1().then, 'function'); @@ -250,7 +249,7 @@ test('class support - transforms only members in options.include, copies all', t test('class support - doesn\'t transform members in options.exclude', t => { const instance = new FixtureClass(); const pInstance = pify(instance, { - exclude: ['grandparentMethod1'] + exclude: ['grandparentMethod1'], }); t.not(typeof pInstance.grandparentMethod1(() => {}).then, 'function'); @@ -261,7 +260,7 @@ test('class support - options.include over options.exclude', t => { const instance = new FixtureClass(); const pInstance = pify(instance, { include: ['method1', 'parentMethod1'], - exclude: ['parentMethod1', 'grandparentMethod1'] + exclude: ['parentMethod1', 'grandparentMethod1'], }); t.is(typeof pInstance.method1().then, 'function'); @@ -280,7 +279,7 @@ test('method mutation', async t => { setImmediate(() => { callback(null, 'original'); }); - } + }, }; const pified = pify(object); @@ -296,9 +295,9 @@ test('symbol keys', async t => { const symbol = Symbol('symbol'); const object = { - [symbol]: callback => { + [symbol](callback) { setImmediate(callback); - } + }, }; const pified = pify(object); @@ -313,11 +312,11 @@ test('symbol keys', async t => { test('non-writable non-configurable property', t => { const object = {}; Object.defineProperty(object, 'prop', { - value: callback => { + value(callback) { setImmediate(callback); }, writable: false, - configurable: false + configurable: false, }); const pified = pify(object); @@ -343,7 +342,7 @@ test('do not break internal callback usage', async t => { bar(...arguments_) { const callback = arguments_.pop(); callback(null, 42); - } + }, }; t.is(await pify(object).foo(), 42); });