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