Fork me on GitHub
#shadow-cljs
<
2019-08-12
>
tianshu04:08:38

If ClojureScript is, the shadow would be a good tool.

superstructor07:08:01

Is there a way to declare NPM dependencies in libraries so that those deps will be included in dependant projects that depend on those libraries ? i.e. how to declare transitive NPM deps without having to duplicate it in every dependent project ?

thheller07:08:53

@superstructor you can include a deps.cljs with {:npm-deps {"foo" "version"}} in your .jar

superstructor07:08:32

Ok thanks, but that will just issue a warning not actually provide the deps ? Is there a good way to also provide transitive deps ? @thheller

thheller07:08:45

shadow-cljs will run an npm install on the packages listed in deps.cljs on startup? (if they aren't already in package.json)

thheller08:08:15

unsure what you mean about a warning?

superstructor08:08:08

Is that behaviour any different between using the Node.js shadow-cljs CLI and running via lein with lein run -m shadow.cljs.devtools.cli and :lein true ?

thheller08:08:02

which warning are you refering to? makes things easier if I know what you are talking about 😛

superstructor08:08:04

Ah I see it is the difference between :npm-deps and :foreign-libs ? For example

The required namespace "react" is not available, it was required by "day8/re_frame_10x/inlined_deps/reagent/v0v8v0/reagent/core.cljs".
The namespace was provided via :foreign-libs which is not supported.
Please refer to  for more information.
You may just need to run:
  npm install react

thheller08:08:37

:foreign-libs is a different mechanism. has nothing to do with :npm-deps

thheller08:08:15

you do need a deps.cljs at the root in your distributed .jar file

superstructor08:08:18

Ok I think that is clearer now. So that is for libs, but for apps themselves you still need to have your own mechanism for npm deps like package.json ?

thheller08:08:33

yes that mechanism is for libs (which you asked about 😛)

superstructor08:08:48

Ok awesome, that is clear. Thank you! 😄

superstructor08:08:51

In the user guide there is only one mention of :npm-deps. May I ask where the source is and if you accept pull requests on docs ? I also wanted to add :external-config to the compiler options which is missing.

thheller08:08:09

:external-config is not a thing in the compiler. it is purely community convention?

superstructor08:08:25

Not sure. :compiler-options { :external-config {:devtools/config {:features-to-install [:formatters :hints]}}} does work for cljs devtools.

thheller08:08:55

yes but that is done by cljs-devtools by directly looking at the compiler-options

superstructor08:08:38

My bad, you are correct. After looking it up I see it does (get-in @cljs.env/*compiler* [:options :external-config :devtools/config]). :thumbsup:

thheller08:08:04

yeah as far as CLJS is concerned there is only compiler-options

thheller08:08:12

but it is a map which you can add anything to in theory

thheller08:08:30

(shadow-cljs actually respects the key with regards to caching but thats it)

thheller08:08:05

it is only under the :external-config key because of figwheel which had "strict" validation of the compiler-options map

thheller08:08:11

and would complain about unknown keys

thheller08:08:25

so they added the "convention" for external-config and tooling-config

thheller08:08:31

but its purely on the library side

superstructor08:08:47

I see. Thanks I wasn't aware of that history.

superstructor08:08:32

One more question re earlier :npm-deps / :foreign-libs. For something like re-frame that needs to maintain compatibility with multiple build systems would it be recommended to simply declare both ?

thheller08:08:03

IIRC there are open issues about that which I commented on?

thheller08:08:08

basically just include a deps.cljs

thheller08:08:29

hmm can't find it. maybe not.

thheller08:08:52

just using cljsjs/react etc works but is helped by including a deps.cljs with :npm-deps

thheller08:08:12

basically as far as shadow-cljs is concerned just include a deps.cljs

thheller08:08:27

I thought that was actually added a while ago but I might be wrong

superstructor08:08:39

I can't see one in reagent or re-frame. react itself is probably a reagent PR to be more accurate. That explains how things should be going forward so it is clear, thank you! 🙂

superstructor08:08:35

Are you familiar with the lein-shadow plugin @thheller?

thheller08:08:18

if that is the luminus thing yes

thheller08:08:04

yeah that one

superstructor08:08:11

In my opinion, the implementation has too much coupling of different tasks/concepts, but regardless my follow up question is: Currently it gets :npm-deps out of project.clj. Might it make more sense from a design perspective for such a plugin to get that out of deps.cljs directly ?

superstructor08:08:47

Because then 1. apps and libs using lein could both declare npm-deps in the same way; and 2. when working directly in a libs repo, there would otherwise be duplication of deps between project.clj and deps.cljs.

thheller08:08:36

wasn't aware it was doing anything with :npm-deps at all

thheller08:08:01

apps should just be using package.json IMHO

thheller08:08:10

libs should use deps.cljs

thheller08:08:44

the problem is that npm is a total mess

superstructor08:08:52

Yep it generates package.json from :npm-deps key in project.clj. A bit of a hack, but it works.

thheller08:08:10

so you NEED to hold onto your package.json AND the lockfiles (eg. package-lock.json) if you want anything remotely reliable

thheller08:08:38

so anything that bypasses using npm directly is going to mess things up

superstructor08:08:40

Re your comment libs should use deps.cljs - If you are working directly in a libs project (in this case, it behaves like an 'app') you would need to duplicate deps between both package.json and deps.cljs ?

thheller08:08:09

this is NOT maven where you can somewhat rely on dependencies not chaning unless you change them

superstructor08:08:39

Yep, correct. But if you choose to serialise package.json as deps.cljs; i.e. it is repeatable. Then it is OK ?

thheller08:08:40

yes for libraries I maintain a package.json for development

thheller08:08:51

and then just manually copy over deps.cljs when needed

thheller08:08:28

well "repeatable" is questionable

superstructor08:08:34

Ok, so I guess that answers my question. That if you want to do that manual step automatically (ala lein-sahdow) :npm-deps should be taken from a deps.cljs not from project.clj.

thheller08:08:54

It sort of works and I don't have a better solution is what I'd say

thheller08:08:20

problem with taking it from deps.cljs is that you need to run a JVM with the full classpath to get them

thheller08:08:27

which is much slower than just taking a :npm-deps map

thheller08:08:28

I think that apps should maintain a package.json. period. don't outsource it to anything else.

thheller08:08:38

it is annoying enough as it is. don't make it worse.

thheller08:08:04

libraries with deps.cljs is problematic but works ok-ish until you run into version conflicts

thheller08:08:08

then it becomes bad again

thheller08:08:29

I honestly have no solution to these things so I'm all ears for better solutions

superstructor08:08:10

Thanks. That's a good summary of the issues.

superstructor08:08:41

I think the distinction between apps and libs becomes less interesting once your a lib developer, because then you run the lib as an 'app' (i.e. not a transitive dep) and you end up maintaining a package.json there too.

superstructor08:08:11

So you end up duplicating deps in 2 files at every layer of your dependency tree, which does suck.

superstructor08:08:42

Not only duplicating between files, but duplicating between dependent projects.

thheller08:08:00

as a lib developer you'll need to be much more careful with you declared dependencies anyways

thheller08:08:09

so I think it is ok to manually maintain a deps.cljs

thheller08:08:47

again ... this isn't maven. options for managing dependencies are much more limited

thheller08:08:21

for apps ... you will eventually need to maintain your own package.json

thheller08:08:28

there is simply no way around that

superstructor08:08:29

Would it be worth considering shadow-cljs being responsible for managing/generation of package.json based on deps.cljs as a standard or is that a space you don't want to go ?

thheller08:08:00

I'm pretty close to completely replacing npm and just downloading and managing dependencies manually

thheller08:08:39

but no. the deps.cljs handling shadow-cljs currently does is all I'm willing to do

thheller08:08:33

that is: read all found deps.cljs with :npm-deps on startup, do a best effort conflict resolution, run npm install the-package@version if the package is not in package.json already

thheller08:08:50

everything else SHOULD be in package.json

thheller08:08:03

otherwise we need to replicate every single tooling related thing npm does

thheller08:08:12

which is just silly

superstructor08:08:56

Ok thanks, it is clear what the current extent of responsibility is :thumbsup: Yeah, if you choose to replace npm completely that would obviously avoid the mess and solve the issue, but npm does a lot of stuff. Its questionable how much of that CLJS users would actually need though for shadow-cljs to be viable.

superstructor08:08:49

As-in, if shadow-cljs did ever download/manage deps directly itself, I'm not sure it would actually need to cover 100% of npm's feature set related to that.

superstructor08:08:23

But yeah, I get its also a big scope increase, best avoided if possible.

thheller08:08:42

the reason I haven't done that already is because it would only be practical for browser builds

thheller08:08:22

as that is the only thing that doesn't actually need the node_modules folder structure

thheller08:08:56

but most things do actually need that stuff (eg. node, react-native) so if we were to manage dependencies we'd have another system to worry about deps

thheller08:08:33

so yeah I'm probably never touching that although I have thought about it a lot

superstructor08:08:54

Yeah that would be a formidable maintenance burden.

thheller08:08:40

my recommendation if you want to avoid the npm horrors as best you can: use package.json, managed via npm install etc. and keep the lock files in version control

thheller08:08:33

version ranges in npm are a nightmare and everything uses them

thheller09:08:03

so there is no guarantee that 2 people running npm install the-thing will end up with the same dependencies

superstructor09:08:24

Yep, that's burned me before.

thheller09:08:33

things have gotten a bit better since lock files

thheller09:08:37

but shit still happens

superstructor09:08:27

Yep, software is hard.

superstructor09:08:08

Thanks again for all the insights. Much appreciated. If I think up any possibly reasonable solutions to this I'll definitely ping you.

thheller09:08:51

yeah combining to two different package managers is not that easy

superstructor09:08:12

Absolutely, its a tough position to be in. Overall, I want to say despite that shadow-cljs is amazing work and a huge leap forward in CLJS development. Its a good problem to have! It wasn't long ago that every time I needed a new JS dep it required a PR to CLJSJS!

thheller09:08:31

yeah. although sometimes I wonder if we should maybe avoid using most JS deps 😉

thheller09:08:58

I'm definitely at a stage where I try to avoid them as much as possible

💯 4
thheller09:08:22

too many times were I try to upgrade one tiny dep with a minor version bump and everything breaks 😛

thheller09:08:52

and things seem to be getting worse not better in that regard

thheller09:08:01

hope that webpack5 will bring back some order to the chaos 😛

superstructor09:08:02

Yeah, its a blessing and a curse. Unfortunately its non-trivial to replace all the JS things with CLJS things.

superstructor09:08:49

The set of functionality available in JS is always going to be greater than CLJS, so interop is a key part of CLJS's strength just like Clojure and the JVM w/ Java libs.

thheller09:08:26

yeah absolutely. it is the reason I'm in this in the first place

thheller09:08:01

but there are still a bunch of widely used npm packages that aren't very high quality

thheller09:08:21

so in general the "quality" of JVM libs is much better (whatever that means)

thheller09:08:20

JS world is also still rapidly changing so I guess its something we have to live with for a while

thheller09:08:05

to be fair to JS though they need to solve things that JVM mostly doesn't care about

thheller09:08:08

eg. code size

superstructor09:08:35

Yep. Its nice to be insulated mostly from that churn by abstractions in the CLJS world, but the interaction at the interop/deps layer is going to be a pain point for awhile.

superstructor09:08:00

It'll be interesting to see how things play out. Looks like JS is becoming very much a compile target, esp with asm.js, TypeScript etc.

thheller09:08:30

yeah, its probably be going to completely different (again) in a couple of years

thheller09:08:58

npm isn't even 10 years old after all 😛

😄 12
David Pham18:08:43

So a selling feature of CLJS would also be reliability of dependencies?

superstructor22:08:38

I started a PR on reagent to add deps.cljs as per above - https://github.com/reagent-project/reagent/pull/441 @thheller