While the build was working in the dev environment
I had to also produce a minimized build for production.
That process brought more surprises.
With Leinigen I suggest making :target-dir resources/public.
— Bruce Hauman (Figwheel author)
I rushed to adop that advice - except that the :target-dir
should really be just resources
,
not resources/public
(you would end up with the build directory resources/public/public/cljs-out/…
).
The "figwheel build name" problem
:target-dir
configuration was just the beginning.
A bigger problem was that the JS artifact name depends on the build name.
So using the lein aliases we defined:
"fig:build" ["trampoline" "run" "-m" "figwheel.main" "--build" "dev" "--repl"]
;; a separate figwheel build is used for optimized build
"fig:min" ["run" "-m" "figwheel.main" "-O" "advanced" "--build-once" "min"]
Having two (or more) different artifact names isn’t great,
because we need to include it in the main HTML file - remember, we had this:
<script type="text/javascript" src="cljs-out/dev/main_bundle.js"></script>
Advance Compile docs
shows an example how to produce an optimized artifact using the dev
build/config
$ clj -m figwheel.main -O advanced -bo dev
If I used this approach, I would have the same artifact name for both unoptimized and optimized builds.
However, I don’t like it because you include the dev configuration
in the build of the artifact intended for the production.
You also cannot use both "min" and "dev" build at the same time.
Injecting the build name
After thinking about it for a while,
I decided to make the backend responsible for generating proper artifact name.
After all, it was the server side logic who rendered the main HTML template
(with Selmer).
I updated my main HTML file to use a variable:
<script type="text/javascript" src="cljs-out/{{figwheel/build-name}}/main_bundle.js"></script>
This is set in the backend clojure code responsible for rendering.
The app gets this name simply as a configuration setting in profiles.clj.
It’s a dev-only setting with a fallback to "min":
(defn home-page []
;; dynamic injection of figwheel build is needed to insert proper minified js into uberjar
(let [figwheel-build (or (:figwheel-build-name env) "min")]
(layout/render "home.html" {:figwheel/build-name figwheel-build})))
Making server port configuration flexible
To glue everything together, I needed one more piece:
the dynamic configuration of the URL serving the HTML file.
Since my app has both backend and frontend,
early on I added
custom :open-url
to the figwheel config:
:open-url "http://localhost:5001/app"
The problem was that a developer can choose another port.
In fact, the default port is 5000, it’s only me who’s using 5001
to avoid conflicts with other applications running on my machine.
Figwheel makes this port configuration flexible to some extent
- you can use [[server-port]]
placeholder and it’s expanded to the actual port used by Figwheel.
But this is not the same thing as my backend server port!
It’s the port of the internal ring server that Figwheel uses.
As such, it doesn’t work in our setup. I had to find a different solution.
I simply renamed figwheel-main.edn
to figwheel-main.edn.template
and added a script (dev-configure
Make target)
to parse the port from profiles.clj
and replace the
placeholder in figwheel-main.edn.template
with the correct port.
This output is then saved as the final figwheel-main.edn
file.
This is an extra step that the developer has to do,
but it’s typically a one-time thing - you run it once and that’s it.
Of course, if you change your port configuration, you have to run it again.
The code
You can see that I renamed figwheel-main.edn to figwheel-main.edn.template
and the final config file
is now generated dynamically.
That’s it - the transition to figwheel-main is now complete
and I can run both Clojure and ClojureScript REPLs again!