This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-10-15
Channels
- # announcements (8)
- # beginners (65)
- # calva (25)
- # cider (11)
- # clj-kondo (9)
- # cljsrn (14)
- # clojure (103)
- # clojure-europe (15)
- # clojure-greece (1)
- # clojure-italy (28)
- # clojure-nl (39)
- # clojure-spec (9)
- # clojure-uk (28)
- # clojuredesign-podcast (37)
- # clojurescript (56)
- # cursive (41)
- # data-science (10)
- # datomic (25)
- # duct (1)
- # emacs (1)
- # events (3)
- # figwheel-main (7)
- # fulcro (9)
- # graalvm (7)
- # graphql (10)
- # jobs (2)
- # nrepl (17)
- # off-topic (40)
- # quil (12)
- # reitit (11)
- # remote-jobs (5)
- # rum (2)
- # shadow-cljs (387)
- # sql (22)
- # tools-deps (8)
- # vim (26)
- # xtdb (47)
- # yada (9)
Hi channel, I have somewhat of a noob question: I have a multi-build shadow-cljs project where one of the builds is a nodejs app. When I use shadow-cljs watch [build-name]
, I don't see logging from the app like I do when I do a shadow-cljs compile [build-name] && node app.js
-- any idea why?
well you still need top call node app.js
? you don't seem to be doing that for the watch
? shadow-cljs does not run node for you.
Hey, thank you so much for the response.
I completely missed that. What's the best way to start/stop node as a part of watch
? :build-hooks
?
I just created a simple ClojureScript on Node.js + Shadow-CLJS project and can't seem to get CIDER to notice it. I run cider-jack-in-cljs
and I get a complaint that I'm not in a Clojure project even though I'm editing a CLJS file in a directory which has the shadow-cljs.edn
at parent. Just trying to confirm that one should not be having to jump hoops here and it should "just work".
I get this particular message >>> Are you sure you want to run `cider-jack-in' without a Clojure project? (y or n)
If I no then nothing will start, if I answer yes then I get another error about missing ClojureScript
Hi all, we use shadow-cljs release prod --source-maps
to compile our clojurescript to javascript. We now need to compile the code to make it IE-proof too. The usage of Babel was suggested by a javascript developer here. Is this idiomatic to do in cljs too? If so, how do we include it in the build pipeline?
http://polyfill.io might be your friend before jumping through more transpile hoops.
no. there are already enough ways to do this in shadow-cljs. don't use anything external.
does it create bundles per browser, or put all the polyfills for all browsers into the same thing?
well you can always add external polyfills too ... but that could end up including 3 separate polyfills
if you are talking about the "new" pattern of including things via script with nomodule
and type="module"
then that is not currently supported
but not everything can be polyfilled ... so the problem needs to be solved differently
i’d still try polyfiling legacy as a first step. if it doesn’t work, can drop back to more complex approaches.
depends on which problem. the thing you mention about packaging code for node vs browsers isn’t going to be fixed by it, but stuff like missing array methods will be.
it isn't even a complex approach .. so please don't suggest "custom" approaches BEFORE trying the actual built-in
fair enough - i’ve been using polyfills for this problem for years, so would be curious what approach you have in mind.
using https://babeljs.io/docs/en/babel-preset-env basically
but yeah if you want this to be "optional" you need 2 separate builds. which is kinda dirty currently
cool, but .. that does have the drawbacks of outputting legacy js - so, things like weakmaps would be downpiled..?
yes ... BUT this might be happening regardless of what you are doing outside the build
so the only way to do this properly is to create however many builds you need, targetted at each specific browser and served accordingly
Hmm looking at polyfills that might be the quickest fix for now. Include polyfills in IE browsers only via a script conditional. Then there’s also no configuration needed with separate builds per environment. I understand the code size will be larger and the page will be slower so it’s suboptimal.
Advantage is that also the sentry-sdk javascript will start to work in IE
Because that now crashes with Object.assign that is not recognized
So we did not even get the frontend IE errors in Sentry
if you configure :js-options {:babel-preset-config {:targets {:ie "8"}}}
and use the sentry stuff from npm then everything will likely work as expected
Ah of course thanks
@erwinrooijakkers is the "bad" code part of your CLJS code or part of npm packages? where does it fail?
I’ll have to check
anyone use cljs-http in react-native?
(go (let [response (<! (http/get ""
{:with-credentials? false
:query-params {"since" 135}}))]
(prn (:status response))
(prn (map :login (:body response)))))
i test the code, it output the error
TypeError: Cannot read property 'chan' of undefined
at eval (eval at <anonymous> (), <anonymous>:1:53)
at eval (eval at <anonymous> (), <anonymous>:108:3)
at eval ()
at Object.shadow$cljs$devtools$client$env$repl_call [as repl_call] ()
at Object.shadow$cljs$devtools$client$react_native$repl_invoke [as repl_invoke] ()
at shadow$cljs$devtools$client$react_native$handle_message ()
at eval ()
at Object.shadow$cljs$devtools$client$env$process_next_BANG_ [as process_next_BANG_] ()
at Object.shadow$cljs$devtools$client$env$process_ws_msg [as process_ws_msg] ()
at WebSocket.eval ()
did you eval this at the REPL? well I can see that you did. I mean did you eval the ns
properly too?
it might be a problem in the REPL. or does this also happen if you load the app regularly?
maybe there is some subtle code loading issue. so please try using this in actual code (without the REPL)
@haiyuan.vinurs if you have something reproducible it would help immensly if you add it to https://github.com/thheller/shadow-cljs/issues/574
yeah. I suspect that it works if the code is initially loaded from the build directly
@thheller im seeing an issue and there are two datapoints that I suspect are related
I get this during compilation with “release”: https://gist.github.com/ghaskins/de98175b39fd994e7e5a30740c3a3941
so, if i had to guess, for reasons that are unclear, shadowcljs isnt seeing my reference to .registerComponent and tree-shaking the react-native lib out
I tried to annotate with ^js as I was advised to do in a different context…however, here it doesnt seem to have any effect
17 | (defn register-componnet [name root]
18 | (.registerComponent ^js app-registry name #(r/reactify-component root)))
it is working around issues in the CLJS compiler that have been either fixed for years OR better adressed via :infer-externs
OK, so it may be unneeded. But is it actually a bad practice to still use it? You say that "shadow-cljs can no longer do its job for externs inference" - what are the repercussions?
@U2FRKM4TW at least in my case, it caused advanced compilation to fail by eliding a reference actually in use
To be honest, I have never seen it with cljs-oops. 🙂 And I use NPM quite a lot. Maybe I was just lucky, I dunno.
although I'm not entirely sure why you get an externs inference warning with the ^js
tag added
@U2FRKM4TW have you ever tried NOT using cljs-oops and just straight interop instead? I mean with shadow-cljs, not before?
I used it extensively before I knew about shadow-cljs 😄
@thheller I definitely started using shadow-cljs way past after I started using cljs-oops. I started using oops exactly because I had nasty issues with externs, and I hate cljsjs with passion. Another advantage of cljs-oops, albeit a dubious one, is that Cursive doesn't scream at me with yellow "cannot be resolved"s.
yeah the infer-externs is great, and just being able to use a ^js tag is genius
shadow-cljs sees that and automatically adds AppRegistry
and registerComponent
to the externs
At first I thought the check wasn’t working for me but it turns out I’d put it in the top level shadow-cljs config instead of under :compiler-options
. So there’s that 😄
with cljs-oops its just a string somewhere. I'm assuming that cljs-oops emits something like rn["AppRegistery"]
@thheller Yeah, it generates exactly that:
(defn gen-key-get [obj key]
(case (config/key-get-mode)
:core `(~'js* "(~{}[~{}])" ~obj ~key)
:goog `(goog.object/get ~obj ~key)))
Another pro of using it though - you can write selector like :a.?b.c
instead of its cumbersome equivalent.
Would the issue be remedied if cljs-oops emitted code like x.y
instead of x["y"]
?And also different from what I wrote. 🙂 And cljs-oops also has !
"punch" access.
Why not? Can't it write something like (~'js* "(~{}.~{})" ~obj ~key)
when key
contains a valid JS identifier?
Sorry, I've never dealt with such macros that emit JS code.
yeah can't get into this too much right now but it can't work. especially not when things are called as a function as it was in the example of (defn get-obj [name] (oget+ ReactNative name))
Yeah, I'm not considering the +
variety at all. I don't think externs inference would work here either if the names of keys are truly dynamic.
OK, I read up a bit on externs inference. It seems that if I use cljs-oops then this mechanism is completely unneeded for me. And vice versa - if I don't need any of the additional features of cljs-oops, I can stick to the inference. I don't see a clear winner as it doesn't seem that using externs inference would produce smaller or more optimal code.
Regarding 'in code they are never "truly dynamic"' - of course. But specifying everything in a truly static manner that's visible to shadow-cljs could easily end up in creating an AbstractSingletonProxyFactoryBean
with all of the consequences. Sometimes it's much easier and simpler to just get a key with a check that it exists, in a dynamic manner.
the problem is :advanced
and you need to understand what that is doing. you simply cannot mix using x.y
in one place and x["y"]
in another
Well, nothing is truly future proof. We have multiple sets of versions of dependencies, tools, and languages that work together. Everything else makes our bits rot and we're responsible for keeping them fresh. About that "truly dynamic" comment. Consider this code:
(defn f1 [k]
(oget+ js/Lib k))
(defn f2 [k]
(case k
:a (.-a js/Lib)
:b (.-b js/Lib)
:c (.-c js/Lib)))
We know what js/Lib
is, so we can write f2
, easy. But look at how it compares to f1
, how much more code there is. And if we want all of the keys to be accessible, we have to keep f2
and js/Lib
in sync. Unless there's some option that I'm missing.
Note that replacing (f1 :a)
with just (.-a js/Lib)
is not possible in the general case. E.g. because it immediately removes the ability to iterate over keys.
> the problem is :advanced
and you need to understand what that is doing
Yep, guilty here. I assume that I have some superficial knowledge, but at the same time it completely contradicts what I see - despite all the advanced compilations, I still see my code working with the x["y"]
kind of access. To be fair, I just checked all of the instances of oget
in one of my projects, and about 90% of them access fields that GCC cannot change (DOM API, external libraries). But the rest 10% access fields that come from NPM libraries. And they still work just fine for some reason! Again - maybe I'm just lucky.
> you simply cannot mix using x.y
in one place and x["y"]
in another
If x.y
is a known extern (either via externs inference or via explicit externs) and if my understanding of the concept of externs is anywhere near correct, GCC will not rename y
to anything. In this situation, using x["y"]
should be just fine.
When x.y
is not a known extern, GCC can rename it to anything under :advanced
. And that's exactly where using cljs-oops can bite me, right?
I'm more and more interested in why my usage of NPM libraries doesn't explode...@U2FRKM4TW here is an example:
Oh, thanks, nice!
Huh. It's able to substitute k = 'age', p[key]
with a.key
but it doesn't replace that a.key
with a.a
.
yea, but maybe they don't bother because in many cases the key won't be known at compile time like this simple example, and then advanced would be even harder to reason about
There seems to be a more robust reasoning because the README of cljs-oops states: "Google Closure compiler rewrites string literals to dot property access whenever possible." So it seems to rely on it.
And "String names explicitly prevent minification of key names which must stay intact."
@U2FRKM4TW only for that "callsite" though, which doesn't help you if the property has been renamed elsewhere
Yes, of course.
I just realized that I don't understand yet another thing. Suppose I use (.-field lib)
where lib
comes from NPM. Why do we even need externs here? Let GCC rename field
to something else both in CLJS and in JS code. Or is it simply because GCC cannot really tell without the externs that field
in CLJS is the same field
as in JS?
Externs is for code that is not processed by GCC compiler. It can rename such cases if both the JS and CLJS runs through it. But for shadow, it explicitly does not run npm deps via GCC, because it causes too many problems. I think that is a feature people tried to add to the clojurescript compiler, but they failed
Oh, right, I remember now... It is processed by GCC but only the :simple
level of optimizations is used. Which is exactly why cljs-oops is able to work at all. OK, I think I understand now, thanks!
And consequently, now I see why using cljs-oops indeed adds something to the set of moving parts. Although I'm still unsure whether it's worth it to dump it at this point or not.
Oh, you're right. Yea, I'm kind of in the same boat, (not clj-oops but reagent.interop). Haven't tried converting it to all prop access yet.
yes, the npm code is only running through :simple
in a separate compilation step. thats why we still need externs in the CLJS parts. running everything through advanced doesn't work.
but if we are ever able to run everything through advanced then cljs-oops becomes a problem. because it mixes direct property access with string access
should we get to a future where everything is written in strict ESM, then it may actually become a possibility to run everything through advanced
the issue with cljs-oops is that shadow-cljs can no longer do its job for externs interence and stuff
yeah, always good advice..problem is, im a clojure/jvm guy so im a stranger in a strange land on js
@thheller do you remember the reason that CIDER and shadow don't work when using node-script
target? I thought that was a known issue but don't remember any details
How is javascript source processed? (Not npm deps, I mean .js under /src
). GCC Advanced? (assuming the CLJS is advanced)
@dpsutton not sure what you mean? that should work fine. or are you referring to the https://shadow-cljs.github.io/docs/UsersGuide.html#missing-js-runtime?
@U4YGF4NGM do you mean you have a CIDER + shadow-cljs + node-script running?
typically I would cider-jack-in-clj
and then start the build, but I had many builds in my project
I basically just created the shadow-cljs sample project and changed it to node-script but CIDER doesn't find the project
can you try https://github.com/thheller/shadow-cljs#quick-start and change to node-script?
thanks a bunch anyway! I was looking for confirmation that it should work as advertised without jumping through hoops
1. npx create-cljs-project acme-app
2. Add build to shadow-cljs.edn:
;; shadow-cljs configuration
{:source-paths
["src/dev"
"src/main"
"src/test"]
:dependencies
[]
:builds
{:backend
{:target :node-script
:main
:output-to "target/backend.js"
}}}}
3. Create
(ns )
(defn init []
(prn "init"))
4. in CLJS buffer, M-x cider-jack-in-cljs
5. Select shadow
for REPL type. type backend
for build ID.
6. In a terminal, run node target/backend.js
@U4YGF4NGM it's ok, here it is anyway https://github.com/Macroz/shadow-test
yeah it has nothing to do with #shadow-cljs. shadow-cljs works fine with node-script, I don’t know what dpsutton was talking about
shadow-cljs does indeed work here because I can build the project fine, just that CIDER does not find it
CLJS requires you to run a JS process. for web stuff it’s the browser window. for node it’s the node script
I don't understand how it could work for a while, then again not. I have now three projects and none of them work
I delete-package clj-refactor
, comment it out in my .emacs
, restart emacs, doesn't work
uncomment clj-refactor
in .emacs
then start emacs and let it reinstall clj-refactor
, now it works
so cider-jack-in-cljs
works in shadow-cljs project iff I have just reinstalled clj-refactor
Emacs package
and what exact error are you seeing? i know clj-refactor doesn't know that shadow is a project root. but i think its just a warning that clj-refactor won't be enabled and everything should work fine
exact error is >>> Are you sure you want to run `cider-jack-in' without a Clojure project
the shadow-cljs project is as in the github project earlier in this thread created by the shadow-cljs quickstart guide
logical that it's the reason why the project is not found but surprising that the code is there
thanks @U4YGF4NGM and @dpsutton for working this out with me
@thheller hm, how can I tell? I didn't think about ES6 when writing, but did for example "goog.provide(...)" at the top, and followed the Closure JS rules
should prefer esm whenever possible but that gets a bit tricky when mixing with commonjs (ie. node_modules)
Recently I started extracting shadow-cljs hooks which I used into the library, maybe it will be handy for somebody: https://github.com/beetleman/shadow-cljs-hooks/
I added a similar hook recently btw https://github.com/thheller/shadow-cljs/commit/2d0af30dd064c28e5c74846af5272a9e24276fb0
I wanted to have something similar to https://github.com/jantimon/html-webpack-plugin/blob/master/README.md
and then provide a data file like {"/img/foo.png" "/actual/path/img.hash.png"}
or more complex if needed
otherwise changing an image will require recompiling JS which makes no sense to me 😛
I do not know source code of shadow-cljs but I saw hooks and want something morw user friendly like webpack have;)
I'm not started worked on images. But my idea was similar to your. But I must work on implementation/
Has anyone been able to set up shadow-cljs to work well with AWS lamda functions? I’d like to use Zeit Now. Basically all *.cljs files in the /src/api folder would be compiled into the build/api folder as JS files and then the build files would be shipped via now
I made a netlify example a while ago. zeit is pretty much the same https://github.com/thheller/netlify-cljs
Anyone has a guide about web workers? I read shadow-CLJS documentation but I am not sure if I understand everything and if there is anything else to know to use webworkers
I’m following this for shadow-cljs on zeit now https://github.com/jntn/haiku
not sure how that works either. not sure if they need to be bundled separately or if you can have multiple in one bundle
@thheller Is there anything else to know about webworkers?
Delegate some computation off the main thread to avoid blocking
if you have to serialize too much back and forth that it is often not worth doing in the first place
The communication could be async though right? I am not so concerned about speed, more about blocking the UI
Thanks a lot!! :)
> it would be easy to have shadow-cljs generated multiple files By an existing config? Or something that would need to be built?
something that might need to be built. it is mostly about figuring out how zeit/aws want stuff to be organized
it isn't really documented but the defaults are here https://github.com/thheller/shadow-cljs/tree/master/src/main/shadow/build/targets
anyone in theory could write those since it also accepts symbols :target my.custom/stuff
but given that there is no documentation I don't really expect anyone to write these just yet 😛
For reference, the expected output for zeit is flexible
{ "src": "/api", "dest": "/my-api.js" },
{ "src": "/users", "methods": ["POST"], "dest": "/users-api.js" },
{ "src": "/users/(?<id>[^/]*)", "dest": "/users-api.js?id=$id" },
There is the zero-config option which behaves like:
{
"src": "/api/(.*)",
"dest": "/api/$1"
}
not sure what you mean by that. the /my-api.js
has to be self-contained without require
of its own?
I built this some time ago which basically had the config for stuff in metadata in CLJS
I had plans to build examples for more of these platforms .. but it is really time intense to figure out how they all work
Each function should be considered isolated, I’m assuming deps would need to be bundled with each
You could also either document the symbol usage or open up some way for people to build plugins and download those
the basic setup is always the same, configure stuff, tell the compiler which files it should compile, then "post-process" everything into the right shape
so :node-script
basically just is syntax sugar for :modules {:main {:entries [that.main.ns] :append-js "that.main.ns.main(process.argv)"}}
https://github.com/thheller/shadow-cljs/blob/master/src/main/shadow/build/node.clj#L82-L86
sigh so what's the best way to solve advanced compilation problems (I have set optimization simple, pretty-print, pseudo-names and)
changing to advanced compilation fixes that but then one of the npm dependencies breaks
What are the disadvantage of using deps.edn with shadow-CLJS?