From 44f1358b587a4dd8e9f4c2004936953c0b116e09 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Fri, 26 Jan 2024 13:16:44 +0800 Subject: [PATCH] feat: add `util.promisify` --- DOWNLOAD_STATS.md | 1 + create.ts | 3 +- package.json | 2 + packages/data/es-shim-like/package.json | 1 + .../data/es-shim-like/src/util.promisify.ts | 135 ++++++++++++++++++ packages/generated/util.promisify/auto.js | 2 + packages/generated/util.promisify/entry.js | 7 + .../util.promisify/implementation.js | 2 + packages/generated/util.promisify/index.js | 2 + .../generated/util.promisify/package.json | 22 +++ packages/generated/util.promisify/polyfill.js | 2 + packages/generated/util.promisify/shim.js | 2 + packages/tools/cli/src/all-packages.ts | 1 + pnpm-lock.yaml | 13 ++ turbo.json | 3 +- 15 files changed, 196 insertions(+), 2 deletions(-) create mode 100644 packages/data/es-shim-like/src/util.promisify.ts create mode 100644 packages/generated/util.promisify/auto.js create mode 100644 packages/generated/util.promisify/entry.js create mode 100644 packages/generated/util.promisify/implementation.js create mode 100644 packages/generated/util.promisify/index.js create mode 100644 packages/generated/util.promisify/package.json create mode 100644 packages/generated/util.promisify/polyfill.js create mode 100644 packages/generated/util.promisify/shim.js diff --git a/DOWNLOAD_STATS.md b/DOWNLOAD_STATS.md index b27e970e..63fccbb0 100644 --- a/DOWNLOAD_STATS.md +++ b/DOWNLOAD_STATS.md @@ -94,5 +94,6 @@ | `@nolyfill/typed-array-length` | [![npm](https://img.shields.io/npm/dt/@nolyfill/typed-array-length.svg?style=flat-square&logo=npm&logoColor=white&label=total%20downloads&color=333)](https://www.npmjs.com/package/@nolyfill/typed-array-length) | | `@nolyfill/typedarray` | [![npm](https://img.shields.io/npm/dt/@nolyfill/typedarray.svg?style=flat-square&logo=npm&logoColor=white&label=total%20downloads&color=333)](https://www.npmjs.com/package/@nolyfill/typedarray) | | `@nolyfill/unbox-primitive` | [![npm](https://img.shields.io/npm/dt/@nolyfill/unbox-primitive.svg?style=flat-square&logo=npm&logoColor=white&label=total%20downloads&color=333)](https://www.npmjs.com/package/@nolyfill/unbox-primitive) | +| `@nolyfill/util.promisify` | [![npm](https://img.shields.io/npm/dt/@nolyfill/util.promisify.svg?style=flat-square&logo=npm&logoColor=white&label=total%20downloads&color=333)](https://www.npmjs.com/package/@nolyfill/util.promisify) | | `@nolyfill/which-boxed-primitive` | [![npm](https://img.shields.io/npm/dt/@nolyfill/which-boxed-primitive.svg?style=flat-square&logo=npm&logoColor=white&label=total%20downloads&color=333)](https://www.npmjs.com/package/@nolyfill/which-boxed-primitive) | | `@nolyfill/which-typed-array` | [![npm](https://img.shields.io/npm/dt/@nolyfill/which-typed-array.svg?style=flat-square&logo=npm&logoColor=white&label=total%20downloads&color=333)](https://www.npmjs.com/package/@nolyfill/which-typed-array) | \ No newline at end of file diff --git a/create.ts b/create.ts index c691eae5..1f439824 100644 --- a/create.ts +++ b/create.ts @@ -86,7 +86,8 @@ const autoGeneratedPackagesList = [ ['es-aggregate-error'], ['promise.any', { '@nolyfill/es-aggregate-error': 'workspace:*' }, '>=12.4.0'], ['promise.allsettled'], - ['array.prototype.toreversed'] + ['array.prototype.toreversed'], + ['util.promisify', { '@nolyfill/safe-array-concat': 'workspace:*' }, '>=12.4.0'] ] as const; const singleFilePackagesList = [ diff --git a/package.json b/package.json index e949d739..5c84ad53 100644 --- a/package.json +++ b/package.json @@ -131,6 +131,7 @@ "typed-array-length": "workspace:@nolyfill/typed-array-length@*", "typedarray": "workspace:@nolyfill/typedarray@*", "unbox-primitive": "workspace:@nolyfill/unbox-primitive@*", + "util.promisify": "workspace:@nolyfill/util.promisify@*", "which-boxed-primitive": "workspace:@nolyfill/which-boxed-primitive@*", "which-typed-array": "workspace:@nolyfill/which-typed-array@*" }, @@ -234,6 +235,7 @@ "typed-array-length": "npm:@nolyfill/typed-array-length@latest", "typedarray": "npm:@nolyfill/typedarray@latest", "unbox-primitive": "npm:@nolyfill/unbox-primitive@latest", + "util.promisify": "npm:@nolyfill/util.promisify@latest", "which-boxed-primitive": "npm:@nolyfill/which-boxed-primitive@latest", "which-typed-array": "npm:@nolyfill/which-typed-array@latest" } diff --git a/packages/data/es-shim-like/package.json b/packages/data/es-shim-like/package.json index 41f3876a..263e53d9 100644 --- a/packages/data/es-shim-like/package.json +++ b/packages/data/es-shim-like/package.json @@ -8,6 +8,7 @@ "license": "MIT", "dependencies": { "@nolyfill/es-aggregate-error": "workspace:*", + "@nolyfill/safe-array-concat": "workspace:*", "@nolyfill/shared": "workspace:*" } } diff --git a/packages/data/es-shim-like/src/util.promisify.ts b/packages/data/es-shim-like/src/util.promisify.ts new file mode 100644 index 00000000..bed89338 --- /dev/null +++ b/packages/data/es-shim-like/src/util.promisify.ts @@ -0,0 +1,135 @@ +'use strict'; + +import type { CustomPromisify } from 'util'; + +import { defineEsShim } from '@nolyfill/shared'; +import safeConcat from '@nolyfill/safe-array-concat'; + +const kCustomPromisifiedSymbol = Symbol.for('nodejs.util.promisify.custom'); +const kCustomPromisifyArgsSymbol = Symbol('customPromisifyArgs'); + +// eslint-disable-next-line @typescript-eslint/ban-types -- overload signature +function promisify(fn: CustomPromisify): TCustom; + +function promisify( + fn: (callback: (err: any, result: TResult) => void) => void, +): () => Promise; +function promisify(fn: (callback: (err?: any) => void) => void): () => Promise; + +function promisify( + fn: (arg1: T1, callback: (err: any, result: TResult) => void) => void, +): (arg1: T1) => Promise; +function promisify(fn: (arg1: T1, callback: (err?: any) => void) => void): (arg1: T1) => Promise; + +function promisify( + fn: (arg1: T1, arg2: T2, callback: (err: any, result: TResult) => void) => void, +): (arg1: T1, arg2: T2) => Promise; +function promisify( + fn: (arg1: T1, arg2: T2, callback: (err?: any) => void) => void, +): (arg1: T1, arg2: T2) => Promise; + +function promisify( + fn: (arg1: T1, arg2: T2, arg3: T3, callback: (err: any, result: TResult) => void) => void, +): (arg1: T1, arg2: T2, arg3: T3) => Promise; +function promisify( + fn: (arg1: T1, arg2: T2, arg3: T3, callback: (err?: any) => void) => void, +): (arg1: T1, arg2: T2, arg3: T3) => Promise; + +function promisify( + fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, callback: (err: any, result: TResult) => void) => void, +): (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => Promise; +function promisify( + fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, callback: (err?: any) => void) => void, +): (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => Promise; + +function promisify( + fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, callback: (err: any, result: TResult) => void) => void, +): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) => Promise; +function promisify( + fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, callback: (err?: any) => void) => void, +): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) => Promise; + +// eslint-disable-next-line @typescript-eslint/ban-types -- overload signature +function promisify(orig: Function): Function { + if (typeof orig !== 'function') { + const error = new TypeError('The "original" argument must be of type function') as NodeJS.ErrnoException; + error.code = 'ERR_INVALID_ARG_TYPE'; + error.toString = function value() { + return `${this.name}[${this.code}]: ${this.message}`; + }; + throw error; + } + + if (kCustomPromisifiedSymbol in orig && orig[kCustomPromisifiedSymbol]) { + const customFunction = orig[kCustomPromisifiedSymbol]; + if (typeof customFunction !== 'function') { + const customError = TypeError('The [util.promisify.custom] property must be of type function.') as NodeJS.ErrnoException; + customError.code = 'ERR_INVALID_ARG_TYPE'; + customError.toString = function value() { + return `${this.name}[${this.code}]: ${this.message}`; + }; + throw customError; + } + Object.defineProperty(customFunction, kCustomPromisifiedSymbol, { + configurable: true, + enumerable: false, + value: customFunction, + writable: false + }); + return customFunction; + } + + // Names to create an object from in case the callback receives multiple + // arguments, e.g. ['stdout', 'stderr'] for child_process.exec. + const argumentNames = kCustomPromisifyArgsSymbol in orig && (orig[kCustomPromisifyArgsSymbol] as ArrayLike | undefined); + + const promisified = function fn(this: any, ...args: any[]) { + // eslint-disable-next-line @typescript-eslint/no-this-alias -- wrap fn + const self = this; + return new Promise((resolve, reject) => { + orig.apply(self, safeConcat(args, (err: any) => { + const values = args.length > 1 ? args.slice(1) : []; + if (err) { + reject(err); + } else if (argumentNames && typeof argumentNames !== 'undefined' && values.length > 1) { + const obj: Record = {}; + Array.prototype.forEach.call(argumentNames, (name: string, index: number) => { + obj[name] = values[index]; + }); + resolve(obj); + } else { + resolve(values[0]); + } + })); + }); + }; + + Object.setPrototypeOf(promisified, Object.getPrototypeOf(orig)); + + Object.defineProperty(promisified, kCustomPromisifiedSymbol, { + configurable: true, + enumerable: false, + value: promisified, + writable: false + }); + const descriptors = Object.getOwnPropertyDescriptors(orig); + Array.prototype.forEach.call(descriptors, (k, v) => { + try { + Object.defineProperty(promisified, k, v); + } catch { + // handle nonconfigurable function properties + } + }); + return promisified; +} + +promisify.custom = kCustomPromisifiedSymbol; + +export default defineEsShim(promisify, true); +/** + * @deprecated + * Not exposed by native `util.promisify` or supported by browserify's `util.promisify`. + * + * Use `util.promisify.custom` instead. + */ +export const customPromisifyArgs = kCustomPromisifyArgsSymbol; diff --git a/packages/generated/util.promisify/auto.js b/packages/generated/util.promisify/auto.js new file mode 100644 index 00000000..318c0bb9 --- /dev/null +++ b/packages/generated/util.promisify/auto.js @@ -0,0 +1,2 @@ +'use strict'; +/* noop */ diff --git a/packages/generated/util.promisify/entry.js b/packages/generated/util.promisify/entry.js new file mode 100644 index 00000000..6d4eb729 --- /dev/null +++ b/packages/generated/util.promisify/entry.js @@ -0,0 +1,7 @@ +"use strict";var e;Object.defineProperty(exports,"__esModule",{value:!0}),function(e,t){for(var r in t)Object.defineProperty(e,r,{enumerable:!0,get:t[r]})}(exports,{customPromisifyArgs:function(){return l},default:function(){return f}});const t=require("@nolyfill/shared"),r=(e=require("@nolyfill/safe-array-concat"))&&e.__esModule?e:{default:e},o=Symbol.for("nodejs.util.promisify.custom"),i=Symbol("customPromisifyArgs");// eslint-disable-next-line @typescript-eslint/ban-types -- overload signature +function n(e){if("function"!=typeof e){let e=TypeError('The "original" argument must be of type function');throw e.code="ERR_INVALID_ARG_TYPE",e.toString=function(){return`${this.name}[${this.code}]: ${this.message}`},e}if(o in e&&e[o]){let t=e[o];if("function"!=typeof t){let e=TypeError("The [util.promisify.custom] property must be of type function.");throw e.code="ERR_INVALID_ARG_TYPE",e.toString=function(){return`${this.name}[${this.code}]: ${this.message}`},e}return Object.defineProperty(t,o,{configurable:!0,enumerable:!1,value:t,writable:!1}),t}// Names to create an object from in case the callback receives multiple +// arguments, e.g. ['stdout', 'stderr'] for child_process.exec. +let t=i in e&&e[i],n=function(...o){// eslint-disable-next-line @typescript-eslint/no-this-alias -- wrap fn +let i=this;return new Promise((n,f)=>{e.apply(i,(0,r.default)(o,e=>{let r=o.length>1?o.slice(1):[];if(e)f(e);else if(t&&void 0!==t&&r.length>1){let e={};Array.prototype.forEach.call(t,(t,o)=>{e[t]=r[o]}),n(e)}else n(r[0])}))})};Object.setPrototypeOf(n,Object.getPrototypeOf(e)),Object.defineProperty(n,o,{configurable:!0,enumerable:!1,value:n,writable:!1});let f=Object.getOwnPropertyDescriptors(e);return Array.prototype.forEach.call(f,(e,t)=>{try{Object.defineProperty(n,e,t)}catch(e){// handle nonconfigurable function properties +}}),n}n.custom=o;const f=(0,t.defineEsShim)(n,!0),l=i; +Object.assign(exports.default, exports); module.exports = exports.default; diff --git a/packages/generated/util.promisify/implementation.js b/packages/generated/util.promisify/implementation.js new file mode 100644 index 00000000..34019673 --- /dev/null +++ b/packages/generated/util.promisify/implementation.js @@ -0,0 +1,2 @@ +'use strict'; +module.exports = require('./entry.js').implementation; diff --git a/packages/generated/util.promisify/index.js b/packages/generated/util.promisify/index.js new file mode 100644 index 00000000..563021d7 --- /dev/null +++ b/packages/generated/util.promisify/index.js @@ -0,0 +1,2 @@ +'use strict'; +module.exports = require('./entry.js').index(); diff --git a/packages/generated/util.promisify/package.json b/packages/generated/util.promisify/package.json new file mode 100644 index 00000000..0d9f112e --- /dev/null +++ b/packages/generated/util.promisify/package.json @@ -0,0 +1,22 @@ +{ + "name": "@nolyfill/util.promisify", + "version": "1.0.28", + "repository": { + "type": "git", + "url": "https://github.com/SukkaW/nolyfill", + "directory": "packages/generated/util.promisify" + }, + "main": "./index.js", + "license": "MIT", + "files": [ + "*.js" + ], + "scripts": {}, + "dependencies": { + "@nolyfill/safe-array-concat": "workspace:*", + "@nolyfill/shared": "workspace:*" + }, + "engines": { + "node": ">=12.4.0" + } +} diff --git a/packages/generated/util.promisify/polyfill.js b/packages/generated/util.promisify/polyfill.js new file mode 100644 index 00000000..4166abf3 --- /dev/null +++ b/packages/generated/util.promisify/polyfill.js @@ -0,0 +1,2 @@ +'use strict'; +module.exports = require('./entry.js').polyfill; diff --git a/packages/generated/util.promisify/shim.js b/packages/generated/util.promisify/shim.js new file mode 100644 index 00000000..301a98bd --- /dev/null +++ b/packages/generated/util.promisify/shim.js @@ -0,0 +1,2 @@ +'use strict'; +module.exports = require('./entry.js').shim; diff --git a/packages/tools/cli/src/all-packages.ts b/packages/tools/cli/src/all-packages.ts index 4fdd90dc..d53a09bd 100644 --- a/packages/tools/cli/src/all-packages.ts +++ b/packages/tools/cli/src/all-packages.ts @@ -93,6 +93,7 @@ export const allPackages = [ "typed-array-length", "typedarray", "unbox-primitive", + "util.promisify", "which-boxed-primitive", "which-typed-array" ]; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 63e63c36..b4e16826 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -96,6 +96,7 @@ overrides: typed-array-length: workspace:@nolyfill/typed-array-length@* typedarray: workspace:@nolyfill/typedarray@* unbox-primitive: workspace:@nolyfill/unbox-primitive@* + util.promisify: workspace:@nolyfill/util.promisify@* which-boxed-primitive: workspace:@nolyfill/which-boxed-primitive@* which-typed-array: workspace:@nolyfill/which-typed-array@* @@ -189,6 +190,9 @@ importers: '@nolyfill/es-aggregate-error': specifier: workspace:* version: link:../../generated/es-aggregate-error + '@nolyfill/safe-array-concat': + specifier: workspace:* + version: link:../../generated/safe-array-concat '@nolyfill/shared': specifier: workspace:* version: link:../../tools/shared @@ -701,6 +705,15 @@ importers: packages/generated/unbox-primitive: {} + packages/generated/util.promisify: + dependencies: + '@nolyfill/safe-array-concat': + specifier: workspace:* + version: link:../safe-array-concat + '@nolyfill/shared': + specifier: workspace:* + version: link:../../tools/shared + packages/generated/which-boxed-primitive: {} packages/generated/which-typed-array: diff --git a/turbo.json b/turbo.json index 627e5bb2..da65a5b6 100644 --- a/turbo.json +++ b/turbo.json @@ -13,7 +13,8 @@ }, "//#codegen": { "inputs": [ - "create.ts" + "create.ts", + "packages/data/**" ] }, "build": {