Fork me on GitHub

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


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
    '({: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:
    '({: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)?


@jamescroft that's a bug in your graph


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


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


@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”


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


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


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


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?


so you cannot reproduce on your machine?


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


so you need to explain why you need both


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


you're missing what I'm saying


go back to the beginning


who cares what happening in ClojureScript yet


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


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"]}]


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


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


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


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]}


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


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


clj -Stree or something like that


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


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.


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


@jamescroft I mean the error is super annoying for sure


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


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


it cannot work


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


Can reagent be used to do SSR and then hydrate?


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