clojurescript

ClojureScript, a dialect of Clojure that compiles to JavaScript http://clojurescript.org | Currently at 1.10.879
2021-05-05T11:41:50.108900Z

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

2021-05-05T11:48:26.114800Z

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)?

dnolen 2021-05-05T14:10:11.115700Z

@jamescroft that's a bug in your graph

dnolen 2021-05-05T14:10:31.116200Z

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

dnolen 2021-05-05T14:10:46.116600Z

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

2021-05-05T14:13:52.119800Z

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

dnolen 2021-05-05T14:14:46.120500Z

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

dnolen 2021-05-05T14:14:56.121Z

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

2021-05-05T14:20:31.127Z

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

2021-05-05T14:23:47.129600Z

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?

dnolen 2021-05-05T14:27:03.130Z

so you cannot reproduce on your machine?

2021-05-05T14:27:09.130200Z

I can

dnolen 2021-05-05T14:27:51.131300Z

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

dnolen 2021-05-05T14:28:19.131800Z

so you need to explain why you need both

2021-05-05T14:34:22.136300Z

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

dnolen 2021-05-05T14:35:00.136700Z

you're missing what I'm saying

dnolen 2021-05-05T14:35:07.137300Z

go back to the beginning

dnolen 2021-05-05T14:35:13.137600Z

who cares what happening in ClojureScript yet

dnolen 2021-05-05T14:35:22.137900Z

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

2021-05-05T14:36:06.138600Z

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

dnolen 2021-05-05T14:36:55.139600Z

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

dnolen 2021-05-05T14:37:13.140Z

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

dnolen 2021-05-05T14:37:27.140500Z

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

2021-05-05T14:40:34.142700Z

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 "<https://github.com/roman01la/uix.git>"
                     :sha "0da33eef38a7122be226b9b9a8ae0b5431b6b5d3"}
  uix.dom/uix.dom {:deps/root "dom"
                   :exclusions [cljsjs/react-dom]
                   :git/url "<https://github.com/roman01la/uix.git>"
                   :sha "0da33eef38a7122be226b9b9a8ae0b5431b6b5d3"}}
devcards/devcards {:mvn/version "0.2.7"
                       :exclusions [cljsjs/react cljsjs/react-dom]}

2021-05-05T14:43:25.145Z

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

dnolen 2021-05-05T14:44:18.145700Z

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

dnolen 2021-05-05T14:44:41.146Z

clj -Stree or something like that

2021-05-05T14:49:58.147900Z

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

2021-05-05T14:55:17.149600Z

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.

2021-05-05T14:57:04.151400Z

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

dnolen 2021-05-05T15:02:47.152100Z

@jamescroft I mean the error is super annoying for sure

dnolen 2021-05-05T15:03:03.152500Z

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

dnolen 2021-05-05T15:03:14.152900Z

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

dnolen 2021-05-05T15:03:16.153100Z

it cannot work

2021-05-05T15:06:15.154Z

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

dnolen 2021-05-05T15:34:45.154700Z

np

JohnJ 2021-05-05T18:48:39.155900Z

Can reagent be used to do SSR and then hydrate?

p-himik 2021-05-05T21:19:43.156Z

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

Prabu Rajan 2021-05-05T22:32:51.156200Z

I haven’t tried this myself, but if you can create a web component in cljs like @thheller shows here - https://clojureverse.org/t/how-to-create-custom-elements-web-components-with-clojurescript/4332/2, you could embed that in your html and pass parameters into it like any other web component and read it in cljs using js interop APIs