Different behaviour between lein cljsbuild and shadow-cljs

I’m following nbeloglazov’s guide on self-hosted clojurescript: http://nbeloglazov.com/2016/03/05/getting-started-with-self-hosted-cljs-part-1.html
When writing my own load-fn I had some problems with cljsbuild breaking when I eval’d certain libraries, so I wanted to try with shadow-cljs. Now I get some weird differences, that I’m hoping you might be able to help me with. :slight_smile:

Basically, the code:

(ns cave.core
  (:require [cljs.js :as cjs]
            [cljs.pprint :refer [pprint]]))

(enable-console-print!)

(defn print-result
  [res]
  (if (:value res)
    (println (:value res))
    (pprint res)))

(def state (cjs/empty-state))

(cjs/compile-str state  "(ns my.caal) (defn yo [x] (+ x x)) (+ 1 1)" "adding" {:target :nodejs
                                                                               :eval cjs/js-eval} #'print-result)

(defn -main [& args])

Prints this when using lein cljsbuild once && node ...:

goog.provide('my.caal');
goog.require('cljs.core');
my.caal.yo = (function my$caal$yo(x){
return (x + x);
});
((1) + (1));

And prints this using shadow-cljs compile :app && node ...

WARNING: Use of undeclared Var my.caal/defn at line 1 
WARNING: Use of undeclared Var my.caal/yo at line 1 
WARNING: Use of undeclared Var my.caal/x at line 1 
WARNING: Use of undeclared Var my.caal/+ at line 1 
WARNING: Use of undeclared Var my.caal/x at line 1 
WARNING: Use of undeclared Var my.caal/x at line 1 
WARNING: Use of undeclared Var my.caal/+ at line 1 
goog.provide('my.caal');
goog.require('cljs.core');
my.caal.defn.call(null,my.caal.yo,new cljs.core.PersistentVector(null, 1, 5, cljs.core.PersistentVector.EMPTY_NODE, [my.caal.x], null),my.caal._PLUS_.call(null,my.caal.x,my.caal.x));
my.caal._PLUS_.call(null,(1),(1));

How come they’re so different? Shouldn’t the call to cljs.js/compile-str be the same for both of them?

If you want to try yourself I created a repo here: https://github.com/Saikyun/shadowcljsleincljsbuild/tree/master

1 Like

The self-hosted support in shadow-cljs works by requiring an explicit “bootstrap” phase. Before the self-hosted compiler can do any work it must load the analyzer data for some of the core namespaces. With normal CLJS this analyzer data is inlined for cljs.core. This makes the build quite a bit larger and also not very extensible. In shadow-cljs this data is produced separately and can be modified to include dependencies and such.

See https://code.thheller.com/blog/shadow-cljs/2017/10/14/bootstrap-support.html

The relevant bit is the boot/init call at the bottom. Once bootstrapped you can use the compiler normally.

2 Likes

Thanks a lot for the information. :slight_smile: