deprecate.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. type DeprecationHandler = (message: string) => void;
  2. let deprecationHandler: DeprecationHandler | null = null;
  3. export function warnOnce (oldName: string, newName?: string) {
  4. return warnOnceMessage(newName
  5. ? `'${oldName}' is deprecated and will be removed. Please use '${newName}' instead.`
  6. : `'${oldName}' is deprecated and will be removed.`);
  7. }
  8. export function warnOnceMessage (msg: string) {
  9. let warned = false;
  10. return () => {
  11. if (!warned && !process.noDeprecation) {
  12. warned = true;
  13. log(msg);
  14. }
  15. };
  16. }
  17. export function setHandler (handler: DeprecationHandler | null): void {
  18. deprecationHandler = handler;
  19. }
  20. export function getHandler (): DeprecationHandler | null {
  21. return deprecationHandler;
  22. }
  23. export function warn (oldName: string, newName: string): void {
  24. if (!process.noDeprecation) {
  25. log(`'${oldName}' is deprecated. Use '${newName}' instead.`);
  26. }
  27. }
  28. export function log (message: string): void {
  29. if (typeof deprecationHandler === 'function') {
  30. deprecationHandler(message);
  31. } else if (process.throwDeprecation) {
  32. throw new Error(message);
  33. } else if (process.traceDeprecation) {
  34. return console.trace(message);
  35. } else {
  36. return console.warn(`(electron) ${message}`);
  37. }
  38. }
  39. // remove a function with no replacement
  40. export function removeFunction<T extends Function> (fn: T, removedName: string): T {
  41. if (!fn) { throw new Error(`'${removedName} function' is invalid or does not exist.`); }
  42. // wrap the deprecated function to warn user
  43. const warn = warnOnce(`${fn.name} function`);
  44. return function (this: any) {
  45. warn();
  46. fn.apply(this, arguments);
  47. } as unknown as typeof fn;
  48. }
  49. // change the name of a function
  50. export function renameFunction<T extends Function> (fn: T, newName: string): T {
  51. const warn = warnOnce(`${fn.name} function`, `${newName} function`);
  52. return function (this: any) {
  53. warn();
  54. return fn.apply(this, arguments);
  55. } as unknown as typeof fn;
  56. }
  57. // change the name of an event
  58. export function event (emitter: NodeJS.EventEmitter, oldName: string, newName: string) {
  59. const warn = newName.startsWith('-') /* internal event */
  60. ? warnOnce(`${oldName} event`)
  61. : warnOnce(`${oldName} event`, `${newName} event`);
  62. return emitter.on(newName, function (this: NodeJS.EventEmitter, ...args) {
  63. if (this.listenerCount(oldName) !== 0) {
  64. warn();
  65. this.emit(oldName, ...args);
  66. }
  67. });
  68. }
  69. // remove a property with no replacement
  70. export function removeProperty<T, K extends (keyof T & string)>(object: T, removedName: K, onlyForValues?: any[]): T {
  71. // if the property's already been removed, warn about it
  72. // eslint-disable-next-line no-proto
  73. const info = Object.getOwnPropertyDescriptor((object as any).__proto__, removedName);
  74. if (!info) {
  75. log(`Unable to remove property '${removedName}' from an object that lacks it.`);
  76. return object;
  77. }
  78. if (!info.get || !info.set) {
  79. log(`Unable to remove property '${removedName}' from an object does not have a getter / setter`);
  80. return object;
  81. }
  82. // wrap the deprecated property in an accessor to warn
  83. const warn = warnOnce(removedName);
  84. return Object.defineProperty(object, removedName, {
  85. configurable: true,
  86. get: () => {
  87. warn();
  88. return info.get!.call(object);
  89. },
  90. set: newVal => {
  91. if (!onlyForValues || onlyForValues.includes(newVal)) {
  92. warn();
  93. }
  94. return info.set!.call(object, newVal);
  95. }
  96. });
  97. }
  98. // change the name of a property
  99. export function renameProperty<T extends Object, K extends (keyof T & string)>(object: T, oldName: string, newName: K): T {
  100. const warn = warnOnce(oldName, newName);
  101. // if the new property isn't there yet,
  102. // inject it and warn about it
  103. if ((oldName in object) && !(newName in object)) {
  104. warn();
  105. object[newName] = (object as any)[oldName];
  106. }
  107. // wrap the deprecated property in an accessor to warn
  108. // and redirect to the new property
  109. return Object.defineProperty(object, oldName, {
  110. get: () => {
  111. warn();
  112. return object[newName];
  113. },
  114. set: value => {
  115. warn();
  116. object[newName] = value;
  117. }
  118. });
  119. }
  120. export function moveAPI<T extends Function> (fn: T, oldUsage: string, newUsage: string): T {
  121. const warn = warnOnce(oldUsage, newUsage);
  122. return function (this: any) {
  123. warn();
  124. return fn.apply(this, arguments);
  125. } as unknown as typeof fn;
  126. }