This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-08-10
Channels
- # announcements (2)
- # beginners (37)
- # boot (1)
- # cider (76)
- # clara (14)
- # cljs-dev (132)
- # cljsjs (1)
- # cljsrn (2)
- # clojure (18)
- # clojure-colombia (5)
- # clojure-finland (1)
- # clojure-hamburg (1)
- # clojure-italy (2)
- # clojure-nl (8)
- # clojure-russia (1)
- # clojure-spec (28)
- # clojure-uk (85)
- # clojurescript (84)
- # code-reviews (25)
- # cursive (10)
- # data-science (3)
- # datomic (30)
- # editors (1)
- # emacs (3)
- # fulcro (106)
- # graphql (4)
- # hyperfiddle (26)
- # jobs (2)
- # jobs-discuss (124)
- # keechma (3)
- # leiningen (1)
- # lumo (6)
- # off-topic (5)
- # other-lisps (5)
- # reagent (5)
- # ring-swagger (4)
- # shadow-cljs (140)
- # spacemacs (22)
- # specter (2)
- # sql (48)
- # tools-deps (78)
- # vim (7)
@mfikes I’m slightly confused by https://dev.clojure.org/jira/browse/CLJS-2859
Assuming you are actually referring to https://dev.clojure.org/jira/browse/CLJS-2854
What I observed (simply by adding a few util/debug-prn
s), is that this causes goog.require
to be evaluated
https://github.com/clojure/clojurescript/blob/9923fadc659dc1694c88371d46791da8881c026b/src/main/clojure/cljs/compiler.cljc#L1250
right before this causes the code that looks like
cljs.user.global$module$my_lib = goog.global["mylib"];
to be emitted
https://github.com/clojure/clojurescript/blob/9923fadc659dc1694c88371d46791da8881c026b/src/main/clojure/cljs/compiler.cljc#L1273
And in the browser the goog.require
simply writes out a script tag or somesuch (need to re-look at it), so the assignment is done before the lib is actually loadedIt in fact may not really be a “race”, as it may always lose, and be in the wrong order
My thinking comes from some of the stuff around https://github.com/clojure/clojurescript/blob/9923fadc659dc1694c88371d46791da8881c026b/src/main/cljs/clojure/browser/repl.cljs#L194-L201
we compute the dependencies in order, my feeling is that goog.require shouldn’t actually do anything
Oh, when digging into it, I added some stuff to console log directly from the foreign lib (`mylib.js` in the ticket), and also at the point where cljs.user.global$module$my_lib = goog.global["mylib"];
is executed, and that’s where I could see the assignment occuring prior to the execution of mylib.js
In short goog.global["mylib"]
is null
when the assignment is performed.
My takeaway was that, even though requires / loading occur in a well defined order, we have this assignment sitting in the middle of all of it
All I’m really confident in saying is that we emit code that reads from goog.global["mylib"]
prior to it being set.
Here is a small example (which doesn’t explain why, but it is a step towards the problem):
$ clj -m cljs.main -co co.edn -re node -r
cljs.user=> (require 'my-lib)
my-lib code executing
emit global export
nil
^ That is in node, but if I do the same in the browser, the two lines logged are reversed
the important point is what you’re observing doesn’t reconcile with how Google Closure goog.require
should work - unless we overlooked something
In the REPL, goog.require
isn’t fake, right? It causes code to be loaded in the end.
Our monkey-patched version ultimately calls js/goog.require__
which ultimately evaluates
goog.writeScripts_(path);
Yeah, this sequence
cljs.user=> (.addDependency js/goog "../mylib.js" #js ["my_lib"] #js [] #js {"foreign-lib" true})
nil
cljs.user=> (js/goog.require "my_lib")
nil
cljs.user=> js/mylib
#js {:abc 3}
This is consistent with it being async (done in a fresh browser REPL):
cljs.user=> (.addDependency js/goog "../mylib.js" #js ["my_lib"] #js [] #js {"foreign-lib" true})
nil
cljs.user=> (.-mylib js/goog.global)
nil
cljs.user=> (do (js/goog.require "my_lib") (.-mylib js/goog.global))
nil
cljs.user=> (.-mylib js/goog.global)
#js {:abc 3}
(do (js/goog.require "my_lib") (.-mylib js/goog.global))
is the same as the failure mode… hold on…
right but I mean we don’t care about (do (js/goog.require "my_lib") (.-mylib js/goog.global))
case
https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/repl.cljc#L241-L248
As best I can ascertain these three JavaScript statements get executed
goog.addDependency("../mylib.js", ['my_lib'], [], {'foreign-lib': true});
goog.require('my_lib');
cljs.user.global$module$my_lib = goog.global["mylib"];
with the side effect of the 2nd statement emperically being asynchronousI had seen language like the following in Closure library https://github.com/google/closure-library/blob/master/closure/goog/base.js#L779-L780
which made me believe that goog.require
can essentially be async
The (do (js/goog.require "my_lib") (.-mylib js/goog.global))
example is meant to imitate the 2nd and 3rd lines (at least the salient aspect)
Yes, the transitive aspect probably explains why my last comment in https://dev.clojure.org/jira/browse/CLJS-2854 works
Or, maybe my last comment in that ticket is something else, more like the compiled case. Hrm.
All the patch in CLJS-2854 tries to do is defer
cljs.user.global$module$my_lib = goog.global["mylib"];
when you are in a REPL. (And the patch feels very hackish.)An alternative way to solve it would be to think of the global export initialization code
cljs.user.global$module$my_lib = goog.global["mylib"];
as a tiny library which depends on the foreign library, and then tell Closure to load that tiny library instead of the foreign library.@mfikes did you try wrapping the global export statement in a setTimeout
0 in the REPL case?
the thing I don’t like about 2854, is pushing that responsibility onto REPL implementers
Oh, yeah, that had crossed my mind, but I didn’t think of using 0
. Hmm. Maybe sufficient for 1 tick later. 🙂
Definitely. I had already pinged Bruce so he is aware of that negative aspect of 2854. But there are other Bruce’s out there. 😞
Dang. 0 might be too fast (it failed). I tried 1000, 100, 10 and all of those worked, so it is like rolling the dice. I also tried setImmediate, but Safari evidently doesn’t have that.
(Assuming that this matters when you do a require, and then type some other form that depsnds on that require, all at the REPL. :)
I think this method is a good solution in the near term even though it is non-deterministic.
and actually a larger delay makes sense in this context because it's a REPL interaction
Well, the trick will be to check to see if (:repl-env env)
is non-`nil` and only do it in that case
I’ll put together a tentative patch that tries 100 for now. We can adjust (or some up with something more sophisticated)
By the way, I love that I can test CLJS-2854 downstream with Figwheel by replacing
clj -m cljs.main -co co.edn -r
with
clj -m figwheel.main -co co.edn -r