Added deferred module loading feature

Reviewed By: javache

Differential Revision: D2717687

fb-gh-sync-id: 4c03c721c794a2e7ac67a0b20474129fde7f0a0d
This commit is contained in:
Nick Lockwood
2015-12-10 04:27:45 -08:00
committed by facebook-github-bot-7
parent 5775d9e1d0
commit d138403c2e
6 changed files with 176 additions and 119 deletions

View File

@@ -11,10 +11,78 @@
*/
'use strict';
var NativeModules = require('BatchedBridge').RemoteModules;
const BatchedBridge = require('BatchedBridge');
const RemoteModules = BatchedBridge.RemoteModules;
var nativeModulePrefixNormalizer = require('nativeModulePrefixNormalizer');
function normalizePrefix(moduleName: string): string {
return moduleName.replace(/^(RCT|RK)/, '');
}
nativeModulePrefixNormalizer(NativeModules);
/**
* Dirty hack to support old (RK) and new (RCT) native module name conventions.
*/
Object.keys(RemoteModules).forEach((moduleName) => {
const strippedName = normalizePrefix(moduleName);
if (RemoteModules['RCT' + strippedName] && RemoteModules['RK' + strippedName]) {
throw new Error(
'Module cannot be registered as both RCT and RK: ' + moduleName
);
}
if (strippedName !== moduleName) {
RemoteModules[strippedName] = RemoteModules[moduleName];
delete RemoteModules[moduleName];
}
});
/**
* Define lazy getters for each module.
* These will return the module if already loaded, or load it if not.
*/
const NativeModules = {};
Object.keys(RemoteModules).forEach((moduleName) => {
Object.defineProperty(NativeModules, moduleName, {
enumerable: true,
get: () => {
let module = RemoteModules[moduleName];
if (module && module.moduleID) {
const json = global.nativeRequireModuleConfig(moduleName);
const config = json && JSON.parse(json);
module = config && BatchedBridge.processModuleConfig(config, module.moduleID);
RemoteModules[moduleName] = module;
}
return module;
},
});
});
/**
* Copies the ViewManager constants into UIManager. This is only
* needed for iOS, which puts the constants in the ViewManager
* namespace instead of UIManager, unlike Android.
*
* We'll eventually move this logic to UIManager.js, once all
* 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];
const constants = {};
if (viewConfig.Manager) {
Object.defineProperty(viewConfig, 'Constants', {
enumerable: true,
get: () => {
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;
},
});
}
});
module.exports = NativeModules;

View File

@@ -66,8 +66,6 @@ class MessageQueue {
this._genModulesConfig(localModules),this._moduleTable, this._methodTable
);
this._copyNativeComponentConstants(this.RemoteModules);
this._debugInfo = {};
this._remoteModuleTable = {};
this._remoteMethodTable = {};
@@ -105,6 +103,12 @@ class MessageQueue {
return queue[0].length ? queue : null;
}
processModuleConfig(config, moduleID) {
const module = this._genModule(config, moduleID);
this._genLookup(config, moduleID, this._remoteModuleTable, this._remoteMethodTable)
return module;
}
/**
* "Private" methods
*/
@@ -200,30 +204,6 @@ class MessageQueue {
* Private helper methods
*/
/**
* Copies the ViewManager constants into UIManager. This is only
* needed for iOS, which puts the constants in the ViewManager
* namespace instead of UIManager, unlike Android.
*/
_copyNativeComponentConstants(remoteModules) {
let UIManager = remoteModules.RCTUIManager;
UIManager && Object.keys(UIManager).forEach(viewName => {
let viewConfig = UIManager[viewName];
if (viewConfig.Manager) {
const viewManager = remoteModules[viewConfig.Manager];
viewManager && Object.keys(viewManager).forEach(key => {
const value = viewManager[key];
if (typeof value !== 'function') {
if (!viewConfig.Constants) {
viewConfig.Constants = {};
}
viewConfig.Constants[key] = value;
}
});
}
});
}
/**
* Converts the old, object-based module structure to the new
* array-based structure. TODO (t8823865) Removed this
@@ -269,55 +249,62 @@ class MessageQueue {
}
_genLookupTables(modulesConfig, moduleTable, methodTable) {
modulesConfig.forEach((module, moduleID) => {
if (!module) {
return;
}
let moduleName, methods;
if (moduleHasConstants(module)) {
[moduleName, , methods] = module;
} else {
[moduleName, methods] = module;
}
moduleTable[moduleID] = moduleName;
methodTable[moduleID] = Object.assign({}, methods);
modulesConfig.forEach((config, moduleID) => {
this._genLookup(config, moduleID, moduleTable, methodTable);
});
}
_genLookup(config, moduleID, moduleTable, methodTable) {
if (!config) {
return;
}
let moduleName, methods;
if (moduleHasConstants(config)) {
[moduleName, , methods] = config;
} else {
[moduleName, methods] = config;
}
moduleTable[moduleID] = moduleName;
methodTable[moduleID] = Object.assign({}, methods);
}
_genModules(remoteModules) {
remoteModules.forEach((module, moduleID) => {
if (!module) {
return;
}
let moduleName, constants, methods, asyncMethods;
if (moduleHasConstants(module)) {
[moduleName, constants, methods, asyncMethods] = module;
} else {
[moduleName, methods, asyncMethods] = module;
}
const moduleConfig = {moduleID, constants, methods, asyncMethods};
this.RemoteModules[moduleName] = this._genModule({}, moduleConfig);
remoteModules.forEach((config, moduleID) => {
this._genModule(config, moduleID);
});
}
_genModule(module, moduleConfig) {
const {moduleID, constants, methods = [], asyncMethods = []} = moduleConfig;
_genModule(config, moduleID) {
if (!config) {
return;
}
methods.forEach((methodName, methodID) => {
let moduleName, constants, methods, asyncMethods;
if (moduleHasConstants(config)) {
[moduleName, constants, methods, asyncMethods] = config;
} else {
[moduleName, methods, asyncMethods] = config;
}
let module = {};
methods && methods.forEach((methodName, methodID) => {
const methodType =
arrayContains(asyncMethods, methodID) ?
asyncMethods && arrayContains(asyncMethods, methodID) ?
MethodTypes.remoteAsync : MethodTypes.remote;
module[methodName] = this._genMethod(moduleID, methodID, methodType);
});
Object.assign(module, constants);
if (!constants && !methods && !asyncMethods) {
module.moduleID = moduleID;
}
this.RemoteModules[moduleName] = module;
return module;
}
_genMethod(module, method, type) {
let fn = null;
let self = this;

View File

@@ -1,32 +0,0 @@
/**
* 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.
*
* @providesModule nativeModulePrefixNormalizer
* @flow
*/
'use strict';
// Dirty hack to support old (RK) and new (RCT) native module name conventions
function nativeModulePrefixNormalizer(
modules: {[key: string]: any}
): void {
Object.keys(modules).forEach((moduleName) => {
var strippedName = moduleName.replace(/^(RCT|RK)/, '');
if (modules['RCT' + strippedName] && modules['RK' + strippedName]) {
throw new Error(
'Module cannot be registered as both RCT and RK: ' + moduleName
);
}
if (strippedName !== moduleName) {
modules[strippedName] = modules[moduleName];
delete modules[moduleName];
}
});
}
module.exports = nativeModulePrefixNormalizer;