Repository: nodejs/node
Pull request: lib: add options to util.deprecate

Add options parameter to `util.deprecate` with modifyPrototype to conditionally modify the prototype of the deprecated object.

cc: @legendecas 

Changed files:
filename: doc/api/util.md
patch: @@ -225,11 +225,15 @@ added: v14.9.0
 Alias for `util.debuglog`. Usage allows for readability of that doesn't imply
 logging when only using `util.debuglog().enabled`.
 
-## `util.deprecate(fn, msg[, code])`
+## `util.deprecate(fn, msg[, code[, options]])`
 
 <!-- YAML
 added: v0.8.0
 changes:
+  - version: REPLACEME
+    pr-url: https://github.com/nodejs/node/pull/59982
+    description: Add options object with modifyPrototype to conditionally
+                 modify the prototype of the deprecated object.
   - version: v10.0.0
     pr-url: https://github.com/nodejs/node/pull/16393
     description: Deprecation warnings are only emitted once for each code.
@@ -240,6 +244,10 @@ changes:
   invoked.
 * `code` {string} A deprecation code. See the [list of deprecated APIs][] for a
   list of codes.
+* `options` {Object}
+  * `modifyPrototype` {boolean} When false do not change the prototype of object
+    while emitting the deprecation warning.
+    **Default:** `true`.
 * Returns: {Function} The deprecated function wrapped to emit a warning.
 
 The `util.deprecate()` method wraps `fn` (which may be a function or class) in

filename: lib/util.js
patch: @@ -84,7 +84,7 @@ const { getOptionValue } = require('internal/options');
 const binding = internalBinding('util');
 
 const {
-  deprecate,
+  deprecate: internalDeprecate,
   getLazy,
   getSystemErrorMap,
   getSystemErrorName: internalErrorName,
@@ -459,13 +459,18 @@ function getCallSites(frameCount = 10, options) {
   return binding.getCallSites(frameCount);
 };
 
+// Public util.deprecate API
+function deprecate(fn, msg, code, { modifyPrototype } = {}) {
+  return internalDeprecate(fn, msg, code, undefined, modifyPrototype);
+}
+
 // Keep the `exports =` so that various functions can still be monkeypatched
 module.exports = {
   _errnoException,
   _exceptionWithHostPort,
-  _extend: deprecate(_extend,
-                     'The `util._extend` API is deprecated. Please use Object.assign() instead.',
-                     'DEP0060'),
+  _extend: internalDeprecate(_extend,
+                             'The `util._extend` API is deprecated. Please use Object.assign() instead.',
+                             'DEP0060'),
   callbackify,
   debug: debuglog,
   debuglog,
@@ -479,9 +484,9 @@ module.exports = {
   getSystemErrorMessage,
   inherits,
   inspect,
-  isArray: deprecate(ArrayIsArray,
-                     'The `util.isArray` API is deprecated. Please use `Array.isArray()` instead.',
-                     'DEP0044'),
+  isArray: internalDeprecate(ArrayIsArray,
+                             'The `util.isArray` API is deprecated. Please use `Array.isArray()` instead.',
+                             'DEP0044'),
   isDeepStrictEqual(a, b, skipPrototype) {
     if (internalDeepEqual === undefined) {
       internalDeepEqual = require('internal/util/comparisons').isDeepStrictEqual;

filename: test/parallel/test-util-deprecate.js
patch: @@ -61,6 +61,28 @@ for (const fn of [
   fn2();
 }
 
+
+// Test modifyPrototype option
+{
+  const msg = 'prototype-test';
+  const code = 'proto-code';
+
+  function OriginalFn() {}
+  OriginalFn.prototype.testMethod = function() { return 'test'; };
+
+  const deprecatedWithoutProto = util.deprecate(OriginalFn, msg, code, { modifyPrototype: false });
+
+  assert.notStrictEqual(deprecatedWithoutProto.prototype, OriginalFn.prototype);
+  assert.notStrictEqual(Object.getPrototypeOf(deprecatedWithoutProto), OriginalFn);
+  assert.strictEqual(deprecatedWithoutProto.prototype.testMethod, undefined);
+
+  const deprecatedWithProto = util.deprecate(OriginalFn, msg, code);
+
+  assert.strictEqual(deprecatedWithProto.prototype, OriginalFn.prototype);
+  assert.strictEqual(Object.getPrototypeOf(deprecatedWithProto), OriginalFn);
+  assert.strictEqual(typeof deprecatedWithProto.prototype.testMethod, 'function');
+}
+
 process.on('warning', (warning) => {
   assert.strictEqual(warning.name, 'DeprecationWarning');
   assert.ok(expectedWarnings.has(warning.message));