Fork me on GitHub

i was using multiple socket connections to shadow-cljs, thinking to have one with a current namespace set to one namespace and others set to other namespaces. when i do in-ns in one, all of the other connections appear to have their current namespace affected. is this the expected behavior?


this was after doing shadow/repl in each


hey @thheller I have a yarn link:ed package in node_modules (let's call it lul). whenever I modify the contents of lul, and then trigger a compile using (shadow.cljs.devtools.api/watch-compile! :app), it seems that it won't "recompile" lul. is there something I can do to fix this? 🙂


oh wait, maybe this was no fault of shadow-cljs. I restarted yarn, and then it works. never mind then : ]


Very often recently I’ll see the warning “Stale Client! <...>” — is the workaround to always have the Chrome dev tools open and disable the network cache?


I see it only when I restart shadow-cljs server/worker. Do you see it without any restarts?


Sometimes I’ll delete all caches, I’ll restart shadow-cljs and still see it.


Another reason for that is if you serve a stale JS bundle with your web server. At least, I've made such an error in the past where I was caching manifest.edn during development.


@U7PBP4UVA the shadow-cljs caches don't matter for this error. it is your output that is cached or just plain out of date. sometimes people forget to adjust their paths in the HTML after changing the :output-dir in their config?


if you are not using the built-in http servers than it might be your server just using too lax cache headers


chrome caches very aggressively if you don't tell it not to


so most webservers using the defaults just let chrome cache too much


The server should be setting Cache-Control: private, no-cache at the very least


Ah, yes, I’m using my own server. I’ll make sure I’ll set the correct headers — thanks!


BTW, the Stale Client warning is a very good one! I can’t imagine the frustration if caching is in play and you didn’t know. Thanks!


oh it is there because I had that problem so many times before 😛


@thheller This morning, I went to a different computer, updated shadow-cljs to 2.8.61 - started a REPL/node and now the defn with a docstring is returning nil instead of the var - I swear it worked yesterday on my other computer after the fix - is there some sort of cache I'm not seeing?


no. are you sure you upgraded correctly? ie. if you use project.clj or deps.edn just updating package.json isn't enough?


and/or are you sure you restarted after updating? sometimes easy to forget a running server instance. shouldn't say connected to server ... on startup


yeah, i ran npm upgrade and it set the value in the package.json


i'm going to restart the computer


just to make sure


why would you do that? 😛


to make sure I'm not connecting to some weird ghost process - this is super weird 🙂


I know (defn add "add numbers" [a b] (+ a b)) worked on my other machine


just run shadow-cljs stop


always look at the printed messages on startup


$ shadow-cljs node-repl
shadow-cljs - config: /mnt/c/Users/thheller/code/shadow-cljs/shadow-cljs.edn  cli version: 2.8.61  node: v10.13.0
shadow-cljs - connected to server


don't want to see the "connected to server" if you changed dependencies


shadow-cljs - config: /home/chris/Projects/its-nancy-land-app/shadow-cljs.edn  cli version: 2.8.61  node: v10.16.3
shadow-cljs - server not running
<- that's what i get after stopping


ok, and what shadow-cljs - server version: ... running at do you get? 🙂


> shadow-cljs watch frontend server

shadow-cljs - config: /home/chris/Projects/CloudRepo/its-nancy-land-app/shadow-cljs.edn  cli version: 2.8.61  node: v10.16.3


I restarted IntellIj/Cursive


I was using the REPL there


looksl ike that fixed it


that is only the npm part


odd, since I was doing a remote repl connection


shadow-cljs - HTTP server available at http://localhost:8700 shadow-cljs - server version: 2.8.45 running at http://localhost:9630 shadow-cljs - nREPL server started on port 8450


see. 2.8.45, you didn't actually upgrade


yeah, i just saw that too 🙂


ah, i didn't update the project.clj file


thanks for helping me discover that


so I need to update the project.clj and package.json


project.clj is probably enough


the npm package is just for the command line stuff, changes very rarely


okay makes sense


thanks for your help!


You have a gofundme/patreon/something where people can donate?


Thanks - and you also have the Clojure community funding? (I forget what that thing is called)


yes, is currently funding me

👍 4

I have a JS library (closure compatible) that uses ES6 classes: class Foo extends Bar {} that compiles to $jscomp.inherits(Foo, Bar); which means that it needs $jscomp (which is the es6 closure runtime polyfill) defined. This runtime is only included with simple optimizations (and up) per but shadow-cljs in development mode is in :none mode thus $jscomp is undefined.


Now, say that I have extracted this $jscomp polyfill into a js file (as this guy suggested ). Is there any way I can include this in my dev build?


do you happen to have a reproducible example for that?


I would suggest just bumping the language level so it just stays ES6


either everywhere or just for dev


hmm no atm


ok yeah that would be a solution


:compiler-options {:output-feature-set :es6} or :dev {:compiler-options {:output-feature-set :es6}}


(in the build config of course)


Because I wanted to use this library in a web worker I also tried to importScripts the $jscomp runtime but it fails with weird errors


yeah the polyfills handling in :dev builds is tricky and buggy


depending on which version you are on you might need to delete the .shadow-cljs/builds cache


I fixed a bug in the last release where cache wasn't invalidated if you changed :output-feature-set


ok I will clear the cache and try again


that worked 🙂 thanks @thheller

👍 4

i've got a CIDER nrepl session connected to a shadow watch build cljs-repl, with a browser attached... mostly seems to work fine, but print output seems to be lost - i.e. (prn "foo") executes but i can't see the output - can i configure something to get print output ?


try shadow-cljs cljs-repl the-build and (prn :foo)


if that prints correctly its a cider issue


if it doesn't its a CLJS side issue, could be caused by calling (enable-console-print!) after shadow-cljs initialized its logging


same result with shadow-cljs cljs-repl


ok, so do you mess with set-print-fn! in any way? directly or indirectly via enable-console-print! or some such?


you should basically never call any of those 😛


ok, i found the offending (enable-console-print!) in our codebase 😊


shadow-cljs already does the setup for all of that


i killed it with 🔥

👍 4
🔥 4
Wes Hall15:10:05

Hello you fine people. I am afraid I have a potentially dumb question... 🙂

Wes Hall15:10:43

I am wondering if anybody is doing hiccup->html generation as part of their shadow-cljs build and how they are going about it. I am only just starting to play with the tool (fine, fine work by the way). My M.O. with this stuff is often to dive in and start trying to use something for things it wasn't intended for 😉, but i had this bright idea of using a build hook to generate a little index.html into the build directory on compile. Potentially I would do this with some garden->css too, so I have one watcher process that just assembles everything. I am sure this probably constitutes ridiculous abuse of the tool, but in any case, it doesn't really work because build hooks are not watched or reloaded on change, so you have to restart the watch process for any changes to take effect thus somewhat nullifying the concept. I suspect the right answer here is to stop trying to use shadow-cljs for stuff it wasn't really intended to do and just use some kind of npm concurrently trick to start seperate watcher processes for each thing, but I thought I would check in with the community here first in case there is a better option I am missing.


My initial thought is that shadow-cljs is mostly for compiling to JS and that can be plugged into other pipes for hiccup->html


so separate watchers would seem to be the way to go to keep things separate


but there are hooks you can add at development time that can run everytime code is reloaded


:before-load-async in your build configuration


under :devtools


I'm playing with the serverless framework, created a toy project: Allthough the deployed endpoint works fine I'm struggling to get a nice local workflow going. Serverless provides an offline plugin for just that, but with the shadow-cljs in a watch mode I get compilation errors after touching the sources:

Error: No protocol method ISwap.-swap! defined for type cljs.core/Atom: [object Object]
    at Object.cljs$core$missing_protocol [as missing_protocol] (/ClojureProjects/shadow-cljs-serverless/api/cljs-runtime/cljs/core.cljs:312:3)
    at Function.cljs$core$IFn$_invoke$arity$4 (/ClojureProjects/shadow-cljs-serverless/api/cljs-runtime/cljs/core.cljs:854:1)
    at Function.cljs$core$IFn$_invoke$arity$4 (/ClojureProjects/shadow-cljs-serverless/api/cljs-runtime/cljs/core.cljs:4509:6)
    at Object.cljs$spec$alpha$def_impl [as def_impl] (/ClojureProjects/shadow-cljs-serverless/api/cljs-runtime/cljs/spec/alpha.cljs:312:5)
    at /ClojureProjects/shadow-cljs-serverless/api/cljs-runtime/cljs/spec/alpha.cljs:1384:1
    at global.SHADOW_IMPORT (/ClojureProjects/shadow-cljs-serverless/api/graphql.js:70:44)

Wes Hall15:10:04

Yeah, I looked at those too but I assume they run inside the browser when it is the target meaning quite limited access to the file system. I think you are probably right re: other pipelines. I think my aversion is just in having multiple processes all doing the "watch" work. Would probably be nice to have a single generic watcher with plugins.


@wesley.hall what do you mean by this? > it doesn't really work because build hooks are not watched or reloaded on change


Do you mean the browser doesn't reload with your settings, or that there is no hook that executes at the time you want?


(I think I am doing similar things to what you want to do.)

Wes Hall16:10:02

@isak The lifecycle hooks are written in clojure but these hooks are loaded at the start of the watch processes and any subsequent changes are not loaded until you restart the watch process. You can edit the clj files but triggering a new build (by changing the cljs files), while it does cause the hooks to be rerun, it is code that was read at the start of the watch process and not the latest changes that are executed.


shadow-cljs is meant to turn CLJS into JS


it doesn’t really support much else


if you’re trying to use hiccup in your CLJS code, that will get compiled just fine. if you’re trying to compile hiccup to some static html, outside of your CLJS code, it doesn’t currently support that


the question comes up a lot when people want to add Sass compilation, and thheller usually tells people it’s a better idea to run a separate watcher


@wesley.hall If you want dynamic reload of the hook itself, that may be tricky, but if you are able to write a function that watches+writes (and can remain stable), that is pretty easy to add in via the hooks

Wes Hall16:10:13

@lilactown Cool, I suspected that was probably the case. It's probably more a matter of me watching to "reuse" the watch process than start other ones. The docs on the build hooks do say something to the effect of, "this is a very powerful extension point", so I am probably just testing how far I can push this, but fully understand what is meant here is, "extend it to do more cljs->js stuff", rather than abuse it for other usages.


I'd disagree with @lilactown on this, I found it very easy to add other watchers to, and other compile steps


it would be good to probably have a real-world use case for build hooks


I think you can even get shadow-cljs to reload the page at arbitrary times, stuff not related to it's own build events, since you have access to the build runtime


AFAICT build hooks are good for triggering additional effects based on changes to CLJS files

Wes Hall16:10:19

@isak Yeah, I had thought about writing the hooks as relatively static code that does the dynamic loading within them. I can see how that might be done fairly easily. Then we probably get into best practice stuff.


they are not good for, e.g. reading and compiling files that don’t show up on the classpath (.scss/.graphql/etc.)


it sounds like what you’re trying to do is a little different, but similar in spirit to those things


you want to watch a .clj file and dynamically reload it on change

Wes Hall16:10:59

In the garden case, very similar to .scss yes


and do some additional compilation


I think there are two challenges to this: 1. you will need to figure out how to get shadow-cljs to watch those files (probably the stopper) 2. you will need to read the file from disk on each change


here is what I added at work to watch our custom CSS build:


so then that just starts that watcher in the dev build


so you’re starting your own watcher and shelling out to the external process


I suppose it’s nice because the code gets run when shadow-cljs is run


I think the problem you were running into @wesley.hall is you need some way to reload the Clojure files you’re interested in compiling. I’m not sure the best way to do that


it might be reading them in from disk, it might be reloading them from the classpath, not sure

Wes Hall16:10:04

It's actually simpler than this in my little test case. My clj hook code literally just says, "convert this embedded hiccup to html and spit it to this file". It's because the hiccup is embedded in the hook clj code that means it doesn't "see" the changes until I restart the watcher. I am kind of leaning towards just running different watch processes for each thing now. I think I was more or less here just to see if somebody was going to say, "oh, just set :reload-hooks true in the build config" 🙂. The interesting thing though is both @isak and myself are really just looking for ways to embed our own little jobs into the watcher process. This does somewhat support my previous idea that there might be a space for a generic watcher application into which jobs can be configured. i.e. "if these files change, pass them through shadow-cljs, if these ones change, pass them through the garden compiler" etc etc. I've had this effect before actually. The delights gain by a solid live reload / watcher process are so great that you want the process to do "all the things" 🙂

Wes Hall16:10:57

In @isak's case, I guess since he is starting a seperate watcher in his code, he could equally do that using an npm script and concurrently and keep the two things entirely separate. So that might be a matter of taste.

Wes Hall16:10:54

In any case, thank you gentlemen. I now have a clearer picture of what I need to look at. Appreciate the opportunity to bounce it around a little. As always, community delivers 🙂


@wesley.hall Agreed, and I think Thomas was talking about adding something like that eventually ( file-watch -> run arbitrary code/process helper). Though the good thing is it isn't too bad to add it yourself in the meantime.

👍 4

@isak you :build-hook things "leaks". it is never shutdown if you edit the build config file while watch is running


@thheller Hmm ok, is there an event for that? For :configure, it says it is only run once


not currently no. but I also don't get why this is running as part of a build?


It's so people don't need to start multiple consoles to run the build in development mode


(full build, not just clojurescript)


shadow-cljs run dev/setup and

(ns dev
    [shadow.cljs.devtools.api :as shadow]))

(defn setup
  {:shadow/requires-server true}
  (shadow/watch :your-build))
seems like a better choice?


hm, it is not obvious to me why. Also, when you say it leaks, do you mean if the hook is removed in a shadow-cljs.edn update?


if you modify the build-config while watch is running the old build-state is thrown away and a new one is created with the new config


it looks like your mk-watcher starts a background thread that will never exit?


ok, but if it is thrown away, then how does it leak?


leaks in the sense that it doesn't get thrown away immediately, having to wait for the GC?


that ws is never closed. all associated threads will continue running


(and since you kept a copy of the build-state that is also never GC'd)


you could use the plugin support, since that actually supports lifecycles fully. but it isn't documented or tested much 😛


basically the :start fn is called when the server starts, and :stop is called when it ends


sounds good. Does it get any arguments (e.g., to detect dev vs release)?


it isn't coupled to a build


it is coupled to server-mode


which you probably don't use for release builds


so the plugin isn't started if you just run shadow-cljs release your-build

Wes Hall18:10:06

Ok, that plugin feature is definitely nice 🙂


@isak @wesley.hall my challenge for both of you would be to build whatever you want to build AS IF shadow-cljs did not exist at all. just CLJ functions and then show me why it needs to be intergrated into a shadow-cljs build 😛


typically if you just have a CLJ function turns out to be a enough. since you can run that from the REPL or CLI


hooking it up so it is executed when shadow-cljs runs is then the easy step


but coupling yourself too much to shadow-cljs specifics makes it really hard to run things independently


and in my experience you always want to be able to run/debug things independently


don't make things more complicated than they need to be just because it might safe you 5seconds of time per day

Wes Hall18:10:19

@thheller I just want to hijack the workflow. It's probably just cheating. The "wrapper" html is a stupidly trivial example anyway because it changes so rarely. For garden and css (which is obviously more dynamic), I can just use one of the (apparently several) services built for watching and compiling garden. It's just a matter of it feeling nicer to do in a single process. It's not really that much of a big deal. I don't really want you try to convince you to create another webpack, because at the same time I am asking all these questions, I am acutely aware of the impact trying to support every little usecase has on projects like this. Just really wanted to know if there was a cleverer solution that I was missing that was in regular use. When you have a compilation tool like this, that ultimately moves stuff from src to dist, the obvious problem is that it's now not just my js that needs to go into dist, but probably some html and some css (and indeed, images, fonts, etc etc). I am just trying to figure out the neatest way to get them there.


@wesley.hall I understand what you want but the question I have is "why does copying CLJS to dist have to be coupled to copying html/css to dist?" why can't they be 3 different steps?


I know your answer .. I just want to challenge your assumptions. you want to run one command, so my answer is that that one command shouldn't be shadow-cljs but something else that triggers those 3 steps (one being shadow-cljs)


I don't even think webpack is doing this is a good way


have you ever looked at a 2 year old webpack config that nobody can upgrade anymore?

Wes Hall18:10:41

Yes, which is one of my two possible solution right now. The trouble there of course is that the "one step up" from shadow in the "standard" setup is npm, which dumps me in node land, which not only makes me sad, but means not only do I need a new process but I need a new JVM...

Wes Hall18:10:16

@thheller Definitely not advocating for webpack 🙂


set the shadow--cljs run example I posted above


that gets you one JVM doing things


which you could also replace by clj -m

Wes Hall19:10:41

@thheller Yeah, I saw that. The run option I actually didn't know about. Is this documented anywhere? Does it just clj with shadow on the classpath or is there more to it than that?


it is a bit more but not much

Wes Hall19:10:40

Ahhh, must have missed this in the docs. Thank you 🙂

Wes Hall19:10:17

I do want to reiterate by the way, how great shadow-cljs is. I am really just working on a toy project here, playing with the tools and seeing how cljs to the browser looks in 2019 because i've been buried in backend stuff for a while. Clojurescript and the ecosystem around it has come on a long way apparently, and I appreciate greatly the part you have played in that. Cheers 🙂

👍 8
Wes Hall19:10:03

...and, yes, this clj-run functionality appears to be exactly what I am looking for. Thanks for that too.


@thheller Agreed for that example, appreciate the tips.