1 | const copyProperty = (to, from, property, ignoreNonConfigurable) => {
|
2 |
|
3 |
|
4 | if (property === 'length' || property === 'prototype') {
|
5 | return;
|
6 | }
|
7 |
|
8 |
|
9 | if (property === 'arguments' || property === 'caller') {
|
10 | return;
|
11 | }
|
12 |
|
13 | const toDescriptor = Object.getOwnPropertyDescriptor(to, property);
|
14 | const fromDescriptor = Object.getOwnPropertyDescriptor(from, property);
|
15 |
|
16 | if (!canCopyProperty(toDescriptor, fromDescriptor) && ignoreNonConfigurable) {
|
17 | return;
|
18 | }
|
19 |
|
20 | Object.defineProperty(to, property, fromDescriptor);
|
21 | };
|
22 |
|
23 |
|
24 |
|
25 |
|
26 | const canCopyProperty = function (toDescriptor, fromDescriptor) {
|
27 | return toDescriptor === undefined || toDescriptor.configurable || (
|
28 | toDescriptor.writable === fromDescriptor.writable &&
|
29 | toDescriptor.enumerable === fromDescriptor.enumerable &&
|
30 | toDescriptor.configurable === fromDescriptor.configurable &&
|
31 | (toDescriptor.writable || toDescriptor.value === fromDescriptor.value)
|
32 | );
|
33 | };
|
34 |
|
35 | const changePrototype = (to, from) => {
|
36 | const fromPrototype = Object.getPrototypeOf(from);
|
37 | if (fromPrototype === Object.getPrototypeOf(to)) {
|
38 | return;
|
39 | }
|
40 |
|
41 | Object.setPrototypeOf(to, fromPrototype);
|
42 | };
|
43 |
|
44 | const wrappedToString = (withName, fromBody) => `/* Wrapped ${withName}*/\n${fromBody}`;
|
45 |
|
46 | const toStringDescriptor = Object.getOwnPropertyDescriptor(Function.prototype, 'toString');
|
47 | const toStringName = Object.getOwnPropertyDescriptor(Function.prototype.toString, 'name');
|
48 |
|
49 |
|
50 |
|
51 |
|
52 | const changeToString = (to, from, name) => {
|
53 | const withName = name === '' ? '' : `with ${name.trim()}() `;
|
54 | const newToString = wrappedToString.bind(null, withName, from.toString());
|
55 |
|
56 | Object.defineProperty(newToString, 'name', toStringName);
|
57 | Object.defineProperty(to, 'toString', {...toStringDescriptor, value: newToString});
|
58 | };
|
59 |
|
60 | export default function mimicFunction(to, from, {ignoreNonConfigurable = false} = {}) {
|
61 | const {name} = to;
|
62 |
|
63 | for (const property of Reflect.ownKeys(from)) {
|
64 | copyProperty(to, from, property, ignoreNonConfigurable);
|
65 | }
|
66 |
|
67 | changePrototype(to, from);
|
68 | changeToString(to, from, name);
|
69 |
|
70 | return to;
|
71 | }
|