Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Loading a ClojureScript/Figwheel project with fetch instead of writing script tags.
/*
* This script provides an example of loading a ClojureScript project
* that has been compiled using optimizations level :none.
*
* There are many environments that might not support the default
* method of writing script tags to load files.
*
* I wrote this to solve the problem of loading compiled ClojureScript
* into Chrome extentions.
*
* The key here is that you have to handle asynchronous nature of
* loading files via 'fetch' or 'xmlhttprequest' while using the synchronous
* loading style of goog.base to request all the dependencies in the
* correct order.
*
* Usage:
* You will need the ClojureScript base path. I.E. the path to base.js
* Load a namespace:
*
* ClojureScriptLoader("http://localhost:3449/js/compiled/out/goog/")
* .loadNamespace("example.core")
* .then(function(v){consoel.log("Namspace loaded")})
*
* Load a figwheel build:
*
* ClojureScriptLoader("http://localhost:3449/js/compiled/out/goog/")
* .loadFigwheelBuild("dev")
* .then(function(v){consoel.log("figwheel build loaded")})
*
*/
ClojureScriptLoader = function(googBasePath){
var evaluate = eval;
var debug = false;
var asyncImportChain = Promise.resolve(true);
function logDebug(s) {
if(debug) {
console.log(s);
}
}
function asyncImportScripts(url, success, error) {
logDebug('Importing: ' + url);
// TODO success and error not needed
var noop = function(){};
success = (typeof success == 'function') ? success : noop;
error = (typeof error == 'function') ? error : noop;
asyncImportChain =
asyncImportChain
.then(function (v) {return fetch(url);})
.then(function (response) {
if(response.ok)
return response.text();
throw new Error("Failed to Fetch: " + url)
})
.then(function (responseText) {
console.log("Evaluating: " + url);
evaluate(responseText);
success();
return true;
})
.catch(function (e) {
console.error(e);
error();
return true;
});
}
function importJs(url, opt_sourceText) {
if(opt_sourceText === undefined) {
asyncImportScripts(url);
} else {
evaluate(opt_sourceText);
}
}
function figwheelImportScript(uri, callback) {
asyncImportScripts(uri.toString(),
function () {callback(true);},
function () {callback(false);})
}
function bootstrapGoogBase(googBasePath) {
importJs(googBasePath + "base.js");
asyncImportChain = asyncImportChain.then(function(v) {
goog.basePath = googBasePath;
goog.global.CLOSURE_IMPORT_SCRIPT = importJs;
goog.global.FIGWHEEL_IMPORT_SCRIPT = figwheelImportScript;
});
importJs(googBasePath + "../cljs_deps.js");
importJs(googBasePath + "deps.js");
}
function pollFor(pred,callback) {
setTimeout(function() {
if(pred()) {
callback();
} else {
pollFor(pred,callback);
}
}, 500);
}
function googRequireNamespace(mainNamespace) {
var loaded = false;
asyncImportChain =
asyncImportChain.then(function(v) {
goog.require(mainNamespace);
// the above call is synchronous so all
// the import promises have been chained at this point
// so hang a listener off of the last one to test when everything
// is loaded
asyncImportChain.then(function(v) {
loaded = true;
});
});
return new Promise(function(res,rej){
pollFor(function(){return loaded;},
function(){res(mainNamespace)});
});
}
function loadClojureScriptNamespace(googBasePath, namespace){
bootstrapGoogBase(googBasePath);
return googRequireNamespace(namespace);
}
return {loadNamespace: function(namespace) {
return loadClojureScriptNamespace(googBasePath, namespace);
},
loadFigwheelBuild: function(build_id) {
return loadClojureScriptNamespace(googBasePath,
"figwheel.connect.build_" + build_id);
}}
}
/*
function testIt() {
ClojureScriptLoader("http://localhost:3449/js/compiled/out/goog/")
.loadFigwheelBuild("dev")
.then(function(){console.log("Loaded figwheel")});
}
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment