Search code examples
clojuredependenciesclojurescriptleiningenshadow-cljs

On a Clojure/Clojurescript project, does it make sense to have dependencies declared on a "shadow-cljs.edn" and a "deps.edn" file?


I have been using Clojure, ClojureScript, lein, shadow-cljs, re-frame, reagent, Emacs, and CIDER to work on a Clojure/ClojureScript dynamic web app project.

In one of the private repositories, there is a deps.edn file with the following content:

{:deps
 {org.clojure/clojure {:mvn/version "1.10.3"},
  reagent {:mvn/version "0.10.0"},
  org.clojure/tools.logging {:mvn/version "1.1.0"},
  org.clojure/clojurescript {:mvn/version "1.10.866"}, 
  ring {:mvn/version "1.9.0"}, 
  garden {:mvn/version "1.3.10"}, 
  metosin/malli {:mvn/version "0.5.1"}, 
  hiccup {:mvn/version "1.0.5"}, 
  metasoarous/oz {:mvn/version "1.6.0-alpha35"}, 
  re-frame {:mvn/version "0.12.0"}}

At the same time, there is another file shadow-cljs.edn with the following definition:

 :dependencies
 [[reagent "1.1.0"]
  [re-frame "1.2.0"]
  [day8.re-frame/tracing "0.6.2"]
  [garden "1.3.10"]
  [metosin/malli "0.8.3"]
  [binaryage/devtools "1.0.3"]
  [day8.re-frame/re-frame-10x "1.1.11"]]

As you see, some things such as reagent appear on both files and they use different versions!

This feels weird to me. But the project seems to work fine.

Why does this happen? Where is each version of, say, reagentused? Is there a better way to declare the dependencies?

;; UPDATE

User @EugenePakhomov gave a nice answer to this post. The code is an attempt to implement his suggestion.

Hence, I did:

{:deps
 {reagent {:mvn/version "0.10.0"},
  re-frame {:mvn/version "0.12.0"},
  garden {:mvn/version "1.3.10"},
  metosin/malli {:mvn/version "0.5.1"},
  org.clojure/tools.logging {:mvn/version "1.1.0"},
  org.clojure/clojurescript {:mvn/version "1.10.866"}, 
  ring {:mvn/version "1.9.0"}, 
  hiccup {:mvn/version "1.0.5"}, 
  metasoarous/oz {:mvn/version "1.6.0-alpha35"}, 
  org.clojure/clojure {:mvn/version "1.10.3"},

  {:alias {:cljs-only-dependencies
             reagent {:mvn/version "1.1.0"},
             re-frame {:mvn/version "1.2.0"},
             day8.re-frame/tracing {:mvn/version "0.6.2"},
             garden {:mvn/version "1.3.10"},
             metosin/malli {:mvn/version "0.8.3"},
             binaryage/devtools {:mvn/version "1.0.3"},
             day8.re-frame/re-frame-10x} {:mvn/version "1.1.11"}}}

 :source-paths ["src" "test"]}

Is this the correct implementation of your suggestion?


Solution

  • Shadow-cljs uses those dependencies to build your CLJS code. And the deps.edn dependencies are used to run your CLJ code.

    Those two things aren't necessarily mutually exclusive. E.g. you can have some CLJS code using some macros that are written in CLJ and that rely on some dependencies - in that case, such dependencies will have to be specified in both files.

    An alternative to that layout that avoids duplication is to specify :deps true in shadow-cljs.edn and move all the dependencies from there into deps.edn. Even better, move all CLJS-only dependencies under some specific alias and use :deps {:aliases [:that-alias]} - this way, your CLJ code won't even know about CLJS-only dependencies so they won't pollute the classpath.