I want to make a new Clojure / ClojureScript project
That will use Ring and http-kit on the backend.
Has a client written in ClojureScript which I can develop with hot-reloading using Figwheel.
I don’t care whether it’s lein based or clj-new etc.
I’d assumed that there must be some kind of template or off-the-shelf way to do this today. But I can’t find one. And all my attempts to either retrofit existing projects or to manually build this over the last week have ended in failures I can’t fix.
There probably are some templates, but I would suggest to avoid relying on them for various reasons and just build what you want from scratch. With modern tools it’s relatively trivial to do.
You mention failures that you can’t fix when trying to go that route. What exactly are those failures? Have you encountered them while following the official docs at Introduction | figwheel-main or by doing something else?
Cannot invoke "Object.getClass()" because "x" is null
error when Figwheel tries to recompile the ClojureScript client. I don’t get this if I recompile the client directly on the command line, so I’m guessing it’s something to do with the Figwheel configuration. Maybe a version incompatibility or similar.
From googling around it doesn’t look like that error is specifically to do with either Figwheel or even ClojureScript. Other people seem to have seen it in Clojure. So I’m guessing it’s from the compiler being called from Figwheel.
I’m doing something like Your Own Server | figwheel-main though the route I got there was through starting with a non-Figwheel project and trying to add Figwheel to it.
[Figwheel] Validating figwheel-main.edn
[Figwheel] figwheel-main.edn is valid \(ツ)/
[Figwheel] Compiling build dev to "resources/public/main.js"
[Figwheel] Failed to compile build dev in 1.715 seconds.
[Figwheel:WARNING] Could not Analyze src/cardiganbay/types.cljc line:38 column:1
Cannot invoke "Object.getClass()" because "x" is null
33
34 (media-list [ps])
35
36 )
37
38 (defprotocol IPageExporter
^---
39 (as-map [ex])
40 (page-name->export-file-path [ex page-name])
41 (export-path [ex])
42 (page-name->exported-link [ex page-name])
43 (media-name->exported-link [ex media-name])
[Figwheel:SEVERE] failed compiling file:src/cardiganbay/types.cljc
Figwheel doesn’t give you a stack-trace.
The file types.cljc is a cljc file (compiled from both Clojure and ClojureScript) and all it does is define a couple of interfaces using (defprotocol …)
This file compiles without problems both when used within the Clojure server. And also when I compile the ClojureScript client (that also includes it) from the command line
The error ONLY occurs when Figwheel tries to recompile it during the hot reload. So I’m assuming this is a Figwheel specific problem. And something to do with the way Figwheel is configured.
That’s why a good off-the-shelf template where all the parts (ie. the clojure, clojurescript , figwheel etc.) versions were compatible with each other, would probably resolve this. I’m sure the problem is within the Frankenstein’s monster of a configuration I’ve tried to piece together myself. Rather than that particular source file.
My instinct says its the code, rather than the tools or their configuration.
CLJC is often misunderstood in how it works, so it is easy to have JVM-isms leaking into a CLJS compilation pipeline in a way that cannot be compiled. Kinda hard to say without a stacktrace though. Never seen a defprotocol break before, so no guesses from my side.
There seems to be a rather big misunderstanding in how this all works.
a) shadow-cljs is implemented in Clojure and runs in the JVM, just as figwheel or cljs.main. No difference in that regard. The only difference is in the provided feature set, in that shadow-cljs generally does more, e.g. support npm packages out of the box without additional tools.
b) npm in shadow-cljs is only required if you want to use actual npm packages. No packages then no npm is required.
c) regardless of which build tool you use, they all take .clj(s|c) files and generate .js files as the output.
d) your JVM server does not need to know or care how these .js files were generated. It just needs to serve them as they are as text.
With shadow-cljs you have 2 options.
You can run it separately from your CLJ server. This is what I generally recommend, as it is generally simpler and avoids common problems such as dependency conflicts. It is also much closer to how a production setup would look, since no production setup should ever include running a build tool, doesn’t matter which, as part of the production CLJ server.
So, the workflow is npx shadow-cljs watch <your-build> during development. Once you are ready to move to production you make a release build, via npx shadow-cljs release <your-build> which optimizes everything and substantially reduces the size of the final build.
The other option is running it in embedded mode, which means it is started as part of your CLJ development setup and runs in the same JVM. And then controlling shadow-cljs via either its UI (default http://localhost:9630) or the CLJ REPL.
So can I import shadow-cljs into an existing project, just as I can with figwheel? As another dependency? And then run it entirely through clj, rather than installing and having anything to do with npm?
This doesn’t solve the immediate problem, but the book Learn Clojurescript by Andrew Meredith takes you through uaing Figwheel and shadow-cljs in a clear and concise way.
I think Cannot invoke "Object.getClass()" because "x" is null is hiding a NullPointerException that is lurking somewhere in your code. I would recommend you to search for null values. Hope this works!