Skip to content

Commit

Permalink
Make polyfills and globals lazy
Browse files Browse the repository at this point in the history
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
  • Loading branch information
lexs authored and Facebook Github Bot 9 committed Apr 27, 2016
1 parent 9547a98 commit 9a3a082
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 64 deletions.
89 changes: 46 additions & 43 deletions Libraries/BatchedBridge/BatchedBridgedModules/NativeModules.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

const BatchedBridge = require('BatchedBridge');
const RemoteModules = BatchedBridge.RemoteModules;
const Platform = require('Platform');

function normalizePrefix(moduleName: string): string {
return moduleName.replace(/^(RCT|RK)/, '');
Expand Down Expand Up @@ -65,51 +66,53 @@ Object.keys(RemoteModules).forEach((moduleName) => {
* the call sites accessing NativeModules.UIManager directly have
* been removed #9344445
*/
const UIManager = NativeModules.UIManager;
UIManager && Object.keys(UIManager).forEach(viewName => {
const viewConfig = UIManager[viewName];
if (viewConfig.Manager) {
let constants;
/* $FlowFixMe - nice try. Flow doesn't like getters */
Object.defineProperty(viewConfig, 'Constants', {
configurable: true,
enumerable: true,
get: () => {
if (constants) {
if (Platform.OS === 'ios') {
const UIManager = NativeModules.UIManager;
UIManager && Object.keys(UIManager).forEach(viewName => {
const viewConfig = UIManager[viewName];
if (viewConfig.Manager) {
let constants;
/* $FlowFixMe - nice try. Flow doesn't like getters */
Object.defineProperty(viewConfig, 'Constants', {
configurable: true,
enumerable: true,
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;
}
constants = {};
const viewManager = NativeModules[normalizePrefix(viewConfig.Manager)];
viewManager && Object.keys(viewManager).forEach(key => {
const value = viewManager[key];
if (typeof value !== 'function') {
constants[key] = value;
},
});
let commands;
/* $FlowFixMe - nice try. Flow doesn't like getters */
Object.defineProperty(viewConfig, 'Commands', {
configurable: true,
enumerable: true,
get: () => {
if (commands) {
return commands;
}
});
return constants;
},
});
let commands;
/* $FlowFixMe - nice try. Flow doesn't like getters */
Object.defineProperty(viewConfig, 'Commands', {
configurable: true,
enumerable: true,
get: () => {
if (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;
}
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;
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,29 @@ function polyfillGlobal(name, newValue, scope = GLOBAL) {
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`.
*/
Expand Down Expand Up @@ -104,18 +127,17 @@ function setUpErrorHandler() {
* unexplainably dropped timing signals.
*/
function setUpTimers() {
var JSTimers = require('JSTimers');
GLOBAL.setTimeout = JSTimers.setTimeout;
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
const defineLazyTimer = (name) => {
polyfillLazyGlobal(name, () => require('JSTimers')[name]);
};
defineLazyTimer('setTimeout');
defineLazyTimer('setInterval');
defineLazyTimer('setImmediate');
defineLazyTimer('clearTimeout');
defineLazyTimer('clearInterval');
defineLazyTimer('clearImmediate');
defineLazyTimer('requestAnimationFrame');
defineLazyTimer('cancelAnimationFrame');
}

function setUpAlert() {
Expand All @@ -131,20 +153,19 @@ function setUpAlert() {
function setUpPromise() {
// The native Promise implementation throws the following error:
// ERROR: Event loop not supported.
GLOBAL.Promise = require('Promise');
polyfillLazyGlobal('Promise', () => require('Promise'));
}

function setUpXHR() {
// The native XMLHttpRequest in Chrome dev tools is CORS aware and won't
// let you fetch anything from the internet
polyfillGlobal('XMLHttpRequest', require('XMLHttpRequest'));
polyfillGlobal('FormData', require('FormData'));
polyfillLazyGlobal('XMLHttpRequest', () => require('XMLHttpRequest'));
polyfillLazyGlobal('FormData', () => require('FormData'));

var fetchPolyfill = require('fetch');
polyfillGlobal('fetch', fetchPolyfill.fetch);
polyfillGlobal('Headers', fetchPolyfill.Headers);
polyfillGlobal('Request', fetchPolyfill.Request);
polyfillGlobal('Response', fetchPolyfill.Response);
polyfillLazyGlobal('fetch', () => require('fetch').fetch);
polyfillLazyGlobal('Headers', () => require('fetch').Headers);
polyfillLazyGlobal('Request', () => require('fetch').Request);
polyfillLazyGlobal('Response', () => require('fetch').Response);
}

function setUpGeolocation() {
Expand All @@ -153,10 +174,12 @@ function setUpGeolocation() {
enumerable: true,
configurable: true,
});
polyfillGlobal('geolocation', require('Geolocation'), GLOBAL.navigator);
polyfillLazyGlobal('geolocation', () => require('Geolocation'), GLOBAL.navigator);
}

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('Set', require('Set'));
}
Expand All @@ -166,7 +189,7 @@ function setUpProduct() {
}

function setUpWebSockets() {
polyfillGlobal('WebSocket', require('WebSocket'));
polyfillLazyGlobal('WebSocket', () => require('WebSocket'));
}

function setUpProfile() {
Expand Down

0 comments on commit 9a3a082

Please sign in to comment.