1 | import WeakMap from 'core-js/library/es6/weak-map';
|
2 | import createPrototypeProxy from './createPrototypeProxy';
|
3 | import bindAutoBindMethods from './bindAutoBindMethods';
|
4 | import deleteUnknownAutoBindMethods from './deleteUnknownAutoBindMethods';
|
5 | import supportsProtoAssignment from './supportsProtoAssignment';
|
6 |
|
7 | const RESERVED_STATICS = [
|
8 | 'length',
|
9 | 'name',
|
10 | 'arguments',
|
11 | 'caller',
|
12 | 'prototype',
|
13 | 'toString'
|
14 | ];
|
15 |
|
16 | function isEqualDescriptor(a, b) {
|
17 | if (!a && !b) {
|
18 | return true;
|
19 | }
|
20 | if (!a || !b) {
|
21 | return false;
|
22 | }
|
23 | for (let key in a) {
|
24 | if (a[key] !== b[key]) {
|
25 | return false;
|
26 | }
|
27 | }
|
28 | return true;
|
29 | }
|
30 |
|
31 | let allProxies = new WeakMap();
|
32 |
|
33 | function proxyClass(InitialComponent) {
|
34 |
|
35 |
|
36 | if (allProxies.has(InitialComponent)) {
|
37 | return allProxies.get(InitialComponent);
|
38 | }
|
39 |
|
40 | let CurrentComponent;
|
41 | let ProxyComponent;
|
42 |
|
43 | let staticDescriptors = {};
|
44 | function wasStaticModifiedByUser(key) {
|
45 |
|
46 | const currentDescriptor = Object.getOwnPropertyDescriptor(ProxyComponent, key);
|
47 | return !isEqualDescriptor(staticDescriptors[key], currentDescriptor);
|
48 | }
|
49 |
|
50 | function instantiate(factory, context, params) {
|
51 | const component = factory();
|
52 |
|
53 | try {
|
54 | return component.apply(context, params);
|
55 | } catch (err) {
|
56 |
|
57 | const instance = new component(...params);
|
58 |
|
59 | Object.keys(instance).forEach(key => {
|
60 | if (RESERVED_STATICS.indexOf(key) > -1) {
|
61 | return;
|
62 | }
|
63 | context[key] = instance[key];
|
64 | })
|
65 | }
|
66 | }
|
67 |
|
68 | try {
|
69 |
|
70 | ProxyComponent = new Function('factory', 'instantiate',
|
71 | `return function ${InitialComponent.name || 'ProxyComponent'}() {
|
72 | return instantiate(factory, this, arguments);
|
73 | }`
|
74 | )(() => CurrentComponent, instantiate);
|
75 | } catch (err) {
|
76 |
|
77 | ProxyComponent = function () {
|
78 | return instantiate(() => CurrentComponent, this, arguments);
|
79 | };
|
80 | }
|
81 |
|
82 |
|
83 | ProxyComponent.toString = function toString() {
|
84 | return CurrentComponent.toString();
|
85 | };
|
86 |
|
87 | let prototypeProxy;
|
88 | if (InitialComponent.prototype && InitialComponent.prototype.isReactComponent) {
|
89 |
|
90 | prototypeProxy = createPrototypeProxy();
|
91 | ProxyComponent.prototype = prototypeProxy.get();
|
92 | }
|
93 |
|
94 | function update(NextComponent) {
|
95 | if (typeof NextComponent !== 'function') {
|
96 | throw new Error('Expected a constructor.');
|
97 | }
|
98 |
|
99 |
|
100 | if (allProxies.has(NextComponent)) {
|
101 | return update(allProxies.get(NextComponent).__getCurrent());
|
102 | }
|
103 |
|
104 |
|
105 | CurrentComponent = NextComponent;
|
106 |
|
107 |
|
108 | ProxyComponent.displayName = NextComponent.displayName || NextComponent.name;
|
109 |
|
110 |
|
111 | ProxyComponent.__proto__ = NextComponent.__proto__;
|
112 |
|
113 |
|
114 | Object.getOwnPropertyNames(NextComponent).forEach(key => {
|
115 | if (RESERVED_STATICS.indexOf(key) > -1) {
|
116 | return;
|
117 | }
|
118 |
|
119 | const staticDescriptor = {
|
120 | ...Object.getOwnPropertyDescriptor(NextComponent, key),
|
121 | configurable: true
|
122 | };
|
123 |
|
124 |
|
125 | if (!wasStaticModifiedByUser(key)) {
|
126 | Object.defineProperty(ProxyComponent, key, staticDescriptor);
|
127 | staticDescriptors[key] = staticDescriptor;
|
128 | }
|
129 | });
|
130 |
|
131 |
|
132 | Object.getOwnPropertyNames(ProxyComponent).forEach(key => {
|
133 | if (RESERVED_STATICS.indexOf(key) > -1) {
|
134 | return;
|
135 | }
|
136 |
|
137 |
|
138 | if (NextComponent.hasOwnProperty(key)) {
|
139 | return;
|
140 | }
|
141 |
|
142 |
|
143 | const descriptor = Object.getOwnPropertyDescriptor(ProxyComponent, key);
|
144 | if (descriptor && !descriptor.configurable) {
|
145 | return;
|
146 | }
|
147 |
|
148 |
|
149 | if (!wasStaticModifiedByUser(key)) {
|
150 | delete ProxyComponent[key];
|
151 | delete staticDescriptors[key];
|
152 | }
|
153 | });
|
154 |
|
155 | if (prototypeProxy) {
|
156 |
|
157 | const mountedInstances = prototypeProxy.update(NextComponent.prototype);
|
158 |
|
159 |
|
160 | ProxyComponent.prototype.constructor = ProxyComponent;
|
161 |
|
162 |
|
163 | mountedInstances.forEach(bindAutoBindMethods);
|
164 | mountedInstances.forEach(deleteUnknownAutoBindMethods);
|
165 | }
|
166 | };
|
167 |
|
168 | function get() {
|
169 | return ProxyComponent;
|
170 | }
|
171 |
|
172 | function getCurrent() {
|
173 | return CurrentComponent;
|
174 | }
|
175 |
|
176 | update(InitialComponent);
|
177 |
|
178 | const proxy = { get, update };
|
179 | allProxies.set(ProxyComponent, proxy);
|
180 |
|
181 | Object.defineProperty(proxy, '__getCurrent', {
|
182 | configurable: false,
|
183 | writable: false,
|
184 | enumerable: false,
|
185 | value: getCurrent
|
186 | });
|
187 |
|
188 | return proxy;
|
189 | }
|
190 |
|
191 | function createFallback(Component) {
|
192 | let CurrentComponent = Component;
|
193 |
|
194 | return {
|
195 | get() {
|
196 | return CurrentComponent;
|
197 | },
|
198 | update(NextComponent) {
|
199 | CurrentComponent = NextComponent;
|
200 | }
|
201 | };
|
202 | }
|
203 |
|
204 | export default function createClassProxy(Component) {
|
205 | return Component.__proto__ && supportsProtoAssignment() ?
|
206 | proxyClass(Component) :
|
207 | createFallback(Component);
|
208 | }
|