status-mobile/figwheel-bridge.js

256 lines
7.4 KiB
JavaScript
Raw Normal View History

2016-02-22 22:04:42 +00:00
/*
* Originally taken from https://github.com/decker405/figwheel-react-native
*
* @providesModule figwheel-bridge
*/
var CLOSURE_UNCOMPILED_DEFINES = null;
var debugEnabled = false;
2016-02-22 22:04:42 +00:00
var config = {
basePath: "target/",
googBasePath: 'goog/',
serverPort: 8081
};
var React = require('react');
var createReactClass = require('create-react-class');
var ReactNative = require('react-native');
var WebSocket = require('WebSocket');
2016-02-22 22:04:42 +00:00
var self;
var evaluate = eval; // This is needed, direct calls to eval does not work (RN packager???)
var externalModules = {};
var evalListeners = {};
var asyncImportChain = new Promise(function (succ,fail) {succ(true);});
function fireEvalListenters(url) {
Object.values(evalListeners).forEach(function (listener) {
listener(url)
});
}
function formatCompileError(msg) {
var errorStr = "Figwheel Compile Exception: "
var data = msg['exception-data'];
if(data['message']) {
errorStr += data['message'] + " ";
}
if(data['file']) {
errorStr += "in file " + data['file'] + " ";
}
if(data['line']) {
errorStr += "at line " + data['line'];
}
if(data['column']) {
errorStr += ", column " + data['column'];
}
return errorStr;
}
/* This is simply demonstrating that we can receive and react to
* arbitrary messages from Figwheel this will enable creating a nicer
* feedback system in the Figwheel top level React component.
*/
function figwheelMessageHandler(msg) {
if(msg["msg-name"] == "compile-failed") {
console.warn(formatCompileError(msg));
}
}
function listenToFigwheelMessages() {
if(figwheel.client.add_json_message_watch) {
figwheel.client.add_json_message_watch("ReactNativeMessageIntercept",
figwheelMessageHandler);
}
}
2016-02-22 22:04:42 +00:00
var figwheelApp = function (platform, devHost) {
return createReactClass({
2016-02-22 22:04:42 +00:00
getInitialState: function () {
return {loaded: false}
},
render: function () {
if (!this.state.loaded) {
var plainStyle = {flex: 1, alignItems: 'center', justifyContent: 'center'};
return (
<ReactNative.View style={plainStyle}>
<ReactNative.Text>Waiting for Figwheel to load files.</ReactNative.Text>
</ReactNative.View>
2016-02-22 22:04:42 +00:00
);
}
return this.state.root;
},
2016-02-22 22:04:42 +00:00
componentDidMount: function () {
var app = this;
if (typeof goog === "undefined") {
loadApp(platform, devHost, function (appRoot) {
app.setState({root: appRoot, loaded: true});
listenToFigwheelMessages();
2016-02-22 22:04:42 +00:00
});
}
}
})
};
function logDebug(msg) {
if (debugEnabled) {
console.log(msg);
}
}
2016-02-22 22:04:42 +00:00
var isChrome = function () {
return typeof importScripts === "function"
};
function asyncImportScripts(url, success, error) {
logDebug('(asyncImportScripts) Importing: ' + url);
asyncImportChain =
asyncImportChain
.then(function (v) {return fetch(url);})
2016-02-22 22:04:42 +00:00
.then(function (response) {
if(response.ok)
return response.text();
throw new Error("Failed to Fetch: " + url + " - Perhaps your project was cleaned and you haven't recompiled?")
2016-02-22 22:04:42 +00:00
})
.then(function (responseText) {
evaluate(responseText);
fireEvalListenters(url);
success();
return true;
2016-02-22 22:04:42 +00:00
})
.catch(function (e) {
console.error(e);
error();
return true;
2016-02-22 22:04:42 +00:00
});
}
function syncImportScripts(url, success, error) {
try {
importScripts(url);
logDebug('Evaluated: ' + url);
fireEvalListenters(url);
2016-02-22 22:04:42 +00:00
success();
} catch (e) {
console.error(e);
2016-02-22 22:04:42 +00:00
error()
}
}
// Loads js file sync if possible or async.
function importJs(src, success, error) {
var noop = function(){};
success = (typeof success == 'function') ? success : noop;
error = (typeof error == 'function') ? error : noop;
logDebug('(importJs) Importing: ' + src);
2016-02-22 22:04:42 +00:00
if (isChrome()) {
syncImportScripts(src, success, error);
2016-02-22 22:04:42 +00:00
} else {
asyncImportScripts(src, success, error);
2016-02-22 22:04:42 +00:00
}
}
function interceptRequire() {
var oldRequire = window.require;
console.info("Shimming require");
window.require = function (id) {
console.info("Requiring: " + id);
if (externalModules[id]) {
return externalModules[id];
}
return oldRequire(id);
};
}
function serverBaseUrl(host) {
return "http://" + host + ":" + config.serverPort
}
function isUnDefined(x) {
return typeof x == "undefined";
}
// unlikely to happen but it happened to me a couple of times so ...
function assertRootElExists(platform) {
var basicMessage = "ClojureScript project didn't compile, or didn't load correctly.";
if(isUnDefined(env)) {
throw new Error("Critical Error: env namespace not defined - " + basicMessage);
} else if(isUnDefined(env[platform])) {
throw new Error("Critical Error: env." + platform + " namespace not defined - " + basicMessage);
} else if(isUnDefined(env[platform].main)) {
throw new Error("Critical Error: env." + platform + ".main namespace not defined - " + basicMessage);
} else if(isUnDefined(env[platform].main.root_el)) {
throw new Error("Critical Error: env." +
platform + ".main namespace doesn't define a root-el which should hold the root react node of your app.");
}
}
2016-02-22 22:04:42 +00:00
function loadApp(platform, devHost, onLoadCb) {
var fileBasePath = serverBaseUrl((isChrome() ? "localhost" : devHost)) + "/" + config.basePath + platform;
2016-02-22 22:04:42 +00:00
// callback when app is ready to get the reloadable component
var mainJs = `/env/${platform}/main.js`;
evalListeners.waitForFinalEval = function (url) {
2016-02-22 22:04:42 +00:00
if (url.indexOf(mainJs) > -1) {
assertRootElExists(platform);
2016-02-22 22:04:42 +00:00
onLoadCb(env[platform].main.root_el);
console.info('Done loading Clojure app');
delete evalListeners.waitForFinalEval;
2016-02-22 22:04:42 +00:00
}
};
2016-02-22 22:04:42 +00:00
if (typeof goog === "undefined") {
console.info('Loading Closure base.');
2016-02-22 22:04:42 +00:00
interceptRequire();
// need to know base path here
importJs(fileBasePath + '/goog/base.js', function () {
shimBaseGoog(fileBasePath);
importJs(fileBasePath + '/cljs_deps.js');
importJs(fileBasePath + '/goog/deps.js', function () {
2016-02-22 22:04:42 +00:00
// This is needed because of RN packager
// seriously React packager? why.
var googreq = goog.require;
googreq(`env.${platform}.main`);
2016-02-22 22:04:42 +00:00
});
});
}
}
function startApp(appName, platform, devHost) {
ReactNative.AppRegistry.registerComponent(
2016-02-22 22:04:42 +00:00
appName, () => figwheelApp(platform, devHost));
}
function withModules(moduleById) {
externalModules = moduleById;
return self;
}
function figwheelImportScript(uri, callback) {
importJs(uri.toString(),
function () {callback(true);},
function () {callback(false);})
}
2016-02-22 22:04:42 +00:00
// Goog fixes
function shimBaseGoog(basePath) {
2016-02-22 22:04:42 +00:00
console.info('Shimming goog functions.');
goog.basePath = basePath + '/' + config.googBasePath;
goog.global.FIGWHEEL_WEBSOCKET_CLASS = WebSocket;
goog.global.FIGWHEEL_IMPORT_SCRIPT = figwheelImportScript;
2016-02-22 22:04:42 +00:00
goog.writeScriptSrcNode = importJs;
goog.writeScriptTag_ = function (src, optSourceText) {
importJs(src);
return true;
};
}
self = {
withModules: withModules,
start: startApp
};
module.exports = self;