/* * Originally taken from https://github.com/decker405/figwheel-react-native * * @providesModule figwheel-bridge */ var CLOSURE_UNCOMPILED_DEFINES = null; var debugEnabled = true; 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'); 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); } } var figwheelApp = function (platform, devHost) { return createReactClass({ getInitialState: function () { return {loaded: false} }, render: function () { if (!this.state.loaded) { var plainStyle = {flex: 1, alignItems: 'center', justifyContent: 'center'}; return ( Waiting for Figwheel to load files. ); } return this.state.root; }, componentDidMount: function () { var app = this; if (typeof goog === "undefined") { loadApp(platform, devHost, function (appRoot) { app.setState({root: appRoot, loaded: true}); listenToFigwheelMessages(); }); } } }) }; function logDebug(msg) { if (debugEnabled) { console.log(msg); } } var isChrome = function () { return typeof importScripts === "function" }; async function getUrlText(url) { const text = await fetch(url).then(response => { if(!response.ok) { throw new Error("Failed to Fetch: " + url + " - Perhaps your project was cleaned and you haven't recompiled?"); } return response.text() }); return text; } var ATTEMPTS_COUNT = 3; async function asyncImportScripts(url, success, error) { var attempt = 0; var text = await getUrlText(url); while(attempt < ATTEMPTS_COUNT && text.length === 0) { text = await getUrlText(url); ++attempt; } if(!text || 0 === text.length) { console.log("Can't fetch file: ", url) return; } evaluate(text); fireEvalListenters(url); success(); } function syncImportScripts(url, success, error) { try { importScripts(url); logDebug('Evaluated: ' + url); fireEvalListenters(url); success(); } catch (e) { console.error(e); 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); if (isChrome()) { syncImportScripts(src, success, error); } else { asyncImportChain = asyncImportChain.then(function (v) {return asyncImportScripts(src, success, error);}) } } 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."); } } function loadApp(platform, devHost, onLoadCb) { 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.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(); // 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; // NOTE: :closure-defines is not working with RN goog.global.CLOSURE_UNCOMPILED_DEFINES = { "re_frame.trace.trace_enabled_QMARK_":true, "day8.re_frame.tracing.trace_enabled_QMARK_": true }; googreq(`env.${platform}.main`); }); }); } } function startApp(appName, platform, devHost) { ReactNative.AppRegistry.registerComponent( appName, () => figwheelApp(platform, devHost)); } function withModules(moduleById) { externalModules = moduleById; return self; } function figwheelImportScript(uri, callback) { importJs(uri.toString(), function () {callback(true);}, function () {callback(false);}) } // Goog fixes function shimBaseGoog(basePath) { console.info('Shimming goog functions.'); 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); return true; }; } self = { withModules: withModules, start: startApp }; module.exports = self;