Fork me on GitHub
#shadow-cljs
<
2020-08-22
>
smnplk06:08:47

Hi, I am using shadow-cljs via leiningen. Everything works perfectly, except when I do lein uberjar I get the following error

smnplk06:08:06

Can't find 'shadow.cljs.devtools.cli' as .class or .clj for lein run: please check the spelling. Syntax error (FileNotFoundException) compiling at (/tmp/form-init11854498876761345265.clj:1:74). Could not locate shadow/cljs/devtools/cli__init.class, shadow/cljs/devtools/cli.clj or shadow/cljs/devtools/cli.cljc on classpath.

smnplk06:08:58

:cljs {:source-paths ["src/cljs"]
                    :dependencies [
                                   [org.clojure/clojurescript "1.10.773" :scope "provided"]
                                   [com.google.javascript/closure-compiler-unshaded "v20200719"]
                                   [org.clojure/google-closure-library "0.0-20191016-6ae1f72f"]
                                   [thheller/shadow-cljs "2.10.22"]
                                   [reagent "0.8.1"]
                                   [re-frame "0.10.7"]
                                   [kibu/pushy "0.3.8"]
                                   [fork "2.0.0"]
                                   [vlad "3.3.2"]
                                   [day8.re-frame/http-fx "v0.2.0"]]}
             :uberjar
             {:resource-paths ["resources" "resources/sql"]
              :source-paths ^:replace ["src/clj" "src/cljc"]
              :prep-tasks ["compile"
                           ["run" "-m" "shadow.cljs.devtools.cli" "release" "app"]]
              :hooks []
              :omit-source true
              :aot :all}})
here are the profiles entries in my project.clj. If I run lein run -m shadow.cljs.devtools.cli release app manually in a console, everything works fine.

smnplk06:08:29

i think i know what's wrong, i need to activate the cljs profile, using with-profile...tried this :prep-tasks ["compile" ["with-profile" "+cljs" "run" "-m" "shadow.cljs.devtools.cli" "release" "app"]]`

smnplk06:08:52

i get some weird StackOwerfow exception..:(

smnplk06:08:19

Solved: I just had to :prep-tasks ["compile" ["with-profile" "cljs" ["run" "-m" "shadow.cljs.devtools.cli" "release" "app"]]]

bbss14:08:32

The app I've been working on for a while is getting a bit big (19MB...) I decided to try out the release so closure compiler can do its thing. Brought it down to 11MB, still not great. Running gzip on that brings it down to 1.6MB, which I find acceptable for the interactive app that it is.

bbss14:08:52

Was expecting to run into a lot of trouble with advanced compilation but after a few minor issues it actually seems to go well. The only thing I'm not sure about is some es6 files that I ran through babel, closure doesn't like em:

Closure compilation failed with 4 errors
--- kr/models/ronde-tempel-compiled.js:6
Cannot import Closure files by path. Use either import goog:namespace or goog.require(namespace)
..

bbss14:08:58

and without --debug for release that goes down even further to 1.2mB! nice ๐Ÿ™‚

thheller14:08:29

@bbss yeah there seems to be an issue with handling .js files. still haven't figured out what the problem is though.

bbss15:08:14

alright, well if there's anything I can do to test let me know. Other .js files seem to load okay, but the ones I ran manually through babel don't, they work in watch build though.

bbss15:08:37

These files that don't load are https://github.com/react-spring/gltfjsx output of this. After which I run it through babel. They turn a webGL file into a react class.

Sam Ritchie15:08:55

@thheller Q for you about packaging a cljs dependency for shadow-cljs compatibility if you have a second - I previously had packaged "fraction.js" in cljsjs, using your advice, and that is working great

Sam Ritchie15:08:16

but the npm package has a bigfraction.js living inside of it too, that I'd love to package up as a different dependency

Sam Ritchie15:08:14

When I built the fraction.js package you had noted I needed a :global-export entry that matched the NPM package, which I did and again that works great: https://github.com/cljsjs/packages/blob/master/fraction/build.boot#L35

Sam Ritchie15:08:23

but what to do for an NPM dependency that packages up TWO files, with two identical namespaces? Do I make them... both export "fraction.js", and then this is on the user to only include one of cljsjs/fraction` vs cljsjs/bigfraction?

Sam Ritchie15:08:49

that would work, but I wanted to check before I contribute to someone else's dependency issues down the road. Thanks anyone who has insight here!

thheller16:08:05

@sritchie09 your question is confusing. I'm assuming there is a fraction.js npm package and a bigfraction.js? thats all shadow-cljs needs to know. it doesn't even look at :foreign-libs for anything

thheller16:08:27

so you are probably asking what to do for builds NOT using shadow-cljs?

Sam Ritchie16:08:00

Thereโ€™s not a bigfraction.js

Sam Ritchie16:08:31

@thheller I think he just packages that file inside fraction.js

Sam Ritchie16:08:26

and then tests it like this

Sam Ritchie16:08:55

maybe it's a work in progress, and the answer is to publish it separately using cljsjs and alias it as bigfraction?

thheller16:08:49

ah so in shadow-cljs that would be (:require ["fraction.js/bigfraction.js" :as bf])

thheller16:08:28

no clue how you want to bundle that for cljsjs if its the same package?

Sam Ritchie16:08:01

oh, nice, amazing

Sam Ritchie16:08:52

@thheller I suspect I can do this trick:

Sam Ritchie16:08:12

but bundle bigfraction.js instead, and then do this in build.boot:

(deps-cljs :provides ["fraction.js/bigfraction.js", "cljsjs.bigfraction"]
              :requires []
              :global-exports '{fraction.js/bigfraction.js Fraction
                                cljsjs.bigfraction Fraction})

Sam Ritchie16:08:52

if that works, with the funky provides, then that should be shadow-cljs compatible, yeah?

Sam Ritchie16:08:01

I would go all shadow-cljs, but I haven't converted our build yet

Sam Ritchie16:08:16

and it would be nice if non-shadow folks could use my library as a dependency

thheller16:08:48

this doesn't sound like a good idea to me

thheller16:08:28

but I guess its fine if its just a single file with no other dependencies

Sam Ritchie16:08:32

yes, that's right

Sam Ritchie16:08:01

astonishingly it works

thheller16:08:55

but what happens if a user uses both in one project?

thheller16:08:01

or does that not happen?

thheller16:08:39

I mean if it does you definitely need to use a different global name

Sam Ritchie16:08:11

For sure - I think my aliases cover this

Sam Ritchie16:08:36

oh I see you mean in the bundle phase

thheller16:08:36

if both use the Fraction global it will conflict on that

Sam Ritchie16:08:16

Yes good call, Iโ€™ll rename the bigfraction to BigFraction when I run the bundler

jaime17:08:33

Hi, I'm using d3.js (third party js lib from npm) I'm using its line function but I'm getting error curve is not a function in line 23 https://github.com/d3/d3-shape/blob/master/src/line.js#L23 However, I can see that it is defined in line 10 https://github.com/d3/d3-shape/blob/master/src/line.js#L23 (just few lines above its usage) Is this related to shadow-cljs? Here is snippet of how I use the line function

line (-> d3
         (.line)
         (.curve (.curveNatural d3))
         (.defined #(not (js/isNaN (.-value %))))
         (.x #(x (.-date %)))
         (.y #(y (.-value %))))

_ (-> svg
       (.append "path")
       (.datum data)
       (.attr "fill" "none")
       (.attr "stroke" "green")
       (.attr "stroke-width" 1.5)
       (.attr "d" line))
EDIT: Looking at the file generated by shadow for d3.js. The error is in line 349 in the attached snippet

thheller17:08:04

@jaime.sangcap you are looking at something completely irrelevant ๐Ÿ˜›

thheller17:08:39

curve = curveLinear, means its just a rename for some reason and the actual function is coming from import curveLinear from "./curve/linear.js";

thheller17:08:02

so I'm guessing you just need to adjust your ns :require?

thheller18:08:12

or I don't follow your problem ... d3 went through a whole lot of changes in the various versions

thheller18:08:28

so without seeing an actual full example I can only assume that you are using it wrong

jaime18:08:43

Fair point ๐Ÿ™‚ I might be doing it wrong. I will try to create a repo to reproduce the issue

jaime19:08:31

@thheller The error changed a bit when I try to reproduce it in this repo https://github.com/jaimesangcap/repro-cljs-d3

app.js:2226 failed to load shadow.module.app.append.js TypeError: curve is not a function
    at SVGPathElement.line (d3-shape.js:349)
    at SVGPathElement.eval (d3-selection.js:399)
    at Selection.each (d3-selection.js:366)
    at Selection.attr (d3-selection.js:423)
    at Object.repro_cljs_d3$core$init [as init] (core.cljs:77)
    at eval (shadow.module.app.append.js:4)
    at eval (<anonymous>)
    at Object.goog.globalEval (app.js:836)
    at Object.env.evalLoad (app.js:2224)
    at app.js:2404
Probably I'm missing some configuration?

thheller19:08:31

and you are porting this from which JS example/docs?

thheller19:08:45

I mean what makes you think that there should be a curve fn?

thheller19:08:46

I mean d3 got split up into a lot of smaller packages quite a while ago. are you sure that fn isn't supposed to be in one of those instead of the generic d3?

thheller19:08:09

curveBasis: (...)
curveBasisClosed: (...)
curveBasisOpen: (...)
curveBundle: (...)
curveCardinal: (...)
curveCardinalClosed: (...)
curveCardinalOpen: (...)
curveCatmullRom: (...)
curveCatmullRomClosed: (...)
curveCatmullRomOpen: (...)
curveLinear: (...)
curveLinearClosed: (...)
curveMonotoneX: (...)
curveMonotoneY: (...)
curveNatural: (...)
curveStep: (...)
curveStepAfter: (...)
curveStepBefore: (...)

thheller19:08:29

there are a bunch of curve fns? maybe it just was renamed?

jaime19:08:44

I saw the curve example in the readme https://github.com/d3/d3-shape#curves When using the generic d3 lib and use the line, I can draw a line chart, but the connection between the line/path are sharp. So I tried applying the curve function, but I'm getting the error above. Using the d3-shape gives the same error. I probably need to spend some time reading the docs and code

thheller19:08:58

ok but that is the d3-shape package not d3?

thheller19:08:38

(:require ["d3-shape" :as d3s]) (d3s/line) seems to have a .curve?

thheller19:08:49

I really don't have a clue whats going on in this code so I cannot do much

thheller19:08:09

its impossible for me to tell if you are calling this correctly

jaime20:08:05

Requiring the d3-shape as mentioned above throws error as well when calling the (.curve)

line (-> (d3s/line)
               (.curve (.curveNatural d3))
               (.defined #(not (js/isNaN (.-value %))))
               (.x #(x (.-date %)))
               (.y #(y (.-value %))))
app.js:2226 failed to load shadow.module.app.append.js TypeError: curve is not a function
    at SVGPathElement.line (d3-shape.js:349)
    at SVGPathElement.eval (d3-selection.js:399)
    at Selection.each (d3-selection.js:366)
    at Selection.attr (d3-selection.js:423)
    at Object.repro_cljs_d3$core$init [as init] (core.cljs:76)
    at eval (shadow.module.app.append.js:4)
    at eval (<anonymous>)
    at Object.goog.globalEval (app.js:836)
    at Object.env.evalLoad (app.js:2224)
    at app.js:2427
If I understand the d3-shape code in line 10 https://github.com/d3/d3-shape/blob/master/src/line.js#L10, it creates a var curve=curveLinear , which will be called later in the closure line 23, but it seems like by the time the closure get invoked, it throws an error that var curve declared in line 10 is not a function.

jaime20:08:28

Thanks a lot for your help btw, I need some help and crack it tomorrow ๐Ÿ™‚

thheller20:08:51

it isn't failing on the .curve

thheller20:08:02

that works just fine

thheller20:08:55

it is failing on this line (.attr "d" line)

jaime20:08:32

Yes, when it invokes the function line(data) {} which is a closure of d3s/line

jaime20:08:05

Could it be possible that the var curve has been hoisted?

thheller20:08:24

its completely possible that the closure compiler rewrites this in a weird way

thheller20:08:47

but I cannot tell what is happening in this code or if its correct

jaime20:08:57

I made a dumb interop mistake. I was invoking the function instead of property access ๐Ÿ˜… What I'm doing (just dot) (.curve (.curveMonotoneX d3)) Correct way (dot hyphen) (.curve (.-curveMonotoneX d3))

thheller20:08:03

btw you shouldn't be using d3 like that. it is a namespace alias so d3/curveMonotoneX would be correct and also looks better ๐Ÿ˜‰

jaime20:08:58

great tip! learned the hard way. ๐Ÿ˜† Thanks a lot for your time