Skip to content

util polyfill does not include callbackify, promisify and other utilities #38

@giuseppeg

Description

@giuseppeg

Hi there

Firstly, thanks for your work on this project! 🙂

It seems that the util polyfill is outdated and doesn't include callbackify. It would be awesome if you could add it or depend on node-util directly.

FWIW @remorses @ryanflorence I ended up discovering this because I use remix-run which uses @esbuild-plugins/node-modules-polyfill. Specifically web3.js uses callbackify somewhere in their codebase.

Here's the diff.

diff --git a/node_modules/rollup-plugin-node-polyfills/polyfills/util.js b/node_modules/rollup-plugin-node-polyfills/polyfills/util.js
index 2194380..a00ee54 100644
--- a/node_modules/rollup-plugin-node-polyfills/polyfills/util.js
+++ b/node_modules/rollup-plugin-node-polyfills/polyfills/util.js
@@ -18,6 +18,17 @@
 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 // USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors ||
+  function getOwnPropertyDescriptors(obj) {
+    var keys = Object.keys(obj);
+    var descriptors = {};
+    for (var i = 0; i < keys.length; i++) {
+      descriptors[keys[i]] = Object.getOwnPropertyDescriptor(obj, keys[i]);
+    }
+    return descriptors;
+  };
+
 import process from 'process';
 var formatRegExp = /%[sdj%]/g;
 export function format(f) {
@@ -572,6 +583,54 @@ function hasOwnProperty(obj, prop) {
   return Object.prototype.hasOwnProperty.call(obj, prop);
 }
 
+function callbackifyOnRejected(reason, cb) {
+  // `!reason` guard inspired by bluebird (Ref: https://goo.gl/t5IS6M).
+  // Because `null` is a special error value in callbacks which means "no error
+  // occurred", we error-wrap so the callback consumer can distinguish between
+  // "the promise rejected with null" or "the promise fulfilled with undefined".
+  if (!reason) {
+    var newReason = new Error('Promise was rejected with a falsy value');
+    newReason.reason = reason;
+    reason = newReason;
+  }
+  return cb(reason);
+}
+
+export function callbackify(original) {
+  if (typeof original !== 'function') {
+    throw new TypeError('The "original" argument must be of type Function');
+  }
+
+  // We DO NOT return the promise as it gives the user a false sense that
+  // the promise is actually somehow related to the callback's execution
+  // and that the callback throwing will reject the promise.
+  function callbackified() {
+    var args = [];
+    for (var i = 0; i < arguments.length; i++) {
+      args.push(arguments[i]);
+    }
+
+    var maybeCb = args.pop();
+    if (typeof maybeCb !== 'function') {
+      throw new TypeError('The last argument must be of type Function');
+    }
+    var self = this;
+    var cb = function() {
+      return maybeCb.apply(self, arguments);
+    };
+    // In true node style we process the callback on `nextTick` with all the
+    // implications (stack, `uncaughtException`, `async_hooks`)
+    original.apply(this, args)
+      .then(function(ret) { process.nextTick(cb.bind(null, null, ret)) },
+            function(rej) { process.nextTick(callbackifyOnRejected.bind(null, rej, cb)) });
+  }
+
+  Object.setPrototypeOf(callbackified, Object.getPrototypeOf(original));
+  Object.defineProperties(callbackified,
+                          getOwnPropertyDescriptors(original));
+  return callbackified;
+}
+
 export default {
   inherits: inherits,
   _extend: _extend,
@@ -594,5 +653,6 @@ export default {
   inspect: inspect,
   deprecate: deprecate,
   format: format,
-  debuglog: debuglog
+  debuglog: debuglog,
+  callbackify: callbackify,
 }

This issue body was partially generated by patch-package.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions