Fork me on GitHub
#clojurescript
<
2021-05-05
>
jamescroft11:05:50

Hi, I’ve been chasing down a bug that is leading StackOverflows when I try and compile my CLJS file.

jamescroft11:05:26

I’m seeing that deps/dependency-order is introducing an extra :requires ["react"] and this is causing the StackOverflow:

(require '[cljs.js-deps :as deps])
  => nil
  (deps/dependency-order
    '({:file "libs.js", :provides ["react" "cljsjs.react"], :requires [] :foreign true}
      {:file "libs.js", :provides ["react" "react-dom" "webpack.bundle"] :foreign true}
      {:file "libs.js" :provides ["react-dom" "cljsjs.react.dom"], :requires ["react"] :foreign true}))
  =>
  ({:file "libs.js", :provides ["react" "cljsjs.react"], :requires [], :foreign true}
   {:file "libs.js", :provides ["react" "react-dom" "webpack.bundle"], :foreign true, :requires ["react"]}
   {:file "libs.js", :provides ["react-dom" "cljsjs.react.dom"], :requires ["react"], :foreign true})
Note the extra :requires ["react"] on the second line of the output, it was not present in the input. If the order of the inputs is switched, then the extra require does not get added:
(deps/dependency-order
    '({:file "libs.js", :provides ["react" "react-dom" "webpack.bundle"] :foreign true}
      {:file "libs.js", :provides ["react" "cljsjs.react"], :requires [] :foreign true}
      {:file "libs.js" :provides ["react-dom" "cljsjs.react.dom"], :requires ["react"] :foreign true}))
  =>
  ({:file "libs.js", :provides ["react" "react-dom" "webpack.bundle"], :foreign true, :requires []}
   {:file "libs.js", :provides ["react" "cljsjs.react"], :requires [], :foreign true}
   {:file "libs.js", :provides ["react-dom" "cljsjs.react.dom"], :requires ["react"], :foreign true})
I think this is the cause of the issue that is leading to StackOverflows on some of our developer machines whilst it is working fine on others. Maybe the order of the input to that function is not consistent across developer machines (although all software versions are identical). Can anyone confirm if this is a bug in cljs.js-deps/dependency-order? I assume that the output should be identical to the input (except for order)?

dnolen14:05:11

@jamescroft that's a bug in your graph

dnolen14:05:31

you should only have one foreign entry for libs.js not 3

dnolen14:05:46

you have a cycle in your input so you can't expect this to work

jamescroft14:05:52

@dnolen Thanks for looking. I’m using webpack to bundle a load of JS dependencies that are used by the app. The config that I’m using is:

:foreign-libs [{:file "libs.js"
                 :global-exports {react React
                                  react-dom ReactDOM}
                 :provides ["react" "react-dom" "webpack.bundle"]}]
The webpack bundle includes “react”, “react-dom” and I’m also giving it the synthetic namespace “webpack.bundle”

dnolen14:05:46

this one looks ok, so can you explain what you're doing above

dnolen14:05:56

I cannot make sense of your report because it doesn't look the same

jamescroft14:05:31

Sure, the config that I have in the app is the :foreign-libs config (second snippet). This had been working fine, but recently started to StackOverflow when compiling on some developer machines. I have been trying to track it down and I have found that an extra :requires ["react"] is being added by the call to deps/dependency-order. The inputs to deps/dependency-order that I posted are the ones I grabbed from a debugging session. I don’t know why at that point it has 3 inputs for libs.js , I assume they have been expanded to that by the clojurescript codebase. My only config is what I posted in the second snippet

jamescroft14:05:47

In my dev.cljs.edn config I don’t mention cljsjs.react and cljsjs.react.dom but they are there as part of the :provides for the extra 2 “libs.js” inputs to deps/dependency-order. I think it is the clojurescript compiler that is turning my single foreign lib into 3?

dnolen14:05:03

so you cannot reproduce on your machine?

dnolen14:05:51

but I don't understand the foreign lib at all - providing cljsjs.react.dom and react-dom can't be right

dnolen14:05:19

so you need to explain why you need both

jamescroft14:05:22

I just captured the input that was passed to deps/dependency-order . At that point there are 3 foreign libs with the :file "libs.js" . I didn’t construct these I just captured the call. I only have 1 foreign lib defined in my config file, so I don’t know why there are 3 by the time that the dependency sort happens

dnolen14:05:00

you're missing what I'm saying

dnolen14:05:07

go back to the beginning

dnolen14:05:13

who cares what happening in ClojureScript yet

dnolen14:05:22

why do you have react-dom and cljsjs.react.dom ?

jamescroft14:05:06

Sorry, this is difficult over slack. Where are you seeing cljsjs.react.dom ? This is my config:

:foreign-libs [{:file "libs.js"
                 :global-exports {react React
                                  react-dom ReactDOM}
                 :provides ["react" "react-dom" "webpack.bundle"]}]

dnolen14:05:55

ok, sorry - so we don't talk past each other - this looks good

dnolen14:05:13

but when you debugged somehow you are getting cljsjs.react.dom but you haven't explained how this is possible

dnolen14:05:27

ClojureScript is not going to inject that w/o it be provided by something else

jamescroft14:05:34

A search for cljsjs.react.dom over the codebase doesn’t return anything, so it must be coming from a dependency. Off the top of my head, the dependencies that use react are: UIX and devcards. These are defined in our deps.edn with exclusions for React (because we need to include React in the webpack bundle for other reasons):

uix.core/uix.core {:deps/root "core"
                     :exclusions [cljsjs/react]
                     :git/url ""
                     :sha "0da33eef38a7122be226b9b9a8ae0b5431b6b5d3"}
  uix.dom/uix.dom {:deps/root "dom"
                   :exclusions [cljsjs/react-dom]
                   :git/url ""
                   :sha "0da33eef38a7122be226b9b9a8ae0b5431b6b5d3"}}
devcards/devcards {:mvn/version "0.2.7"
                       :exclusions [cljsjs/react cljsjs/react-dom]}

jamescroft14:05:25

So, the aim is to have a single JS file produced by webpack that contains React, ReactDom and a bunch of other libraries. Hence we exclude React from any clojure dependencies that we add in, and use the :provides and :global-exportsoptions of the foreign-lib to tell them where React is coming from

dnolen14:05:18

right you have to exclude stuff - dump the dependency tree and find out where it's coming from

dnolen14:05:41

clj -Stree or something like that

jamescroft14:05:58

Ok, so it looks like the graphql client re-graph is bringing in cljsjs/react and cljsjs/react-dom too. I haven’t got exclusions defined for that dependency in deps.edn. I’ll try adding an exclusion there and see if it changes anything

jamescroft14:05:17

Awesome! That looks to have fixed it. Changing the dependency to

re-graph/re-graph {:mvn/version "0.1.15"
                     :exclusions [cljsjs/react cljsjs/react-dom]}
avoids the StackOverflow.

jamescroft14:05:04

So, not specifying those :exclusions somehow messed up the graph, leading to a StackOverflow? Thanks so much for your help looking into that

dnolen15:05:47

@jamescroft I mean the error is super annoying for sure

dnolen15:05:03

the stackoverflow and the dependency graph thing makes understanding the issue hard

dnolen15:05:14

but in the end you can't have these things together in your dependency graph anyway

dnolen15:05:16

it cannot work

jamescroft15:05:15

@dnolen Yeah, understood. I had no idea we had an extra React dep being included until you pointed it out. Thanks!

JohnJ18:05:39

Can reagent be used to do SSR and then hydrate?

p-himik21:05:43

Same way you'd do that in React, I imagine - probably with dangerouslySetInnerHTML.