229 lines
5.5 KiB
JavaScript
229 lines
5.5 KiB
JavaScript
const url = require('url');
|
|
const http = require('http');
|
|
const invariant = require('assert');
|
|
const { createContext, Script } = require('vm');
|
|
const ws = require('./ws');
|
|
|
|
let currentContext = null;
|
|
let scriptCached = null;
|
|
|
|
// this is a dummy file path - without a file name the source map is not used in the vm
|
|
const TEMP_BUNDLE_PATH = '/tmp/bridge/react-native.js';
|
|
|
|
/**
|
|
*
|
|
* @param replyId
|
|
* @param result
|
|
*/
|
|
function sendResult(replyID, result) {
|
|
ws.send(
|
|
JSON.stringify({
|
|
replyID,
|
|
result,
|
|
})
|
|
);
|
|
}
|
|
|
|
/**
|
|
* TODO
|
|
* @param message
|
|
*/
|
|
function sendError(error) {
|
|
console.log('error');
|
|
throw error;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param src
|
|
* @param callback
|
|
*/
|
|
function getScript(src, callback) {
|
|
if (scriptCached) return callback(null, scriptCached);
|
|
return http
|
|
.get(src, res => {
|
|
let buff = '';
|
|
|
|
res.setEncoding('utf8');
|
|
|
|
res.on('data', chunk => {
|
|
buff += chunk;
|
|
});
|
|
|
|
res.on('end', () => {
|
|
scriptCached = new Script(buff, {
|
|
// lineOffset: -1,
|
|
// columnOffset: -1,
|
|
timeout: 120000,
|
|
displayErrors: true,
|
|
produceCachedData: true,
|
|
filename: TEMP_BUNDLE_PATH,
|
|
});
|
|
|
|
callback(null, scriptCached);
|
|
});
|
|
})
|
|
.on('error', err => {
|
|
callback(err);
|
|
});
|
|
}
|
|
|
|
function consoleShim() {
|
|
return {
|
|
...console,
|
|
log(...args) {
|
|
if (
|
|
args[0] &&
|
|
typeof args[0] === 'string' &&
|
|
args[0].startsWith('Running application "')
|
|
) {
|
|
return;
|
|
}
|
|
|
|
if (
|
|
args[0] &&
|
|
typeof args[0] === 'string' &&
|
|
args[0].startsWith('Deprecated')
|
|
) {
|
|
return;
|
|
}
|
|
console.log(...args);
|
|
},
|
|
warn(...args) {
|
|
if (
|
|
args[0] &&
|
|
typeof args[0] === 'string' &&
|
|
args[0].startsWith('Running application "')
|
|
) {
|
|
return;
|
|
}
|
|
|
|
if (
|
|
args[0] &&
|
|
typeof args[0] === 'string' &&
|
|
args[0].startsWith('Deprecated')
|
|
) {
|
|
return;
|
|
}
|
|
console.log(...args);
|
|
},
|
|
};
|
|
}
|
|
|
|
process.on('ws-message', request => {
|
|
// console.log(request.method);
|
|
switch (request.method) {
|
|
case 'prepareJSRuntime':
|
|
if (currentContext) {
|
|
try {
|
|
for (const name in currentContext.__fbBatchedBridge) {
|
|
currentContext.__fbBatchedBridge[name] = undefined;
|
|
delete currentContext.__fbBatchedBridge[name];
|
|
}
|
|
|
|
for (const name in currentContext.__fbGenNativeModule) {
|
|
currentContext.__fbGenNativeModule[name] = undefined;
|
|
delete currentContext.__fbGenNativeModule[name];
|
|
}
|
|
|
|
for (const name in currentContext.__fbBatchedBridgeConfig) {
|
|
currentContext.__fbBatchedBridgeConfig[name] = undefined;
|
|
delete currentContext.__fbBatchedBridgeConfig[name];
|
|
}
|
|
|
|
for (const name in currentContext) {
|
|
currentContext[name] = undefined;
|
|
delete currentContext[name];
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}
|
|
currentContext = undefined;
|
|
currentContext = createContext({
|
|
console: consoleShim(),
|
|
__bridgeNode: {
|
|
ready() {
|
|
process.emit('rn-ready');
|
|
},
|
|
provideReactNativeModule(rnModule) {
|
|
global.bridge.rn = undefined;
|
|
global.bridge.rn = rnModule;
|
|
},
|
|
provideModule(moduleExports) {
|
|
global.bridge.module = undefined;
|
|
global.bridge.module = moduleExports;
|
|
},
|
|
provideReload(reloadFn) {
|
|
global.bridge.reload = undefined;
|
|
global.bridge.reload = reloadFn;
|
|
},
|
|
provideRoot(rootComponent) {
|
|
global.bridge.root = undefined;
|
|
global.bridge.root = rootComponent;
|
|
},
|
|
},
|
|
});
|
|
sendResult(request.id);
|
|
return;
|
|
|
|
case 'executeApplicationScript':
|
|
// Modify the URL to make sure we get the inline source map.
|
|
const parsedUrl = url.parse(request.url, /* parseQueryString */ true);
|
|
invariant(parsedUrl.query);
|
|
parsedUrl.query.inlineSourceMap = true;
|
|
delete parsedUrl.search;
|
|
// $FlowIssue url.format() does not accept what url.parse() returns.
|
|
const scriptUrl = url.format(parsedUrl);
|
|
|
|
getScript(scriptUrl, (err, script) => {
|
|
if (err != null) {
|
|
sendError(`Failed to get script from packager: ${err.message}`);
|
|
return;
|
|
}
|
|
|
|
if (currentContext == null) {
|
|
sendError('JS runtime not prepared');
|
|
return;
|
|
}
|
|
|
|
if (request.inject) {
|
|
for (const name in request.inject) {
|
|
currentContext[name] = JSON.parse(request.inject[name]);
|
|
}
|
|
}
|
|
|
|
try {
|
|
script.runInContext(currentContext, TEMP_BUNDLE_PATH);
|
|
} catch (e) {
|
|
sendError(e);
|
|
}
|
|
sendResult(request.id);
|
|
});
|
|
|
|
return;
|
|
|
|
default:
|
|
let returnValue = [[], [], [], 0];
|
|
try {
|
|
if (
|
|
currentContext != null &&
|
|
typeof currentContext.__fbBatchedBridge === 'object'
|
|
) {
|
|
returnValue = currentContext.__fbBatchedBridge[request.method].apply(
|
|
null,
|
|
request.arguments
|
|
);
|
|
}
|
|
} catch (e) {
|
|
if (request.method !== '$disconnected') {
|
|
sendError(
|
|
`Failed while making a call ${request.method}:::${e.message}`
|
|
);
|
|
}
|
|
} finally {
|
|
sendResult(request.id, JSON.stringify(returnValue));
|
|
}
|
|
}
|
|
});
|