Make polyfills and globals lazy
Summary: This avoids requiring things that may never be used at all by the application such as WebSocket or Geolocation. It also stops us from asking for native modules before we actually start the application enabling us to potentially be more lazy in the future. Reviewed By: davidaurelio Differential Revision: D3212802 fb-gh-sync-id: 70cf0d1a85f39fedc47758e5eb5df789a511bc9b fbshipit-source-id: 70cf0d1a85f39fedc47758e5eb5df789a511bc9b
This commit is contained in:
parent
9547a98a68
commit
9a3a082225
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
const BatchedBridge = require('BatchedBridge');
|
const BatchedBridge = require('BatchedBridge');
|
||||||
const RemoteModules = BatchedBridge.RemoteModules;
|
const RemoteModules = BatchedBridge.RemoteModules;
|
||||||
|
const Platform = require('Platform');
|
||||||
|
|
||||||
function normalizePrefix(moduleName: string): string {
|
function normalizePrefix(moduleName: string): string {
|
||||||
return moduleName.replace(/^(RCT|RK)/, '');
|
return moduleName.replace(/^(RCT|RK)/, '');
|
||||||
|
@ -65,51 +66,53 @@ Object.keys(RemoteModules).forEach((moduleName) => {
|
||||||
* the call sites accessing NativeModules.UIManager directly have
|
* the call sites accessing NativeModules.UIManager directly have
|
||||||
* been removed #9344445
|
* been removed #9344445
|
||||||
*/
|
*/
|
||||||
const UIManager = NativeModules.UIManager;
|
if (Platform.OS === 'ios') {
|
||||||
UIManager && Object.keys(UIManager).forEach(viewName => {
|
const UIManager = NativeModules.UIManager;
|
||||||
const viewConfig = UIManager[viewName];
|
UIManager && Object.keys(UIManager).forEach(viewName => {
|
||||||
if (viewConfig.Manager) {
|
const viewConfig = UIManager[viewName];
|
||||||
let constants;
|
if (viewConfig.Manager) {
|
||||||
/* $FlowFixMe - nice try. Flow doesn't like getters */
|
let constants;
|
||||||
Object.defineProperty(viewConfig, 'Constants', {
|
/* $FlowFixMe - nice try. Flow doesn't like getters */
|
||||||
configurable: true,
|
Object.defineProperty(viewConfig, 'Constants', {
|
||||||
enumerable: true,
|
configurable: true,
|
||||||
get: () => {
|
enumerable: true,
|
||||||
if (constants) {
|
get: () => {
|
||||||
|
if (constants) {
|
||||||
|
return constants;
|
||||||
|
}
|
||||||
|
constants = {};
|
||||||
|
const viewManager = NativeModules[normalizePrefix(viewConfig.Manager)];
|
||||||
|
viewManager && Object.keys(viewManager).forEach(key => {
|
||||||
|
const value = viewManager[key];
|
||||||
|
if (typeof value !== 'function') {
|
||||||
|
constants[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
return constants;
|
return constants;
|
||||||
}
|
},
|
||||||
constants = {};
|
});
|
||||||
const viewManager = NativeModules[normalizePrefix(viewConfig.Manager)];
|
let commands;
|
||||||
viewManager && Object.keys(viewManager).forEach(key => {
|
/* $FlowFixMe - nice try. Flow doesn't like getters */
|
||||||
const value = viewManager[key];
|
Object.defineProperty(viewConfig, 'Commands', {
|
||||||
if (typeof value !== 'function') {
|
configurable: true,
|
||||||
constants[key] = value;
|
enumerable: true,
|
||||||
|
get: () => {
|
||||||
|
if (commands) {
|
||||||
|
return commands;
|
||||||
}
|
}
|
||||||
});
|
commands = {};
|
||||||
return constants;
|
const viewManager = NativeModules[normalizePrefix(viewConfig.Manager)];
|
||||||
},
|
viewManager && Object.keys(viewManager).forEach((key, index) => {
|
||||||
});
|
const value = viewManager[key];
|
||||||
let commands;
|
if (typeof value === 'function') {
|
||||||
/* $FlowFixMe - nice try. Flow doesn't like getters */
|
commands[key] = index;
|
||||||
Object.defineProperty(viewConfig, 'Commands', {
|
}
|
||||||
configurable: true,
|
});
|
||||||
enumerable: true,
|
|
||||||
get: () => {
|
|
||||||
if (commands) {
|
|
||||||
return commands;
|
return commands;
|
||||||
}
|
},
|
||||||
commands = {};
|
});
|
||||||
const viewManager = NativeModules[normalizePrefix(viewConfig.Manager)];
|
}
|
||||||
viewManager && Object.keys(viewManager).forEach((key, index) => {
|
});
|
||||||
const value = viewManager[key];
|
}
|
||||||
if (typeof value === 'function') {
|
|
||||||
commands[key] = index;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return commands;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = NativeModules;
|
module.exports = NativeModules;
|
||||||
|
|
|
@ -70,6 +70,29 @@ function polyfillGlobal(name, newValue, scope = GLOBAL) {
|
||||||
Object.defineProperty(scope, name, {...descriptor, value: newValue});
|
Object.defineProperty(scope, name, {...descriptor, value: newValue});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function polyfillLazyGlobal(name, valueFn, scope = GLOBAL) {
|
||||||
|
if (scope[name] !== undefined) {
|
||||||
|
const descriptor = Object.getOwnPropertyDescriptor(scope, name);
|
||||||
|
const backupName = `original${name[0].toUpperCase()}${name.substr(1)}`;
|
||||||
|
Object.defineProperty(scope, backupName, {...descriptor, value: scope[name]});
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(scope, name, {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
get() {
|
||||||
|
return this[name] = valueFn();
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
Object.defineProperty(this, name, {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Polyfill a module if it is not already defined in `scope`.
|
* Polyfill a module if it is not already defined in `scope`.
|
||||||
*/
|
*/
|
||||||
|
@ -104,18 +127,17 @@ function setUpErrorHandler() {
|
||||||
* unexplainably dropped timing signals.
|
* unexplainably dropped timing signals.
|
||||||
*/
|
*/
|
||||||
function setUpTimers() {
|
function setUpTimers() {
|
||||||
var JSTimers = require('JSTimers');
|
const defineLazyTimer = (name) => {
|
||||||
GLOBAL.setTimeout = JSTimers.setTimeout;
|
polyfillLazyGlobal(name, () => require('JSTimers')[name]);
|
||||||
GLOBAL.setInterval = JSTimers.setInterval;
|
|
||||||
GLOBAL.setImmediate = JSTimers.setImmediate;
|
|
||||||
GLOBAL.clearTimeout = JSTimers.clearTimeout;
|
|
||||||
GLOBAL.clearInterval = JSTimers.clearInterval;
|
|
||||||
GLOBAL.clearImmediate = JSTimers.clearImmediate;
|
|
||||||
GLOBAL.cancelAnimationFrame = JSTimers.clearInterval;
|
|
||||||
GLOBAL.requestAnimationFrame = function(cb) {
|
|
||||||
/*requestAnimationFrame() { [native code] };*/ // Trick scroller library
|
|
||||||
return JSTimers.requestAnimationFrame(cb); // into thinking it's native
|
|
||||||
};
|
};
|
||||||
|
defineLazyTimer('setTimeout');
|
||||||
|
defineLazyTimer('setInterval');
|
||||||
|
defineLazyTimer('setImmediate');
|
||||||
|
defineLazyTimer('clearTimeout');
|
||||||
|
defineLazyTimer('clearInterval');
|
||||||
|
defineLazyTimer('clearImmediate');
|
||||||
|
defineLazyTimer('requestAnimationFrame');
|
||||||
|
defineLazyTimer('cancelAnimationFrame');
|
||||||
}
|
}
|
||||||
|
|
||||||
function setUpAlert() {
|
function setUpAlert() {
|
||||||
|
@ -131,20 +153,19 @@ function setUpAlert() {
|
||||||
function setUpPromise() {
|
function setUpPromise() {
|
||||||
// The native Promise implementation throws the following error:
|
// The native Promise implementation throws the following error:
|
||||||
// ERROR: Event loop not supported.
|
// ERROR: Event loop not supported.
|
||||||
GLOBAL.Promise = require('Promise');
|
polyfillLazyGlobal('Promise', () => require('Promise'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function setUpXHR() {
|
function setUpXHR() {
|
||||||
// The native XMLHttpRequest in Chrome dev tools is CORS aware and won't
|
// The native XMLHttpRequest in Chrome dev tools is CORS aware and won't
|
||||||
// let you fetch anything from the internet
|
// let you fetch anything from the internet
|
||||||
polyfillGlobal('XMLHttpRequest', require('XMLHttpRequest'));
|
polyfillLazyGlobal('XMLHttpRequest', () => require('XMLHttpRequest'));
|
||||||
polyfillGlobal('FormData', require('FormData'));
|
polyfillLazyGlobal('FormData', () => require('FormData'));
|
||||||
|
|
||||||
var fetchPolyfill = require('fetch');
|
polyfillLazyGlobal('fetch', () => require('fetch').fetch);
|
||||||
polyfillGlobal('fetch', fetchPolyfill.fetch);
|
polyfillLazyGlobal('Headers', () => require('fetch').Headers);
|
||||||
polyfillGlobal('Headers', fetchPolyfill.Headers);
|
polyfillLazyGlobal('Request', () => require('fetch').Request);
|
||||||
polyfillGlobal('Request', fetchPolyfill.Request);
|
polyfillLazyGlobal('Response', () => require('fetch').Response);
|
||||||
polyfillGlobal('Response', fetchPolyfill.Response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setUpGeolocation() {
|
function setUpGeolocation() {
|
||||||
|
@ -153,10 +174,12 @@ function setUpGeolocation() {
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
});
|
});
|
||||||
polyfillGlobal('geolocation', require('Geolocation'), GLOBAL.navigator);
|
polyfillLazyGlobal('geolocation', () => require('Geolocation'), GLOBAL.navigator);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setUpMapAndSet() {
|
function setUpMapAndSet() {
|
||||||
|
// We can't make these lazy as Map checks the global.Map to see if it's
|
||||||
|
// available but in our case it'll be a lazy getter.
|
||||||
polyfillGlobal('Map', require('Map'));
|
polyfillGlobal('Map', require('Map'));
|
||||||
polyfillGlobal('Set', require('Set'));
|
polyfillGlobal('Set', require('Set'));
|
||||||
}
|
}
|
||||||
|
@ -166,7 +189,7 @@ function setUpProduct() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setUpWebSockets() {
|
function setUpWebSockets() {
|
||||||
polyfillGlobal('WebSocket', require('WebSocket'));
|
polyfillLazyGlobal('WebSocket', () => require('WebSocket'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function setUpProfile() {
|
function setUpProfile() {
|
||||||
|
|
Loading…
Reference in New Issue