I want the following structure of my project:
src
└── some_proj
├── backend
│ └── system.clj
├── dev
│ ├── config.edn
│ └── user.clj
└── frontend
└── app.cljs
And I want to separate frontend, backend, dev dependencies. Like so:
{:paths ["resources"]
:deps {org.clojure/clojure {:mvn/version "1.12.0-alpha4"}}
:aliases
{:backend
{:extra-paths ["src/some_proj/backend"]
:extra-deps {,,,}}
:frontend
{:extra-paths ["src/some_proj/frontend"]
:extra-deps {,,,}}
:dev
{:extra-paths ["src/some_proj/dev"]
:extra-deps {,,,}}}}
And now, if I do clj -A:backend, in the repl I should be able to (require '[some-proj.backend.system]). But it doesn't see it in the classpath. I think I miss something in my understanding of extra-paths.Paths are classpath roots
if the classpath root is src/some_proj/backend, if you are looking for some-proj.backend.system it will look at <classpath-root>/some_project/backend/system.clj
The way namespaces are mapped to files is relative to roots
which would be src/some_project/backend/some_proj/backend/system.clj
Can you guys please tell me what am I doing wrong?
You cannot do this with paths: where the path “ends” becomes the root for class lookup, which in your config will never match the namespace. You need multiple roots if you want to do this with paths
So what's the idiomatic way of splitting dependencies with deps.edn?
more deps.edn
and :local/root
if you want to keep them all in the same dir, you can do this, but you may have trouble consuming artifacts made by this (maybe that doesn’t matter if it’s an application)
src-backend
└── some_proj
└── backend
└── system.clj
src-dev
└── some_proj
└── dev
├── config.edn
└── user.clj
src-frontend
└── some_proj
└── frontend
└── app.cljs
{:aliases
{:backend
{:extra-paths ["src-backend"]}
:frontend
{:extra-paths ["src-frontend"]}
:dev
{:extra-paths ["src-dev"]}}but at this point, why separate classpaths at all?
I guess the question is really: what are you trying to achieve (and why)?
So I can deploy front-end completely separately from the backend
Well, that's about what goes into your deployable artifacts -- so it's all about how you build things, not how the source code is structured.
and if you want stuff in separate folders, just use a structure that has backend, frontend , and dev folders and make each of those an item in :extra-paths ["backend"] …
and then make sure the namespaces are in the correct structure underneath backend/ frontend/ or dev/
@seancorfield so like for example, if I want to have a "test build" that can separately build front-end and test it without bundling dependencies required for the server. And vice-versa, I don't want to include the test scripts into the main (prod) front-end build artifact.
note this is exactly @favila’s suggestion above. except he used src-backend
@ag You’re skipping a step: how will you make those builds? deps.edn doesn’t do this automatically
you can pass aliases into it right? basis takes aliases and everything should work fine from there on out?
or will copy source still have some issues
that’s definitely a way to go, and works for application artifacts
it doesn’t work if it’s supposed to be a git-consumed or local-root consumed things because aliases aren’t transitive
yeah. can’t specify aliases for dependencies. i think there’s an http://ask.clojure.org ticket for it though
but the point is many folder and deps.edn structures can support many different artifact variants and vice-versa
> deps.edn doesn’t do this automatically
so like if I use clojure.tools.build.api and all my code (front, back, dev, tests) are all in the same place, wouldn't it make it difficult to build multiple artifacts? i.e., I wouldn't want to include compiled and minified .js files in my server-side uberjar
you are pretty easily in control of what exactly goes in your jar. you build it, clean the directory, copy sources, etc into your jar.
yeah, good point. Maybe why bother then by separating the files at the root level?
Thank you, everyone. You are all very helpful. As always.
After years of being dazzled by people using add-libs (looking at you, @seancorfield), I decided to give it a try.
I first tried using Clojure 1.12.0-alpha2, but got this error:
(deps/add-libs '{babashka/fs {:mvn/version "0.4.19"}})
Namespace clojure.tools.deps loaded but function not found: resolve-added-libs
I then tried using Clojure 1.12.0-alpha4, and got a different error:
(deps/add-libs '{babashka/fs {:mvn/version "0.4.19"}})
Execution error (ExceptionInfo) at clojure.tools.deps.interop/invoke-tool (interop.clj:49).
null
Has anyone else had this error, or have any recommendations on how to make this work? Thank you!you need a newer version of the cli
Whoa. I’m using this inside of Cursive/IntelliJ. That never occurred to me — trying that now!
add-libs shells out to the cli
(While I’m watching homebrew upgrade/update install…. @hiredman that this depends on shelling out to CLI would have NEVER occured to me!)
It requires CLI 1.11.1.1347 (or later)...
https://clojurians.slack.com/archives/C06MAR553/p1681479874896649?thread_ts=1681479653.194879&cid=C06MAR553 -- looks like 1.11.1.1273 or later from that announcement but I think on Windows it needs 1.11.1.1347 for the "arguments passed via stdin" feature.
Here's the invoke-tool function it uses under the hood (mentioned in one of your errors): https://clojure.github.io/clojure/branch-master/clojure.tools.deps.interop-api.html
OMG. It works!! Thank you, all!!!!
(need to step out for a sec, but will respond more fully later today. 🙏 )