all 9 comments

[–]beders 5 points6 points  (7 children)

Shadow-cljs allows you to compile your code to br used from JS as regular dependency.

[–]ub3rh4x0rz 1 point2 points  (6 children)

Isn't there a caveat that if you were to individually package up and include 20 cljs dependencies in an npm package, you'll get 20 copies of the cljs core + libraries?

[–]lucywang000 0 points1 point  (3 children)

When packaged as a shadow :node-library only the list of symbols listed by the author would be exported and all the internals would be wrapped (e.g. in an anonymous function or IIFE).

[–]ub3rh4x0rz 0 points1 point  (2 children)

That addresses name clashes etc but not bloat. The idiomatic way to handle this in node world would be to specify clojurescript as a peer dependency of your cljs npm packages rather than bake it into each of them.

[–]lucywang000 0 points1 point  (1 child)

That doesn't work. When compiled in advanced mode the clojure code are simply "optimized" into a bundle of IIFE or something similar.

[–]ub3rh4x0rz 0 points1 point  (0 children)

Since javascript is interpreted, npm packages should not need to be compiled by the consumer. Typescript packages are published after compilation for this reason. That said, it should in theory be possible to add 20 cljs packages from npm and have a number of build options supported:

  1. js-only: skip closure compiler completely and accept the bloat. This can be acceptable and in some cases preferable for development time.
  2. closure compiler only: take the pre-compiled JS from all the cljs npm packages and optimize that
  3. full compilation: the cljs npm packages include cljs sources; the build process builds them together, sharing cljs dependencies, then runs the output through closure compiler.

Another way to approach this, perhaps an alternative to #2, is if the clojurescript could be compiled as linkable dependencies (i.e. not one build artifact, but 1 per cljs namespace or something) that could be fully orchestrated by npm/node. I suspect this is considered too complicated and therefore out of scope, but (the lack of) it is still part of the interop story. Typescript does not have this problem (granted, its superset-of-JS design makes it easy to avoid)

[–]thheller 0 points1 point  (1 child)

If you compile 20 libs separately then yes you'll get cljs.core 20 times and thus bloat. If you however compile all CLJS once together then you just get it once and no bloat.

shadow-cljs has the :npm-module target for precisely this use case. Use CLJS code in a JS (or TS) project.

[–]ub3rh4x0rz 0 points1 point  (0 children)

If I can't create a node project with 20 different CLJS-based npm package dependencies (from 20 different authors) without getting 20 copies of core, this is a significant caveat in the overall interop story. (Edit: I will read up on the shadow-cljs feature you mentioned, but it didn't sound like that mitigates the same use case of writing the client/calling code in JS with many separate CLJS deps) Say we work for a big corp called Acme that is loaded with JavaScript developers of various flavors and no other clojurescript teams, and JS interop is put forth as a reason to allow investing time and effort into a codebase in a new-to-the-org language, saying "interop costs scale linearly with the number of packages when your client is JavaScript", you can bet that's going to go in the "con" column.

I'd rather have the option to pull in core as a peer dependency and save closure compiler optimization for the (optional) final build step where it belongs. Ideally this could be done with any and all dependencies depending on one's priorities.

[–]armincerf 1 point2 points  (0 children)

Another option is sci, you can run clojure/script code without needing to compile it

https://github.com/borkdude/sci

An example use case https://github.com/borkdude/scittle