figwheel-main

figwheel-main http://figwheel.org
msolli 2021-04-12T11:59:16.051400Z

Hi all! I’m converting my project from lein-figwheel to figwheel-main. Hot-reloading hadn’t worked for a while for some reason, and I thought it might be good idea to bring my project into the present. I’ve migrated the config, and the compilation and CLJS REPL works. But there’s something wrong with the watch/recompile mechanics. After I change a file in one of the :watch-dirsdirectories, it is picked up by the watcher and compilation starts, but then nothing happens for a very long time:

[Figwheel] Compiling build all to "target/cljsbuild/public/js"
[Figwheel] Successfully compiled build all to "target/cljsbuild/public/js" in 39.085 seconds.
[Figwheel] Watching paths: ("src/cljs" "src/cljc" "env/figwheel/cljs") to compile build - all
[Figwheel:FINE] Build Inputs: ["src/cljs" "src/cljc" "env/figwheel/cljs"]
[Figwheel:FINEST] Figwheel.core config: {:watch-dirs ("src/cljs" "src/cljc" "env/figwheel/cljs")}
[Figwheel] Starting Server at <http://localhost:9500>
[Figwheel] Starting REPL
[Figwheel:FINE] Reloading clj files:  ("/Users/martin/c/v/foo/src/cljc/foo/views/home_page.cljc")
[Figwheel:FINE] Detected changed cljs files:  ("/Users/martin/c/v/foo/src/cljc/foo/views/home_page.cljc")
[Figwheel] Compiling build all to "target/cljsbuild/public/js"
[Figwheel] Successfully compiled build all to "target/cljsbuild/public/js" in 347.875 seconds.
The last four lines are after the change. Notice that the recompile takes almost 6 minutes, whereas the initial compile was around 40 seconds. I don’t know how to debug this. No activity in the log file (if I configure one with :log-file. :log-level is set to :all. The CPU is mostly idle during these 6 minutes, so I’m pretty certain not much compilation is going on. Any ideas for how to investigate what’s happening here? (I’ll post config in the thread.)

msolli 2021-04-13T09:50:00.053Z

Thanks, @bhauman! I tried with cljs.main, and it delivers fast recompiles:

$ clj -M:figwheel -m cljs.main --compile-opts env/figwheel/resources/cljsmain-all.edn --watch src/cljs --watch src/cljc --compile
[...]
Copying file: [lots of files...]
... done. Elapsed 27.26382128 seconds
Watching paths: /Users/martin/c/v/foo/src/cljs, /Users/martin/c/v/foo/src/cljc
Change detected, recompiling ...
Compiling /Users/martin/c/v/foo/src/cljc/foo/views/home_page.cljc to target/cljsbuild/public/js/foo/views/home_page.js
Copying file:/Users/martin/c/v/foo/src/cljc/foo/views/home_page.cljc to target/cljsbuild/public/js/foo/views/home_page.cljc
... done. Elapsed 4.864356377 seconds
One other thing I just tried is to go back to Leiningen for figwheel-main, which is what the rest of my project uses. Maybe there was an impedance mismatch there between the different profiles and aliases. Leiningen is black magic to me, but as I long as I provide enough bat wings and secret incantations it does in fact work. With the same setup as before, but running under Leiningen, I now got this:
[Figwheel:FINE] Detected changed cljs files:  ("/Users/martin/c/v/foo/src/cljc/foo/views/home_page.cljc")
[Figwheel] Compiling build all to "target/cljsbuild/public/js"
[Figwheel] Successfully compiled build all to "target/cljsbuild/public/js" in 482.354 seconds.
[Figwheel:FINE] Reloading clj files:  ("/Users/martin/c/v/foo/src/cljc/foo/views/home_page.cljc" "/Users/martin/c/v/foo/src/cljc/foo/views/home_page.cljc")
[Figwheel:FINE] Detected changed cljs files:  ("/Users/martin/c/v/foo/src/cljc/foo/views/home_page.cljc" "/Users/martin/c/v/foo/src/cljc/foo/views/home_page.cljc")
[Figwheel] Compiling build all to "target/cljsbuild/public/js"
[Figwheel] Successfully compiled build all to "target/cljsbuild/public/js" in 6.114 seconds.
[Figwheel:FINE] Reloading clj files:  ("/Users/martin/c/v/foo/src/cljc/foo/views/home_page.cljc")
[Figwheel:FINE] Detected changed cljs files:  ("/Users/martin/c/v/foo/src/cljc/foo/views/home_page.cljc")
[Figwheel] Compiling build all to "target/cljsbuild/public/js"
[Figwheel] Successfully compiled build all to "target/cljsbuild/public/js" in 5.835 seconds.
Notice that the first re-compile takes a long time, while the next two takes a few short seconds.

msolli 2021-04-13T09:53:55.053300Z

By the way, hot reloading does work with :modules, for me at least (provided the recompile finishes).

bhauman 2021-04-13T22:10:15.053500Z

@msolli this is interesting, I think the aot-cache is what is happening here

bhauman 2021-04-13T22:11:10.053700Z

figwheel disables it because it can cause big headaches. if you enable the aot-cache I think your compile times will be fixed up

bhauman 2021-04-13T22:12:08.053900Z

there compile times should be the same for all of these setups

msolli 2021-04-14T06:15:15.054300Z

OK, so I’ve enabled AOT cache with :aot-cache true in the compiler options. I also wiped the ~/.cljs/.aot_cache directory just in case. I experience the same phenomenon: The initial compile takes 40 seconds, the first recompile takes 6-8 minutes, the subsequent recompiles takes 6 seconds.

msolli 2021-04-14T07:05:06.054500Z

I’m on Clojurescript 1.10.844, by the way. I’ve tried downgrading to 1.10.764, but the problem persisted.

msolli 2021-04-14T07:36:19.054700Z

I wish I knew how to get more insight in what the computer is doing during those 6-8 minutes. It seems to be mostly waiting, since there is hardly any CPU activity. I’ve rebooted it, and there’s not much else going on.

msolli 2021-04-14T10:06:00.054900Z

Just tried one more thing: I copied the config to a foo.cljs.edn file and ran lein fig -- --build foo --repl (ie. not using figwheel.main.api). It showed the same phenomenon of around 6 minutes recompile time after the first file change.

bhauman 2021-04-20T16:29:58.063100Z

@msolli sorry for not getting back sooner

bhauman 2021-04-20T16:30:34.063300Z

If the compile has started then it should be compiling during that time

bhauman 2021-04-20T16:31:32.063500Z

another thing to look at is the options that are provided to the compiler by figwheel

bhauman 2021-04-20T16:35:24.063700Z

you can get the compile options by providing the -pc cli option (before the other options) and it will print out the compile options being send to the cljs compiler

bhauman 2021-04-20T16:37:33.063900Z

but it that doesn’t provide some insight then I’d say that there is probably some strange thing going on with paths or there is a bug in the dependency traversal algorithm such that its recurring on itself

bhauman 2021-04-20T16:38:20.064100Z

but I’d have to really look at what figwheel is doing to make modules reloadable

msolli 2021-04-22T04:57:05.064300Z

@bhauman Thanks for getting back to me – much obliged! Here are the compiler options:

{:output-dir      "target/cljsbuild/public/js",
 :closure-defines {figwheel.core/load-warninged-code true,
                   figwheel.repl/connect-url         "<ws://localhost:9500/figwheel-connect?fwprocess=4cb3df&amp;fwbuild=all>"},
 :modules         {,,,},
 :optimizations   :none,
 :aot-cache       true,
 :warnings        {:munged-namespace false},
 :preloads        [vilect.dev
                   figwheel.core
                   figwheel.main
                   figwheel.repl.preload
                   devtools.preload
                   figwheel.main.system-exit
                   figwheel.main.css-reload],
 :asset-path      "/js",
 :repl-requires   ([figwheel.repl :refer-macros [conns focus]]
                   [figwheel.main
                    :refer-macros
                    [stop-builds start-builds build-once reset clean status]]
                   [cljs.pprint :refer [pprint] :refer-macros [pp]]
                   [cljs.repl :refer-macros [source doc find-doc apropos dir pst]])}
I’ve omitted the contents of :modules, which is a map with 139 entries of the form:
:vilect.views.organization.processes.new
  {:output-to
   "target/cljsbuild/public/js/vilect.views.organization.processes.new.js",
   :entries #{vilect.views.organization.processes.new}},
:munged-namespace false is set because that prevents warnings when compiling namespaces that contain the word “new” (which is a reserved keyword in Javascript).

msolli 2021-04-12T12:00:51.051500Z

This is how I start Figwheel:

(figwheel/start
    {:id      :all
     :options {:modules       (clojure.edn/read-string (slurp "cljsbuilds.edn"))
               :output-dir    "target/cljsbuild/public/js"
               :asset-path    "/js"
               :optimizations :none
               :warnings      {:munged-namespace false}
               }
     :config  {:watch-dirs          ["src/cljs" "src/cljc" "env/figwheel/cljs"]
               :css-dirs            ["resources/public/css" "target/sass/public/css"]
               :helpful-classpaths  false
               :open-url            false
               :log-level           :all
               :load-warninged-code true
               :client-log-level    :finest
               }})

msolli 2021-04-12T12:02:20.051700Z

cljsbuilds.ednis an EDN file with many entries like this:

:foo.views.home-page
 {:output-to "target/cljsbuild/public/js/foo.views.home-page.js",
  :entries #{foo.views.home-page}}

msolli 2021-04-12T12:18:46.051900Z

Relevant entry in deps.edn:

:figwheel
  {:extra-deps  {com.bhauman/figwheel-main {:mvn/version "0.2.13"}
                 cider/piggieback          {:mvn/version "0.5.2"}}
   :extra-paths ["target/cljsbuild" "src/cljs" "env/figwheel/resources" "env/figwheel/clj" "env/figwheel/cljs"]
   :main-opts   ["-m" "nrepl.cmdline"
                 "-p" "7070"
                 "--middleware" "[cider.piggieback/wrap-cljs-repl]"]}

bhauman 2021-04-12T22:45:37.052300Z

@msolli I think you should try cljs.main with the watch flag and see if you get the same compile time.

bhauman 2021-04-12T22:45:59.052500Z

This may just be the way it works.

bhauman 2021-04-12T22:48:29.052700Z

Hot reloading for modules doesn’t work if I recall correctly, my suggestion is that if you wan’t fast recompiles and hot reloading that you use a :main entry for dev and then break things into modules for prod.