I currently have 2 shadow-cljs projects with 2 different ways to manage their dependencies to npm libraries.
One uses the default shadow-cljs.edn configuration :js-options {:js-provider :shadow}
, we will call it project A.
The other, we will call it project B, uses the external configuration with the use of webpack inside the shadow-cljs.edn file :js-options {:js-provider :external :external-index "target/index.js"}
as described in the following article How about webpack now?
Locally I can run these project A and project B independently without errors.
However, I would now like to import project A into project B, and use the method my-function
from project_A.core.
(ns project_A.core)
(defn ^:export my-function [] ...)
I tried to release project A by specifying the :target
field of the shadow-cljs.edn file to the value :npm-module
.
Project A > shadow-cljs.edn :
{
[...]
:builds {:app {:target :npm-module
:output-dir "release/"
:entries [project_A.core]
:js-options {:js-provider :external :external-index "target/index.js"}}}
[...]
}}
Then I install it in project B i did a npm install path/to/project_A
, as for a classic npm package and to use it the same way as the others.
I tried to add the local dependency like this:
Project B > package.json :
{
"scripts": {[...]},
"devDependencies": {[...]},
"dependencies": {
[...]
"project_A": "file:path/to/project_A",
[...]
},
"name": "projet B",
}
And I try to import the package inside the ns require field. However project B does not compile.
Is there a clean way to import a project into the other one while taking into account their different configuration?
Your approach is incorrect. You never include the build output of one CLJS library/project in another. You always include the sources directly instead, and then build the proper final output only where you are going to use it. You cannot include CLJS libraries via npm
package.json
, neither should you want to.
So, for the purpose of this explanation the build config in A is entirely irrelevant. It doesn't need to be built, we only want access to the sources.
In B you have a couple options of using it. Say you have everything in ~/code/project-a
and ~/code/project-b
.
In ~/code/project-b/shadow-cljs.edn
you can specify :source-paths ["src/main" "../project-a/src/main"]
. This will make all sources from A directly accessible when building B. It does however require manually transferring the :dependencies
from A to B.
Another option is using a local install (or maven install), but for this you'll need lein
or deps.edn
to build the .jar
. shadow-cljs
itself does not support publishing "libraries".
Another option is using the clojure tools deps.edn
with :local/root
.
;; ~/code/project-a/deps.edn
{:paths ["src/main"]
:deps {...}
:aliases
{:dev {:extra-deps {thheller/shadow-cljs {:mvn/version "2.22.8"}}}}}
;; ~/code/project-b/deps.edn
{:paths ["src/main"]
:deps {my.company/project-a {:local/root "../project-a"}}
:aliases
{:dev {:extra-deps {thheller/shadow-cljs {:mvn/version "2.22.8"}}}}}
;; ~/code/project-b/shadow-cljs.edn
{:deps {:aliases [:dev]}
:builds ...}
Also make sure you actually need to split projects in the first place. It is perfectly fine and even encouraged to build mutiple outputs out of one project. So, often I see people splitting things because of "best practices". Which often isn't necessary and just makes things complicated for no good reason. IMHO, YMMV. If you really must, deps.edn
is the most flexible option.
As far as npm
dependencies are concerned you project-a
can express them via a deps.cljs
file on the classpath. So, with the above config ~/code/project-a/src/main/deps.cljs
with {:npm-deps {"the-dep" "the-version"}}
. These must be manually added, as the package.json
is not "inherited". When you start shadow-cljs
in B it'll pick those :npm-deps
and transfer them to the package.json
in B. You then build everything as usual in B.