Starting a new ClojureScript project in 2022. Setup suggestions

I need to start a new ClojureScript project with the following properties :

  1. I want to write a ClojureScript app that runs purely in the browser. No need for a server. And eventually I want to compile it to a single JS lib in a stand-alone flat web-page.

  2. This app will be dependent on a library which is written in CLJC and is being worked on in parallel. This lib isn’t available on Clojars. I want to be able to pull the changes live from the library’s own project. (Currently this project is in Leiningen). I believe this is possible if my new project is written with CLI / deps.edn. AFAIK this can’t be done if the new project is itself in Lein. (Or can it now?)

  3. I’d like the “livest” development experience possible. Presumably using figwheel. Maybe devcards. Or maybe there’s something new that I don’t know about? I probably can’t expect that changes to the library automatically trigger a reload of the app in development, but at the least I want an easy way to tell the development app that the library just changed so that it will recompile / reload it.

  4. I’m on Linux and my editor of choice is Emacs. But I’m not an emacs power-user.

Given these requirements, what kind of project should I use? Shadow.js? CLI / deps.edn? Is it possible in lein (which I’m most familiar with)? What development frameworks etc? Is Figwheel / devcards still state of the art or do I want something else?

1 Like

Shadow-cljs + deps.edn seems to fit the bill. Especially with the “not on clojars” situation, deps.edn makes developing with local libraries great. Use the :local/root option to develop the library in parallel and changes will be picked up by shadow’s watcher.

If you want a quickstart to deps.edn, go here.

3 Likes

A quick plug for the deps-new SPA frontend template I started playing with to replace lein new re-frame and clj -Tclj-new create :template re-frame in my personal use cases… here

The original of which, the re-frame template, being something I consistently enjoy that does all of what you describe.

What is it I run exactly? I tried running

clj -Tclj-new create :template re-frame

but it just throws an error.

So you’re familiar with lein. Have you used clj-new? To use it as a ‘tool’ globally from the command line (invoking something with -T, read more here), you need to install it. Sean Corfield who adapted clj-new from boot-new gives the install one-liner with its current version on its repo.

Invoking clj -Tclj-new create :template re-frame is telling the tool to create a template. This particular template is not directly built-in to clj-new. I believe these are the templates automatically included with the tool. I.e, there is syntax sugar to use them by just typing clj -Tclj-new app instead of clj -Tclj-new create :template app.

The template re-frame does not need to be qualified AFAIR, but creating a template with that keyword is specifically adapting the existing leiningen re-frame template, which is a main purpose of clj-new:

Generate new projects from Leiningen or Boot templates, or clj-template projects, using just the clojure command-line installation of Clojure!

clj-new is typically oriented toward leveraging deps.edn for projects instead of lein’s project.clj. So normally, using a lein template would eschew leiningen’s build system altogether for the native tooling libraries. However, the re-frame template uses shadow-cljs instead of either to develop, run, build your app.

Have you used re-frame before?

I don’t know what scope your web app needs to be but, paired with its great docs, the library helped me learn a lot about structuring web projects in clj/s. However it’s a whole thing to get into lol!

Feel free look more into the differences between lein clj-new and deps-new to get a feel for their differences and how easy project setup is these days… I do not have expertise but I’ll try to answer any more questions

To elaborate a little on :local/root mentioned above, here is its usage in context.

;; deps.edn located in the project root

{:deps {org.clojure/clojure {:mvn/version "1.11.1"} ; syntax and coordinates available per-library on clojars
        standard-hosted-lib/library {:mvn/version "x.y.z"}
        me/my-local-library {:local/root "/absolute/path/to/my-local-library"}
        me/other-library {:local/root "../relative/to/other-library"}
        someone/their-library {:git/tag "x.y.z" :git/sha "1234567"} ; coordinates would be provided by the author if available solely on github
 ;; :paths [...] :aliases {...} etc
}

You can see you just need to supply the coordinates like anything else hosted on maven or git, but using the correct keyword and a dir path.

It’s a really powerful tool and there is plenty of writing elsewhere about the flexibility it provides compared to working on projects with leiningen, I will agree


Oops just note that the syntax would be slightly different here with shadow-cljs.edn as opposed to deps.edn .. Again linked above by @borkdude

The problem with this is if I run your single line it tells me that

-T is no longer supported, use -A with repl, -M for main, or -X for exec

But if I just substitute -T for -A or -M then I get an error file with :

{:clojure.main/message
 "Execution error (FileNotFoundException) at java.io.FileInputStream/open0 (FileInputStream.java:-2).\ncreate (No such file or directory)\n",
 :clojure.main/triage
 {:clojure.error/class java.io.FileNotFoundException,
  :clojure.error/line -2,
  :clojure.error/cause "create (No such file or directory)",
  :clojure.error/symbol java.io.FileInputStream/open0,
  :clojure.error/source "FileInputStream.java",
  :clojure.error/phase :execution},
 :clojure.main/trace
 {:via
  [{:type java.io.FileNotFoundException,
    :message "create (No such file or directory)",
    :at [java.io.FileInputStream open0 "FileInputStream.java" -2]}],
  :trace
  [[java.io.FileInputStream open0 "FileInputStream.java" -2]
   [java.io.FileInputStream open "FileInputStream.java" 219]
   [java.io.FileInputStream <init> "FileInputStream.java" 157]
   [java.io.FileInputStream <init> "FileInputStream.java" 112]
   [clojure.lang.Compiler loadFile "Compiler.java" 7571]
   [clojure.main$load_script invokeStatic "main.clj" 475]
   [clojure.main$script_opt invokeStatic "main.clj" 535]
   [clojure.main$script_opt invoke "main.clj" 530]
   [clojure.main$main invokeStatic "main.clj" 664]
   [clojure.main$main doInvoke "main.clj" 616]
   [clojure.lang.RestFn applyTo "RestFn.java" 137]
   [clojure.lang.Var applyTo "Var.java" 705]
   [clojure.main main "main.java" 40]],
  :cause "create (No such file or directory)"}}

I have to say that at this point I seem to have tried half a dozen completely different ways to boot up a new reagent project with clojure.tools, following various instructions / tutorials I have found online.

AND EVERY ONE HAS THROWN AN ERROR SOMEWHERE.

I CAN’T BELIEVE how bad and difficult it seems for a non expert to start up a new Clojure project these days.

In 2014 when I first got into Clojure, it was beautifully simple and straightforward using Leiningen. It just worked and I was programming within an hour!

WTF???

How have we all managed to make this whole thing infinitely worse in the last 10 years? More confusing! Layers of brittle dependencies! Dozens of outdated tutorials! New tools all of which explain I need the latest version of some other tool I’ve never heard of.

I’m genuinely shocked. How does anyone get started with Clojure these days? If it had been this hard back in 2014 I’d probably still be writing Python.

2 Likes

Well it’s neither my library nor template but I’d still like to know more. This hasn’t been my experience at all

What version of clojure?

That specific command I mentioned worked smoothly for me with
Clojure 1.11.1.1155
clj-new 1.2.399

Maybe time for a fresh reinstallation?

What have you installed in regards to tooling? What does clj -Ttools list output?

1 Like

How old is your Clojure version? I’d say first make sure you have the latest version of the JDK and Clojure, which you can do if you have brew like so:

brew install openjdk
brew install clojure/tools/clojure

Then run:

clojure -Ttools install com.github.seancorfield/clj-new '{:git/tag "v1.2.399"}' :as clj-new
clj -Tclj-new create :template re-frame

That’s it.

1 Like
clj -Ttools list 

just gives me

-T is no longer supported, use -A with repl, -M for main, or -X for exec

and

clj -Mtools

gives me

WARNING: Specified aliases are undeclared: [:tools]
Clojure 1.11.1 
user=> 

But seriously, basic setup is going to work or fail based on whether I’m using “Clojure 1.11.1” vs “Clojure 1.11.1.1155” ???

I take it we’re not using semantic versioning then?

I have Clojure 1.11.1

Java is

openjdk version “11.0.16” 2022-07-19
OpenJDK Runtime Environment (build 11.0.16+8-post-Ubuntu-0ubuntu120.04)
OpenJDK 64-Bit Server VM (build 11.0.16+8-post-Ubuntu-0ubuntu120.04, mixed mode, sharing)

Surely that should be enough? Should things really be succeeding or failing based on 1.11.1.x vs 1.11.1.y

I don’t use brew. I’m on Ubuntu.

Yes precisely. I was going to say it should be all of three to five commands to go from nothing installed to a project not only installed but running in active development.

I use jenv to manage which jdk I currently have in operation and if I run into mystery issues I’ll often just jump back and forth between, say, graal, openjdk or oracle just for kicks.

I feel like these odd issues just come down to something going wrong with some link in the chain. Which making sure versions match and software is installed properly generally fixes.

I don’t believe that’s it. 1.11.1.xxxx will all fall under the same 1.11.1 version when declaring dependencies per-project

1 Like

You could still use brew on Linux, that’s what I do as well, I’d recommend it, does make a lot of things easier when there’s isn’t always official Ubuntu repo packages for all the things. Just FYI: Homebrew on Linux — Homebrew Documentation

What happens when you run:

clj -version

The Clojure CLI is pretty new, and things were changing quite quickly, so ya, even a minor version could have added the tools supports.

I would also try:

clojure -T

Don’t type clj, type clojure. I’d be surprised, since both work for me, but I’m currently on a mac, so maybe rlwrap does something weird for you.

Otherwise, there’s something wrong maybe with your install.

Also, if you don’t want to learn how to use the new official build and deps tooling and Clojure command line, you can still use lein, a lot of people still use lein. You can at any time in the future choose to learn the new stuff and migrate your project to them.

clj -version
WARNING: When invoking clojure.main, use -M
Execution error (FileNotFoundException) at java.io.FileInputStream/open0 (FileInputStream.java:-2).
-version (No such file or directory)

Full report at:
/tmp/clojure-2320301461189067898.edn

Meanwhile

clojure -T
-T is no longer supported, use -A with repl, -M for main, or -X for exec

clj -version
WARNING: When invoking clojure.main, use -M
Execution error (FileNotFoundException) at java.io.FileInputStream/open0 > (FileInputStream.java:-2).
-version (No such file or directory)

Ok, can you just try uninstalling and re-installing?

Something is definitely wrong on your side.

Did you alias clj in your shell to something specific?

Seconding everything said by didibus.

I’m sorry this is frustrating because I sympathize when trying to start an effort seemingly fails for me before I can even get to step 1! It’s happened to me a lot. I hope you can get it dialed in because I’ve found the fruits of developing in this environment so rewarding.

I personally am absolutely no expert with any step of software development. Let alone the complexities of this language which has struck me from day 1 as advanced! But major steps have been taken by a lot of hands to smooth out the sharp corners.

These errors look really unusual and unrelated to any of the project tooling we’re discussing here. I’d emphasize reinstalling fresh. I also advocate for homebrew on unix-like systems.

1 Like

Ah … this is interesting.

One of the things I had been trying was @seancorfield’s instructions here : GitHub - seancorfield/clj-new: Generate new projects based on clj, Boot, or Leiningen Templates!

Specifically with the section entitled “Installation via deps.edn”

In that directory I had made a deps.edn file based on his suggestion there. And when I ran

clj -Mtools 

I got

clj -Mtools
Clojure 1.11.1
user=> 

But actually when running the same thing outside that directory, I’m seeing

clj -Mtools
Clojure 1.10.1
user=> 

So maybe I only have 1.10.1 installed “globally” and I was being misled by trying some of the things in that local context that was defined by having that deps.edn in the directory.

This would probably explain some of the mystery.

So, OK, what I need to do is to update Clojure cli.tools from 1.10 to 1.11

Is there really no way to do that except using homebrew? I don’t really want to bring yet another package manager into my life, as I already seem to have way too many.

But I assume clj doesn’t know how to upgrade itself then?

Ya, there’s a shell script installer as well, see here:
https://clojure.org/guides/install_clojure

1 Like

OK. Thanks.

I think I must have used that the first time I installed clj.tools, but at some point afterwards I must have allowed Ubuntu’s package manager to take over.