Details
-
Type:
Enhancement
-
Status:
Resolved
-
Priority:
Trivial
-
Resolution: Completed
-
Affects Version/s: None
-
Fix Version/s: None
-
Component/s: None
-
Labels:None
Description
External tools often have the need to redefine ClojureScript's
cljs.core.print-fn var in order to get printing in different
runtimes working. In a browser environment it is usually
redefined to console.log for example.
This works fine in whitespace and simple mode because the
cljs.core.print-fn var is accessible from the outside
environment. However, in advanced mode the cljs.core.print-fn
gets renamed and external tools can't access this var anymore.
Since it looks like redefining cljs.core.print-fn is common
task, it would make sense to add ^:export to it's meta data to
prevent the Goolge Closure Compiler from renaming it.
References:
Unfortunatly it's not that easy. Advanced compilation is quite a beast. I added :export true to the meta data of *print-fn*, build a ClojureScript release and put it into my local Maven repo. Changed the ClojureScript version in clojurescript.test and recompiled. The var is exported: function s() { d(Error("No *print-fn* fn set for evaluation environment")) } fa("cljs.core._STAR_print_fn_STAR_", s); The fn "fa" is the goog/exportSymbol fn, which (i guess) builds up some nested object and finally assigns "s" under the proper path. function fa(a, b) { var c = a.split("."), e = ba; !(c[0] in e) && e.execScript && e.execScript("var " + c[0]); for(var f;c.length && (f = c.shift());) { !c.length && b !== g ? e[f] = b : e = e[f] ? e[f] : e[f] = {} } } Assigning cljs.core._STAR_print_fn_STAR_ from the outside just points anyone actually calling cljs.core._STAR_print_fn_STAR_ to the new function. I tried this in clojurescript.test: #!/usr/bin/env phantomjs // reusable phantomjs script for running clojurescript.test tests // see http://github.com/cemerick/clojurescript.test for more info var p = require('webpage').create(); p.injectJs(require('system').args[1]); p.onConsoleMessage = function (x) { console.log(x); }; // p.evaluate(function () { // cemerick.cljs.test.set_print_fn(); // }); p.evaluate(function () { // Try to redefine cljs.core._STAR_print_fn_STAR_ console.log(cljs.core._STAR_print_fn_STAR_); cljs.core._STAR_print_fn_STAR_ = function (x) { x = x.replace(/\n/g, ""); console.log(x); }; // BUT THIS HAS TO BE REDEFINED console.log(s); s = function (x) { x = x.replace(/\n/g, ""); console.log(x); }; }); var success = p.evaluate(function () { var results = cemerick.cljs.test.run_all_tests(); console.log(results); return cemerick.cljs.test.successful_QMARK_(results); }); phantom.exit(success ? 0 : 1); Anyone else calling the renamed "s" still has a problem. We actually need to reasign "s", which external tools still don't know by name. In clojurescript.test I solved this dilemma by providing something like this: (defn ^:export set-print-fn! [f] (set! cljs.core.*print-fn* f)) and pass the print fn as an argument cemerick.cljs.test.set_print_fn_BANG_(function(x) { x = x.replace(/\n/g, ""); console.log(x); }); In summary, exporting *print-fn* does not help as I initially thought. You have to get to the renamed var. We could provide the mentioned set-print-fn! from above for tools to use. But then this could be done by tools themself. Any other thoughts?Unfortunatly it's not that easy. Advanced compilation is quite a beast. I added :export true to the meta data of *print-fn*, build a ClojureScript release and put it into my local Maven repo. Changed the ClojureScript version in clojurescript.test and recompiled. The var is exported: function s() { d(Error("No *print-fn* fn set for evaluation environment")) } fa("cljs.core._STAR_print_fn_STAR_", s); The fn "fa" is the goog/exportSymbol fn, which (i guess) builds up some nested object and finally assigns "s" under the proper path. function fa(a, b) { var c = a.split("."), e = ba; !(c[0] in e) && e.execScript && e.execScript("var " + c[0]); for(var f;c.length && (f = c.shift());) { !c.length && b !== g ? e[f] = b : e = e[f] ? e[f] : e[f] = {} } } Assigning cljs.core._STAR_print_fn_STAR_ from the outside just points anyone actually calling cljs.core._STAR_print_fn_STAR_ to the new function. I tried this in clojurescript.test: #!/usr/bin/env phantomjs // reusable phantomjs script for running clojurescript.test tests // see http://github.com/cemerick/clojurescript.test for more info var p = require('webpage').create(); p.injectJs(require('system').args[1]); p.onConsoleMessage = function (x) { console.log(x); }; // p.evaluate(function () { // cemerick.cljs.test.set_print_fn(); // }); p.evaluate(function () { // Try to redefine cljs.core._STAR_print_fn_STAR_ console.log(cljs.core._STAR_print_fn_STAR_); cljs.core._STAR_print_fn_STAR_ = function (x) { x = x.replace(/\n/g, ""); console.log(x); }; // BUT THIS HAS TO BE REDEFINED console.log(s); s = function (x) { x = x.replace(/\n/g, ""); console.log(x); }; }); var success = p.evaluate(function () { var results = cemerick.cljs.test.run_all_tests(); console.log(results); return cemerick.cljs.test.successful_QMARK_(results); }); phantom.exit(success ? 0 : 1); Anyone else calling the renamed "s" still has a problem. We actually need to reasign "s", which external tools still don't know by name. In clojurescript.test I solved this dilemma by providing something like this: (defn ^:export set-print-fn! [f] (set! cljs.core.*print-fn* f)) and pass the print fn as an argument cemerick.cljs.test.set_print_fn_BANG_(function(x) { x = x.replace(/\n/g, ""); console.log(x); }); In summary, exporting *print-fn* does not help as I initially thought. You have to get to the renamed var. We could provide the mentioned set-print-fn! from above for tools to use. But then this could be done by tools themself. Any other thoughts?