Skip to content

Commit 57ece23

Browse files
yungstersMorgan Pretty
authored andcommitted
RN: Scrub InitializeJavaScriptAppEngine
Summary: Cleans up `InitializeJavaScriptAppEngine` in a few ways: - Fix bug where `global.navigation.geolocation` was being assigned to `global`. - Rename `polyfillGlobal` to `defineProperty`. - Rename `polyfillLazyGlobal` to `defineLazyProperty`. - Inline `polyfillIfNeeded` (only used once). - Rename `setUpMapAndSet` to `setUpCollections`. - Add `flow`. I've changed `defineProperty` and `defineLazyProperty` to always accept an `object` property since it is not only used for defining properties on `global`. Reviewed By: davidaurelio Differential Revision: D3472147 fbshipit-source-id: 492da62a303cf040211c386fa6260789e50b43c1
1 parent 24b57af commit 57ece23

File tree

1 file changed

+67
-72
lines changed

1 file changed

+67
-72
lines changed

Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js

Lines changed: 67 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -17,35 +17,36 @@
1717
* 2. Bridged modules.
1818
*
1919
* @providesModule InitializeJavaScriptAppEngine
20+
* @flow
2021
*/
2122

2223
/* eslint strict: 0 */
2324
/* globals window: true */
2425

2526
require('regenerator-runtime/runtime');
2627

27-
if (typeof GLOBAL === 'undefined') {
28+
if (global.GLOBAL === undefined) {
2829
global.GLOBAL = global;
2930
}
3031

31-
if (typeof window === 'undefined') {
32+
if (global.window === undefined) {
3233
global.window = global;
3334
}
3435

35-
function setUpProcess() {
36+
function setUpProcess(): void {
3637
global.process = global.process || {};
3738
global.process.env = global.process.env || {};
3839
if (!global.process.env.NODE_ENV) {
3940
global.process.env.NODE_ENV = __DEV__ ? 'development' : 'production';
4041
}
4142
}
4243

43-
function setUpProfile() {
44+
function setUpProfile(): void {
4445
const Systrace = require('Systrace');
4546
Systrace.setEnabled(global.__RCTProfileIsProfiling || false);
4647
}
4748

48-
function setUpConsole() {
49+
function setUpConsole(): void {
4950
// ExceptionsManager transitively requires Promise so we install it after
5051
const ExceptionsManager = require('ExceptionsManager');
5152
ExceptionsManager.installConsoleErrorReporter();
@@ -56,58 +57,56 @@ function setUpConsole() {
5657
}
5758

5859
/**
59-
* Assigns a new global property, replacing the existing one if there is one.
60+
* Sets an object's property. If a property with the same name exists, this will
61+
* replace it but maintain its descriptor configuration.
6062
*
61-
* Existing properties are preserved as `originalPropertyName`. Both properties
62-
* will maintain the same enumerability & configurability.
63+
* The original property value will be preserved as `original[PropertyName]` so
64+
* that, if necessary, it can be restored. For example, if you want to route
65+
* network requests through DevTools (to trace them):
6366
*
64-
* This allows you to undo the more aggressive polyfills, should you need to.
65-
* For example, if you want to route network requests through DevTools (to trace
66-
* them):
67+
* global.XMLHttpRequest = global.originalXMLHttpRequest;
6768
*
68-
* global.XMLHttpRequest = global.originalXMLHttpRequest;
69-
*
70-
* For more info on that particular case, see:
71-
* https://github.com/facebook/react-native/issues/934
69+
* @see https://github.com/facebook/react-native/issues/934
7270
*/
73-
function polyfillGlobal(name, newValue, scope = global) {
74-
const descriptor = Object.getOwnPropertyDescriptor(scope, name);
71+
function defineProperty(object: Object, name: string, newValue: mixed): void {
72+
const descriptor = Object.getOwnPropertyDescriptor(object, name);
7573
if (descriptor) {
7674
const backupName = `original${name[0].toUpperCase()}${name.substr(1)}`;
77-
Object.defineProperty(scope, backupName, {...descriptor, value: scope[name]});
75+
Object.defineProperty(object, backupName, {
76+
...descriptor,
77+
value: object[name],
78+
});
7879
}
7980

8081
const {enumerable, writable} = descriptor || {};
81-
82-
// jest for some bad reasons runs the polyfill code multiple times. In jest
83-
// environment, XmlHttpRequest doesn't exist so getOwnPropertyDescriptor
84-
// returns undefined and defineProperty default for writable is false.
85-
// Therefore, the second time it runs, defineProperty will fatal :(
86-
87-
Object.defineProperty(scope, name, {
82+
Object.defineProperty(object, name, {
8883
configurable: true,
8984
enumerable: enumerable !== false,
9085
writable: writable !== false,
9186
value: newValue,
9287
});
9388
}
9489

95-
function polyfillLazyGlobal(name, valueFn, scope = global) {
96-
const descriptor = getPropertyDescriptor(scope, name);
90+
function defineLazyProperty(
91+
object: Object,
92+
name: string,
93+
getValue: () => mixed
94+
): void {
95+
const descriptor = getPropertyDescriptor(object, name);
9796
if (descriptor) {
9897
const backupName = `original${name[0].toUpperCase()}${name.substr(1)}`;
99-
Object.defineProperty(scope, backupName, descriptor);
98+
Object.defineProperty(object, backupName, descriptor);
10099
}
101100

102101
const {enumerable, writable} = descriptor || {};
103-
Object.defineProperty(scope, name, {
102+
Object.defineProperty(object, name, {
104103
configurable: true,
105104
enumerable: enumerable !== false,
106105
get() {
107-
return (global[name] = valueFn());
106+
return (object[name] = getValue());
108107
},
109108
set(value) {
110-
Object.defineProperty(global, name, {
109+
Object.defineProperty(object, name, {
111110
configurable: true,
112111
enumerable: enumerable !== false,
113112
writable: writable !== false,
@@ -117,16 +116,7 @@ function polyfillLazyGlobal(name, valueFn, scope = global) {
117116
});
118117
}
119118

120-
/**
121-
* Polyfill a module if it is not already defined in `scope`.
122-
*/
123-
function polyfillIfNeeded(name, polyfill, scope = global, descriptor = {}) {
124-
if (scope[name] === undefined) {
125-
Object.defineProperty(scope, name, {...descriptor, value: polyfill});
126-
}
127-
}
128-
129-
function setUpErrorHandler() {
119+
function setUpErrorHandler(): void {
130120
if (global.__fbDisableExceptionsManager) {
131121
return;
132122
}
@@ -135,7 +125,9 @@ function setUpErrorHandler() {
135125
try {
136126
require('ExceptionsManager').handleException(e, isFatal);
137127
} catch (ee) {
128+
/* eslint-disable no-console-disallow */
138129
console.log('Failed to print error: ', ee.message);
130+
/* eslint-enable no-console-disallow */
139131
throw e;
140132
}
141133
}
@@ -151,9 +143,9 @@ function setUpErrorHandler() {
151143
* implement our own custom timing bridge that should be immune to
152144
* unexplainably dropped timing signals.
153145
*/
154-
function setUpTimers() {
155-
const defineLazyTimer = (name) => {
156-
polyfillLazyGlobal(name, () => require('JSTimers')[name]);
146+
function setUpTimers(): void {
147+
const defineLazyTimer = name => {
148+
defineLazyProperty(global, name, () => require('JSTimers')[name]);
157149
};
158150
defineLazyTimer('setTimeout');
159151
defineLazyTimer('setInterval');
@@ -165,7 +157,7 @@ function setUpTimers() {
165157
defineLazyTimer('cancelAnimationFrame');
166158
}
167159

168-
function setUpAlert() {
160+
function setUpAlert(): void {
169161
if (!global.alert) {
170162
global.alert = function(text) {
171163
// Require Alert on demand. Requiring it too early can lead to issues
@@ -175,45 +167,48 @@ function setUpAlert() {
175167
}
176168
}
177169

178-
function setUpPromise() {
170+
function setUpPromise(): void {
179171
// The native Promise implementation throws the following error:
180172
// ERROR: Event loop not supported.
181-
polyfillLazyGlobal('Promise', () => require('Promise'));
173+
defineLazyProperty(global, 'Promise', () => require('Promise'));
182174
}
183175

184-
function setUpXHR() {
176+
function setUpXHR(): void {
185177
// The native XMLHttpRequest in Chrome dev tools is CORS aware and won't
186178
// let you fetch anything from the internet
187-
polyfillLazyGlobal('XMLHttpRequest', () => require('XMLHttpRequest'));
188-
polyfillLazyGlobal('FormData', () => require('FormData'));
179+
defineLazyProperty(global, 'XMLHttpRequest', () => require('XMLHttpRequest'));
180+
defineLazyProperty(global, 'FormData', () => require('FormData'));
189181

190-
polyfillLazyGlobal('fetch', () => require('fetch').fetch);
191-
polyfillLazyGlobal('Headers', () => require('fetch').Headers);
192-
polyfillLazyGlobal('Request', () => require('fetch').Request);
193-
polyfillLazyGlobal('Response', () => require('fetch').Response);
182+
defineLazyProperty(global, 'fetch', () => require('fetch').fetch);
183+
defineLazyProperty(global, 'Headers', () => require('fetch').Headers);
184+
defineLazyProperty(global, 'Request', () => require('fetch').Request);
185+
defineLazyProperty(global, 'Response', () => require('fetch').Response);
194186

195-
polyfillLazyGlobal('WebSocket', () => require('WebSocket'));
187+
defineLazyProperty(global, 'WebSocket', () => require('WebSocket'));
196188
}
197189

198-
function setUpGeolocation() {
199-
polyfillIfNeeded('navigator', {}, global, {
200-
writable: true,
201-
enumerable: true,
202-
configurable: true,
203-
});
204-
Object.defineProperty(global.navigator, 'product', {value: 'ReactNative'});
205-
206-
polyfillLazyGlobal('geolocation', () => require('Geolocation'), global.navigator);
190+
function setUpGeolocation(): void {
191+
if (global.navigator === undefined) {
192+
Object.defineProperty(global, 'navigator', {
193+
configurable: true,
194+
enumerable: true,
195+
writable: true,
196+
value: {},
197+
});
198+
}
199+
const {navigator} = global;
200+
Object.defineProperty(navigator, 'product', {value: 'ReactNative'});
201+
defineLazyProperty(navigator, 'geolocation', () => require('Geolocation'));
207202
}
208203

209-
function setUpMapAndSet() {
210-
// We can't make these lazy as Map checks the global.Map to see if it's
211-
// available but in our case it'll be a lazy getter.
212-
polyfillGlobal('Map', require('Map'));
213-
polyfillGlobal('Set', require('Set'));
204+
function setUpCollections(): void {
205+
// We can't make these lazy because `Map` checks for `global.Map` (which would
206+
// not exist if it were lazily defined).
207+
defineProperty(global, 'Map', require('Map'));
208+
defineProperty(global, 'Set', require('Set'));
214209
}
215210

216-
function setUpDevTools() {
211+
function setUpDevTools(): void {
217212
if (__DEV__) {
218213
// not when debugging in chrome
219214
if (!window.document && require('Platform').OS === 'ios') {
@@ -226,7 +221,7 @@ function setUpDevTools() {
226221
}
227222
}
228223

229-
function getPropertyDescriptor(object, name) {
224+
function getPropertyDescriptor(object: Object, name: string): any {
230225
while (object) {
231226
const descriptor = Object.getOwnPropertyDescriptor(object, name);
232227
if (descriptor) {
@@ -245,7 +240,7 @@ setUpPromise();
245240
setUpErrorHandler();
246241
setUpXHR();
247242
setUpGeolocation();
248-
setUpMapAndSet();
243+
setUpCollections();
249244
setUpDevTools();
250245

251246
// Just to make sure the JS gets packaged up. Wait until the JS environment has

0 commit comments

Comments
 (0)