This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-11-01
Channels
- # announcements (7)
- # babashka (41)
- # beginners (117)
- # cider (3)
- # clj-kondo (145)
- # cljdoc (25)
- # cljs-dev (19)
- # clojure (197)
- # clojure-dev (14)
- # clojure-europe (4)
- # clojure-italy (3)
- # clojure-nl (2)
- # clojure-spec (11)
- # clojure-uk (21)
- # clojuredesign-podcast (5)
- # clojurescript (29)
- # code-reviews (4)
- # cursive (87)
- # data-science (11)
- # datomic (29)
- # duct (2)
- # emacs (10)
- # graalvm (1)
- # lumo (13)
- # malli (2)
- # nrepl (5)
- # off-topic (25)
- # onyx (1)
- # pathom (6)
- # reagent (20)
- # reitit (4)
- # rewrite-clj (7)
- # shadow-cljs (114)
- # spacemacs (16)
I started writing this post as a question but it ended up as just a rant about JS and an appreciation for shadow-cljs. 🙂
I was trying to help a friend that uses TypeScript with a plotting library that I use in CLJS called BokehJS. Since I don't include BokehJS in my build at all (I just put some CDN links into <script>
s and use the created global object), I have never had any problems with it.
The thing is, BokehJS relies on another library called FlatBush and requires it like this:
- TS: import FlatBush = require("flatbush")
- Distributed JS: var FlatBush = require("flatbush")
- Compiled JS: var FlatBush = __webpack_require__(/*! flatbush */ "./node_modules/flatbush/index.js");
- Compiled JS in CDN: var FlatBush = require(394)
On the other hand, FlatBush uses:
- Source JS: export default class Flatbush
- Distributed JS: a bunch of boilerplate that tries to use module.exports
, define
, and just global
- Compiled JS: __webpack_require__.d(__webpack_exports__, "default", function() { return Flatbush; });
- Distributed JS in BokehJS CDN: same boilerplate as in just distributed JS.
Because of this difference, it's easy to use BokehJS as a CDN library but not that easy when you want to have it in your build. Namely, it results in TypeError: FlatBush is not a constructor
because __webpack_require__(/*! flatbush */ "./node_modules/flatbush/index.js")
returns the whole module, which is an object with a field default
.
We spent a few hours on this. Apparently, one must use a Babel plugin called add-module-exports
in such situations, but it also proves to be not an easy task for all sorts of reasons.
Finally, I decided to give it a go in shadow-cljs, expecting to come up with a few new questions.
And it... just worked? Heh. Awesome, thank you!
BTW would it work in vanilla CLJS, without shadow-cljs?
vanilla js doesn't have a module loader
this kinda boils down to different handling of commonjs/import interop, especially in the case of namespace imports
https://2ality.com/2019/04/nodejs-esm-impl.html#interoperability might give some insight on this problem in general
Hi! When I try to evaluate a file with calva or cider, I get an error like this
Evaluating file: nodemap2.cljs
symbol re-frame2.items.nodemap2 already provided by [:shadow.cljs.repl/resource "re_frame2/items/nodemap2.cljs"], conflict with [:shadow.build.classpath/resource "re_frame2/items/nodemap2.cljs"]
{:provide re-frame2.items.nodemap2, :conflict [:shadow.cljs.repl/resource "re_frame2/items/nodemap2.cljs"], :resource-id [:shadow.build.classpath/resource "re_frame2/items/nodemap2.cljs"]}
ExceptionInfo: symbol re-frame2.items.nodemap2 already provided by [:shadow.cljs.repl/resource "re_frame2/items/nodemap2.cljs"], conflict with [:shadow.build.classpath/resource "re_frame2/items/nodemap2.cljs"]
shadow.build.data/add-provide (data.clj:91)
I couldn't find anything on google, any hints?
@p-himik I don't quite follow what you described? just including a bunch of stuff via <script>
tags will work with vanilla CLJS yes?
I meant that including a fully-fledged NPM module has worked with shadow-cljs, despite that a plain JS+Webpack project has some serious issues with, if I understand correctly, combining CommonJS and ES6 modules. Would the same work with vanilla CLJS?
@publicz hmm that looks like the namespace was first defined in the REPL and than loaded via the filesystem. I thought I fixed that a while ago though? are you an on older version maybe?
does anyone have any recommendations on externs debugging? ive worked through a few in the past, and generate-extern
/the webclient always seem to more or less do the trick, but ive encountered a new one that seems like it should be straightforward, but is failing spectacularly. whats strange to me is that its failing at b.createReactClass
. reagent/react was obviously working just fine prior to adding the new library (which is ES6, no less!), so its throwing me off that it seems like that would be any problem. for some detail:
;; imports look like:
(ns some.ns
(:require ,,,
["gsap/TextPlugin" :refer [TextPlugin]] ;; these are coming from npm/node_modules
["gsap" :refer [gsap]]))
;; auto-generated externs look like:
var gsap = {
// ... long, seemingly correct list
}
;; node_modules/gsap/dist/gsap.js module looks like:
// ...
exports.default = gsap;
exports.gsap = gsap;
Object.defineProperty(exports, '__esModule', { value: true });
let me know if anything pops out.to debug externs related things you pretty much don't need to look at node_modules
code at all
let me get the exact error. its basically b is nil
where b
is some munged name, and then when i click into the error, the line that triggers the error (at least says firefox) is a call to b.createReactClass
the runtime error you can sort of figure out by compiling with shadow-cljs release app --pseudo-names
ah, well, with :all
there are ~245 of them (as you warn in the manual 😂), but let me get that instead
so here is the (single) output of :infer-externs :auto
:
------ WARNING #1 - :infer-warning ---------------------------------------------
File: /Users/nolan/dev/nuid/site/src/nuid/site/routes.cljs:59:16
--------------------------------------------------------------------------------
56 | js/location.search
57 | js/location.hash)]
58 | (js/ga "send" "pageview" #js {"page" p}))
59 | (route! (.-token event))))
----------------------^---------------------------------------------------------
Cannot infer target type in expression (. event -token)
--------------------------------------------------------------------------------
60 | (.setEnabled true)))
61 |
--------------------------------------------------------------------------------
which i suppose is relatively straightforward, but whats interesting is that that code has been the same for a long time and hasnt caused release
issues in the pastwarnings will warn you about situations cannot figure out if externs are needed or not
in this case event
looks to be from goog.History
or so. which means no externs are needed, but the compiler can't tell
looking through to see if anything particularly “identifying” pops out, so far it looks like a lot of react/reagent calls
TypeError: "$JSCompiler_temp$jscomp$1970_JSCompiler_temp$jscomp$2730_JSCompiler_temp_const$jscomp$1971_c$jscomp$inline_1109_comp$jscomp$12_f$jscomp$inline_2750_jsprops$jscomp$inline_1100_n$jscomp$186_props$jscomp$inline_1098_tag$jscomp$18_temp__5737__auto__$jscomp$inline_1105$$ is undefined"
is what im seeingim starting to get a feeling it has to do with something perf related that gsap
is doing. its pretty hsyterical how different the package size is with and without pseudo-names
not much interesting use of compiler options, either—just :externs
and :infer-externs
:
:compiler-options {:externs ["externs/isotope.ext.js"
"externs/gsap.ext.js"
"externs/gsap.textplugin.ext.js"
"externs/gsap.easepack.ext.js"]
:infer-externs :auto}
ive just sort of thrown together what seems to have a shot at working, and ship once it does
the token
error you can fix by adding (.-token ^goog event)
or (.-token ^js event)
so without the manaul externs, release
warns about the first lib (`isotope`) that i think will get rid of the need for that first manual extern entry. but doesnt seem to find any problems with the rest. let me see what the error looks like now
and once i had an issue (back in my cljsjs days) where the generated extern was like 400k lines
:infer-externs
output is way, way cleaner. its awesome to start getting a better understanding of how to act on this
so the error is still that opaque monster. my intuition is that something is being pulled out from under reagent/react by gsap. gsap tweens are described by JS objects, so i have quite a few #js { ,,, }
in there.
id rejoice for an immutable-all-the-way-down version of gsap. or just an implementation in cljs. im sure transients would suffice for hot paths, more or less
you can also try shadow-cljs release app --debug
which will give you pseudo-names + source maps
are there any pitfalls (beyond the obvious mutability) of using #js { }
w.r.t. compilation and hinting? or does that play a role
right on. it could be that gsap internally mutates them—which would actually make some sense as an explanation for this error
btw make sure you have a shadow-cljs server
instance running if you call shadow-cljs release
repeatedly
so source maps help identify that it seems to have to do with a ratom
, which is actually super helpful because that limits the space of where things could be going wrong pretty significantly. the bummer is that the trace doesnt even touch any of my files:
TypeError: "<that monster name> is undefined"
$reagent$impl$template$vec_to_elem$$ component.cljs:338
$reagent$impl$template$as_element$$ template.cljs:409
$reagent$impl$template$make_element$$/</< template.cljs:483
$cljs$core$IKVReduce$_kv_reduce$arity$3$ core.cljs:5649
$cljs$core$_kv_reduce$$ core.cljs:700
$cljs$core$reduce_kv$$ core.cljs:2562
$reagent$impl$template$make_element$$ template.cljs:481
$reagent$impl$template$native_element$$ template.cljs:359
$reagent$impl$template$vec_to_elem$$ template.cljs:385
$reagent$impl$template$as_element$$ template.cljs:409
$reagent$impl$component$wrap_render$$ component.cljs:104
$reagent$impl$component$static_fns$$</</< component.cljs:128
$reagent$ratom$deref_capture$$ ratom.cljs:37
$reagent$ratom$run_in_reaction$$ ratom.cljs:504
$reagent$impl$component$static_fns$$< component.cljs:143
React (12) ...
@thheller It was the version! The re-frame template used shadow-cljs 2.8.55, the .69 works. Thanks a lot!
hmm. i wonder if it has to do with fragments. i dont dynamically compute any [<thing> ,,,]
anywhere, but i do make pretty heavy use of fragments, which could be easily changed
thats a great point, and will be the first thing i look into after i eliminate what i must admit would be the most classical of user errors
there was a part of a component that i had wrapped in (when ^boolean js/goog.DEBUG ,,,)
and well…
nothing like feeling dumber than a box of rocks on a friday afternoon, but the upside is that the package size is down