hello
i have a question regarding js libs that probably has been asked many time
i did search the answers but still find myself a bit confused
my goal is to manage a monorepo with clj + cljs (with js libs)
i know how shadow resolve js libs in a project (using node resolution from node_modules)
but i dont understand what happens if i have a project p1 with js libs in its package.json, and another project p2 that depends on p1 via deps.edn
when i build p2 - would the js deps of p1 be installed?
more general case - if i publish a cljs lib that depends on js lib, should the consumer install that js lib or cljs / shadow resolve it somehow?
there's a support for deps.cljs
i've been thinking about adding a support for package.json - searching for it in the root classpath and using npm install --no-save
in addition to that, extend the support for js tool in the js-options such as
:save-command "npm install" / "npm install --no-save"
or even more customizable by allowing the user choose the package manager (yarn / pnpm) and its commands
:js-resolution {:provider "yarn"
:install "yarn add --no-save"}
what do you think about this direction?not interested to add anything to shadow-cljs in this regard. it just has no end ... you are asking for yarn. the next one is asking for bun. then quickjs. it just never ends. ultimately it isn't a shadow-cljs concern where your packages come from
so at most there should be a "get :npm-deps from deps.cljs files on the classpath". this exists already, and exposing this in a nicer way would be something I consider. actually running and installing the stuff is a no. I already regret what currently exists.
heck shadow-cljs doesn't even care where your CLJS/CLJ dependencies come from. thats why you can just use deps.edn or project.clj. I see this as a plus
running and installing can be done via shadow-hooks - it's a great feature i see a duplication with deps.cljs and package.json i think supporting package.json (without installation or anything) could be useful for reducing deps.cljs generation (or maintanance) and duplication
we can't support package.json since npm doesn't look into .jar files or knows what a classpath is
and package.json files aren't on the classpath and you are insane if you put them there given how npm works you'd also have node_modules on the classpath and in your jars ...
hooks aren't supposed to do this, so abusing them for this is bad. like actually really terrible bad.
all dependency installation should have been done before shadow-cljs was even started if you ask me
when you execute a shadow build with deps.edn enabled, it does download / install all the deps i want to couple it with js in the same mechanism i'm not sure how tho, can't decide on the tradeoffs just yet
ok, small distinction to make to clarify. there is a shadow-cljs npm package, and shadow-cljs clojure library. the npm shadow-cljs command only launches shadow-cljs. it never "builds" anything. in your case it launches the actual shadow-cljs tool via clojure and deps.edn
so, the building part I'm talking about never downloads or starts anything. it happens before.
maybe take a look at my workflow I described and use in every of my projects
https://code.thheller.com/blog/shadow-cljs/2024/10/18/fullstack-cljs-workflow-with-shadow-cljs.html
https://code.thheller.com/blog/shadow-cljs/2024/10/30/supercharging-the-repl-workflow.html
I actually modernized this a bit, and this is the current "template" https://github.com/thheller/shadow-stack-mvp
clj -M:dev:start is the command I use to start stuff. it runs https://github.com/thheller/shadow-stack-mvp/blob/main/src/dev/repl.clj#L51-L53
imagine
(defn -main [& args]
(manage-my-npm-deps)
(start)
(main/repl :init repl-init))that satifies my "do it before shadow-cljs starts" recommendation and also completely disappears in the workflow (no extra step)
this is my workflow of course. yours can look different. all I'm saying shadow-cljs doesn't need to do anything for any of this, and I regret that it does something.
yes i understand now it's clojure + deps.edn that actually download the deps, not shadow
shadow-cljs resolves from where it was started. so mono/p1/shadow-cljs.edn resolves in mono/p1/node_modules, when shadow-cljs was started in mono/p1
mono/p2 can have a deps.cljs on its classpath, so mono/p2/src/main/deps.cljs, that can specify {:npm-deps {"foo" "1.0.1"}}. that will cause shadow-cljs to run npm install foo@1.0.1 in the mono/p1 folder
exactly what i was about to ask https://shadow-cljs.github.io/docs/UsersGuide.html#publish-deps-cljs so if i update lib version - shadow will track it and rerun the npm install ?
no, it will not. if p1 already has the dependency installed (in its package.json) shadow will assume that is what you want and not install anything
is it possible to force update? i find it as a different mechanism from deps.edn for clj (or npm for js) what's the philosophy behind it? what escape hatch can we do? we want to keep our packages in sync - the same way deps.edn works for clj
it works exactly the same way in clj ... if you change p2 deps.edn {:deps {foo/bar {:mvn/version "2"}} and p1 also has {:deps {foo/bar {:mvn/version "1"}} then "1" will win, regardless of what p2 says
only difference is that deps are usually not transferred between "projects"
the reason is simply that these are two different package managers with different ideas about the world
do not use any of what shadow-cljs provides and just make your own sync-deps.sh shell script or whatever
yes i understand clojure does place the responsibility on the consumer and not override its deps the difference is the injection into package.json i still wonder if shadow could somehow elevate how node (npm) address it (installing in nested node_modules), but i'm not fully understand it i will think of how to solve it for our use-case, but i see it as a general problem that others may encounter, so probably a generic tooling can be useful
shadow-cljs generally doesn't even know what npm is. you might be using bun or whatever. all it considers is the files it finds in node_modules. all the rest is an attempt to solve the general problems people may have. a mono repo with separate cljs/js libs is no common or general since everyone does that differently
but I absolutely encourage writing tools that solve your needs exactly. that'll always be better than something else that "almost" solves it 😉
you can set :npm-deps {:install false} in shadow-cljs.edn to prevent shadow-cljs from ever even looking at your package.json, which makes sense if you build your own tooling for managing your node_modules
https://grok.com/share/bGVnYWN5_4aa179f4-c5ea-46b0-a533-364bc570648e
something like that maybe. didn't try it. just wrote the prompt with the idea in my mind of how I would do something like this.
ok thank you for your answers i will think about it in the upcoming days and update this thread