react-native/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngi...

260 lines
7.7 KiB
JavaScript
Raw Normal View History

/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* Sets up global variables typical in most JavaScript environments.
*
* 1. Global timers (via `setTimeout` etc).
* 2. Global console object.
* 3. Hooks for printing stack traces with source maps.
*
* Leaves enough room in the environment for implementing your own:
* 1. Require system.
* 2. Bridged modules.
*
* @providesModule InitializeJavaScriptAppEngine
* @flow
*/
/* eslint strict: 0 */
/* globals window: true */
require('regenerator-runtime/runtime');
if (global.GLOBAL === undefined) {
global.GLOBAL = global;
}
if (global.window === undefined) {
global.window = global;
}
function setUpProcess(): void {
global.process = global.process || {};
global.process.env = global.process.env || {};
if (!global.process.env.NODE_ENV) {
global.process.env.NODE_ENV = __DEV__ ? 'development' : 'production';
}
}
function setUpProfile(): void {
const Systrace = require('Systrace');
Systrace.setEnabled(global.__RCTProfileIsProfiling || false);
}
function setUpConsole(): void {
// ExceptionsManager transitively requires Promise so we install it after
const ExceptionsManager = require('ExceptionsManager');
ExceptionsManager.installConsoleErrorReporter();
if (__DEV__) {
require('RCTLog');
}
}
/**
* Sets an object's property. If a property with the same name exists, this will
* replace it but maintain its descriptor configuration.
*
* The original property value will be preserved as `original[PropertyName]` so
* that, if necessary, it can be restored. For example, if you want to route
* network requests through DevTools (to trace them):
*
* global.XMLHttpRequest = global.originalXMLHttpRequest;
*
* @see https://github.com/facebook/react-native/issues/934
*/
function defineProperty(object: Object, name: string, newValue: mixed): void {
const descriptor = Object.getOwnPropertyDescriptor(object, name);
if (descriptor) {
const backupName = `original${name[0].toUpperCase()}${name.substr(1)}`;
Object.defineProperty(object, backupName, {
...descriptor,
value: object[name],
});
}
const {enumerable, writable} = descriptor || {};
Object.defineProperty(object, name, {
configurable: true,
enumerable: enumerable !== false,
writable: writable !== false,
value: newValue,
});
}
function defineLazyProperty<T>(
object: Object,
name: string,
getNewValue: () => T
): void {
const descriptor = getPropertyDescriptor(object, name);
if (descriptor) {
const backupName = `original${name[0].toUpperCase()}${name.substr(1)}`;
Object.defineProperty(object, backupName, descriptor);
}
const config = {
configurable: true,
enumerable: descriptor ? descriptor.enumerable !== false : true,
writable: descriptor ? descriptor.writable !== false : true,
};
let value;
let valueSet = false;
function getValue(): T {
// WORKAROUND: A weird infinite loop occurs where calling `getValue` calls
// `setValue` which calls `Object.defineProperty` which somehow triggers
// `getValue` again. Adding `valueSet` breaks this loop.
if (!valueSet) {
setValue(getNewValue());
}
return value;
}
function setValue(newValue: T): void {
value = newValue;
valueSet = true;
Object.defineProperty(object, name, {...config, value: newValue});
}
Object.defineProperty(object, name, {
configurable: config.configurable,
enumerable: config.enumerable,
get: getValue,
set: setValue,
});
}
function setUpErrorHandler(): void {
if (global.__fbDisableExceptionsManager) {
return;
}
function handleError(e, isFatal) {
try {
require('ExceptionsManager').handleException(e, isFatal);
} catch (ee) {
/* eslint-disable no-console-disallow */
console.log('Failed to print error: ', ee.message);
/* eslint-enable no-console-disallow */
throw e;
}
}
const ErrorUtils = require('ErrorUtils');
ErrorUtils.setGlobalHandler(handleError);
}
/**
* Sets up a set of window environment wrappers that ensure that the
* BatchedBridge is flushed after each tick. In both the case of the
2015-03-17 10:08:46 +00:00
* `UIWebView` based `RCTJavaScriptCaller` and `RCTContextCaller`, we
* implement our own custom timing bridge that should be immune to
* unexplainably dropped timing signals.
*/
function setUpTimers(): void {
const defineLazyTimer = name => {
defineLazyProperty(global, name, () => require('JSTimers')[name]);
};
defineLazyTimer('setTimeout');
defineLazyTimer('setInterval');
defineLazyTimer('setImmediate');
defineLazyTimer('clearTimeout');
defineLazyTimer('clearInterval');
defineLazyTimer('clearImmediate');
defineLazyTimer('requestAnimationFrame');
defineLazyTimer('cancelAnimationFrame');
}
function setUpAlert(): void {
if (!global.alert) {
global.alert = function(text) {
// Require Alert on demand. Requiring it too early can lead to issues
// with things like Platform not being fully initialized.
require('Alert').alert('Alert', '' + text);
};
}
}
function setUpPromise(): void {
// The native Promise implementation throws the following error:
// ERROR: Event loop not supported.
defineLazyProperty(global, 'Promise', () => require('Promise'));
}
function setUpXHR(): void {
// The native XMLHttpRequest in Chrome dev tools is CORS aware and won't
// let you fetch anything from the internet
defineLazyProperty(global, 'XMLHttpRequest', () => require('XMLHttpRequest'));
defineLazyProperty(global, 'FormData', () => require('FormData'));
defineLazyProperty(global, 'fetch', () => require('fetch').fetch);
defineLazyProperty(global, 'Headers', () => require('fetch').Headers);
defineLazyProperty(global, 'Request', () => require('fetch').Request);
defineLazyProperty(global, 'Response', () => require('fetch').Response);
defineLazyProperty(global, 'WebSocket', () => require('WebSocket'));
}
function setUpGeolocation(): void {
if (global.navigator === undefined) {
Object.defineProperty(global, 'navigator', {
configurable: true,
enumerable: true,
writable: true,
value: {},
});
}
const {navigator} = global;
Object.defineProperty(navigator, 'product', {value: 'ReactNative'});
defineLazyProperty(navigator, 'geolocation', () => require('Geolocation'));
}
function setUpCollections(): void {
// We can't make these lazy because `Map` checks for `global.Map` (which would
// not exist if it were lazily defined).
defineProperty(global, 'Map', require('Map'));
defineProperty(global, 'Set', require('Set'));
}
function setUpDevTools(): void {
if (__DEV__) {
// not when debugging in chrome
if (!window.document && require('Platform').OS === 'ios') {
const setupDevtools = require('setupDevtools');
setupDevtools();
}
require('RCTDebugComponentOwnership');
require('react-transform-hmr');
}
}
function getPropertyDescriptor(object: Object, name: string): any {
while (object) {
const descriptor = Object.getOwnPropertyDescriptor(object, name);
if (descriptor) {
return descriptor;
}
object = Object.getPrototypeOf(object);
}
}
setUpProcess();
setUpProfile();
setUpConsole();
setUpTimers();
setUpAlert();
setUpPromise();
setUpErrorHandler();
setUpXHR();
setUpGeolocation();
setUpCollections();
setUpDevTools();
// Just to make sure the JS gets packaged up. Wait until the JS environment has
// been initialized before requiring them.
require('RCTDeviceEventEmitter');
require('RCTNativeAppEventEmitter');
require('PerformanceLogger');