-
-
Save jeaye/ba9513abcbe1d0ca0014af8e2a878fb4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/figwheel-bridge.js b/figwheel-bridge.js | |
index e5d9a9c..6f4e285 100644 | |
--- a/figwheel-bridge.js | |
+++ b/figwheel-bridge.js | |
@@ -14,28 +14,58 @@ var config = { | |
}; | |
var React = require('react'); | |
+var createReactClass = require('create-react-class'); | |
var ReactNative = require('react-native'); | |
var WebSocket = require('WebSocket'); | |
var self; | |
-var scriptQueue = []; | |
-var serverHost = null; // will be set dynamically | |
-var fileBasePath = null; // will be set dynamically | |
var evaluate = eval; // This is needed, direct calls to eval does not work (RN packager???) | |
var externalModules = {}; | |
-var evalListeners = [ // Functions to be called after each js file is loaded and evaluated | |
- function (url) { | |
- if (url.indexOf('jsloader') > -1) { | |
- shimJsLoader(); | |
- } | |
- }, | |
- function (url) { | |
- if (url.indexOf('/figwheel/client/socket') > -1) { | |
- setCorrectWebSocketImpl(); | |
- } | |
- }]; | |
+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); | |
+ } | |
+} | |
var figwheelApp = function (platform, devHost) { | |
- return React.createClass({ | |
+ return createReactClass({ | |
getInitialState: function () { | |
return {loaded: false} | |
}, | |
@@ -50,11 +80,13 @@ var figwheelApp = function (platform, devHost) { | |
} | |
return this.state.root; | |
}, | |
+ | |
componentDidMount: function () { | |
var app = this; | |
if (typeof goog === "undefined") { | |
loadApp(platform, devHost, function (appRoot) { | |
- app.setState({root: appRoot, loaded: true}) | |
+ app.setState({root: appRoot, loaded: true}); | |
+ listenToFigwheelMessages(); | |
}); | |
} | |
} | |
@@ -67,50 +99,30 @@ function logDebug(msg) { | |
} | |
} | |
-// evaluates js code ensuring proper ordering | |
-function customEval(url, javascript, success, error) { | |
- if (scriptQueue.length > 0) { | |
- if (scriptQueue[0] === url) { | |
- try { | |
- evaluate(javascript); | |
- logDebug('Evaluated: ' + url); | |
- scriptQueue.shift(); | |
- evalListeners.forEach(function (listener) { | |
- listener(url) | |
- }); | |
- success(); | |
- } catch (e) { | |
- console.error(e); | |
- error(); | |
- } | |
- } else { | |
- setTimeout(function () { | |
- customEval(url, javascript, success, error) | |
- }, 5); | |
- } | |
- } else { | |
- console.error('Something bad happened...'); | |
- error() | |
- } | |
-} | |
- | |
var isChrome = function () { | |
return typeof importScripts === "function" | |
}; | |
function asyncImportScripts(url, success, error) { | |
logDebug('(asyncImportScripts) Importing: ' + url); | |
- scriptQueue.push(url); | |
- fetch(url) | |
+ asyncImportChain = | |
+ asyncImportChain | |
+ .then(function (v) {return fetch(url);}) | |
.then(function (response) { | |
- return response.text() | |
+ if(response.ok) | |
+ return response.text(); | |
+ throw new Error("Failed to Fetch: " + url + " - Perhaps your project was cleaned and you haven't recompiled?") | |
}) | |
.then(function (responseText) { | |
- return customEval(url, responseText, success, error); | |
+ evaluate(responseText); | |
+ fireEvalListenters(url); | |
+ success(); | |
+ return true; | |
}) | |
- .catch(function (error) { | |
- console.error(error); | |
- return error(); | |
+ .catch(function (e) { | |
+ console.error(e); | |
+ error(); | |
+ return true; | |
}); | |
} | |
@@ -118,9 +130,7 @@ function syncImportScripts(url, success, error) { | |
try { | |
importScripts(url); | |
logDebug('Evaluated: ' + url); | |
- evalListeners.forEach(function (listener) { | |
- listener(url) | |
- }); | |
+ fireEvalListenters(url); | |
success(); | |
} catch (e) { | |
console.error(e); | |
@@ -130,22 +140,14 @@ function syncImportScripts(url, success, error) { | |
// Loads js file sync if possible or async. | |
function importJs(src, success, error) { | |
- if (typeof success !== 'function') { | |
- success = function () { | |
- }; | |
- } | |
- if (typeof error !== 'function') { | |
- error = function () { | |
- }; | |
- } | |
- | |
- var file = fileBasePath + '/' + src; | |
- | |
- logDebug('(importJs) Importing: ' + file); | |
+ var noop = function(){}; | |
+ success = (typeof success == 'function') ? success : noop; | |
+ error = (typeof error == 'function') ? error : noop; | |
+ logDebug('(importJs) Importing: ' + src); | |
if (isChrome()) { | |
- syncImportScripts(serverBaseUrl("localhost") + '/' + file, success, error); | |
+ syncImportScripts(src, success, error); | |
} else { | |
- asyncImportScripts(serverBaseUrl(serverHost) + '/' + file, success, error); | |
+ asyncImportScripts(src, success, error); | |
} | |
} | |
@@ -161,66 +163,56 @@ function interceptRequire() { | |
}; | |
} | |
-function compileWarningsToYellowBox() { | |
- var log = window.console.log; | |
- var compileWarningRx = /Figwheel: Compile/; | |
- var compileExceptionRx = /Figwheel: Compile Exception/; | |
- var errorInFileRx = /Error on file/; | |
- var isBuffering = false; | |
- var compileExceptionBuffer = ""; | |
- window.console.log = function (msg) { | |
- log.apply(window.console, arguments); | |
- if (compileExceptionRx.test(msg)) { // enter buffering mode to get all the messages for exception | |
- isBuffering = true; | |
- compileExceptionBuffer = msg + "\n"; | |
- } else if (errorInFileRx.test(msg) && isBuffering) { // exit buffering mode and log buffered messages to YellowBox | |
- isBuffering = false; | |
- console.warn(compileExceptionBuffer + msg); | |
- compileExceptionBuffer = ""; | |
- } else if (isBuffering) { //log messages buffering mode | |
- compileExceptionBuffer += msg + "\n"; | |
- } else if (compileWarningRx.test(msg)) { | |
- console.warn(msg); | |
- } | |
- }; | |
-} | |
- | |
function serverBaseUrl(host) { | |
return "http://" + host + ":" + config.serverPort | |
} | |
-function setCorrectWebSocketImpl() { | |
- figwheel.client.socket.get_websocket_imp = function () { | |
- return WebSocket; | |
- }; | |
+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."); | |
+ } | |
+ } | |
+ | |
function loadApp(platform, devHost, onLoadCb) { | |
- serverHost = devHost; | |
- fileBasePath = config.basePath + platform; | |
+ var fileBasePath = serverBaseUrl((isChrome() ? "localhost" : devHost)) + "/" + config.basePath + platform; | |
// callback when app is ready to get the reloadable component | |
- var mainJs = '/env/' + platform + '/main.js'; | |
- evalListeners.push(function (url) { | |
+ var mainJs = `/env/${platform}/main.js`; | |
+ evalListeners.waitForFinalEval = function (url) { | |
if (url.indexOf(mainJs) > -1) { | |
+ assertRootElExists(platform); | |
onLoadCb(env[platform].main.root_el); | |
console.info('Done loading Clojure app'); | |
+ delete evalListeners.waitForFinalEval; | |
} | |
- }); | |
+ }; | |
if (typeof goog === "undefined") { | |
console.info('Loading Closure base.'); | |
interceptRequire(); | |
- compileWarningsToYellowBox(); | |
- importJs('goog/base.js', function () { | |
- shimBaseGoog(); | |
- importJs('cljs_deps.js'); | |
- importJs('goog/deps.js', function () { | |
+ // 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 () { | |
// This is needed because of RN packager | |
// seriously React packager? why. | |
var googreq = goog.require; | |
- googreq('figwheel.connect.build_' + platform); | |
+ googreq(`env.${platform}.main`); | |
}); | |
}); | |
} | |
@@ -236,10 +228,18 @@ function withModules(moduleById) { | |
return self; | |
} | |
+function figwheelImportScript(uri, callback) { | |
+ importJs(uri.toString(), | |
+ function () {callback(true);}, | |
+ function () {callback(false);}) | |
+} | |
+ | |
// Goog fixes | |
-function shimBaseGoog() { | |
+function shimBaseGoog(basePath) { | |
console.info('Shimming goog functions.'); | |
- goog.basePath = 'goog/'; | |
+ goog.basePath = basePath + '/' + config.googBasePath; | |
+ goog.global.FIGWHEEL_WEBSOCKET_CLASS = WebSocket; | |
+ goog.global.FIGWHEEL_IMPORT_SCRIPT = figwheelImportScript; | |
goog.writeScriptSrcNode = importJs; | |
goog.writeScriptTag_ = function (src, optSourceText) { | |
importJs(src); | |
@@ -247,47 +247,9 @@ function shimBaseGoog() { | |
}; | |
} | |
-// Figwheel fixes | |
-// Used by figwheel - uses importScript to load JS rather than <script>'s | |
-function shimJsLoader() { | |
- console.info('==== Shimming jsloader ===='); | |
- goog.net.jsloader.load = function (uri, options) { | |
- var deferred = { | |
- callbacks: [], | |
- errbacks: [], | |
- addCallback: function (cb) { | |
- deferred.callbacks.push(cb); | |
- }, | |
- addErrback: function (cb) { | |
- deferred.errbacks.push(cb); | |
- }, | |
- callAllCallbacks: function () { | |
- while (deferred.callbacks.length > 0) { | |
- deferred.callbacks.shift()(); | |
- } | |
- }, | |
- callAllErrbacks: function () { | |
- while (deferred.errbacks.length > 0) { | |
- deferred.errbacks.shift()(); | |
- } | |
- } | |
- }; | |
- | |
- // Figwheel needs this to be an async call, | |
- // so that it can add callbacks to deferred | |
- setTimeout(function () { | |
- importJs(uri.getPath(), | |
- deferred.callAllCallbacks, | |
- deferred.callAllErrbacks); | |
- }, 1); | |
- | |
- return deferred; | |
- }; | |
-} | |
- | |
self = { | |
withModules: withModules, | |
start: startApp | |
}; | |
-module.exports = self; | |
\ No newline at end of file | |
+module.exports = self; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment