ClojureScript Terminal-User-Interface Template
Create rich Terminal-User-Interface (TUI) apps in ClojureScript using web technologies you already know.
Table of Contents
- Quick Start
- Features
- What's included?
- Usage
- Getting Started
- Dependencies and Further Information
- Frequently Asked Questions
- Credits
- Thanks
- Support
- Contributing
- License
Quick Start
Getting started is easy. Assuming you have node, clojure, and lein 2 installed:
lein new cljs-tui my-test-project
cd my-test-project
npm run init
Once the ClojureScript builds run it in another terminal window:
node --inspect target/js/compiled/my-test-project.js
You should now see the demo UI from the gif above.
Features
Develop rich apps, live with ClojureScript
Re-render on Save
Change a view within a ClojureScript source file, save it, and the build server will automatically re-build your app and re-render your views without losing state. Ideal when developing nested views.
Instant changes from REPL
Connect your editor to the nREPL server and send code changes to your running app without even saving a file. Ideal for changing application state, redefining functions, or debugging.
Use web technologies you already know
- Write views in React with the ClojureScript Reagent wrapper.
- Reagent views compose together just like React components
Create rich applications
- Use Reagent atoms for simple, local state updates
- Use Reframe for handling effects and updating shared app state
- Leverage functional programming and data-oriented design to the fullest for a fast feedback cycle.
Distribute your applications
Transpile ClojureScript into vanilla JS and distribute your application on NPM for easy installation and quick startup times.
What's included?
Upon generating an app from this template the following resources will be available with slight differences depending on the build tool you choose.
<root>
|- bin/
| |- <project-name> - A JS script to run your compiled app as a npm binary
|- docs/
| |- intro.md - A docs into template
|- env/
| |- <project_name>/
| | |- debug/views.cljs - A debug view to display current state and captured text output
| | app.cljs - Development app entrypoint. Handle reloading, re-rendering, and initializing the debug views
| |- dev/user.clj - Only for figwheel based builds
|- scripts/
| |- build - A bash script for creating a production build
|- src/
| |- <project_name>/
| | |- demo/
| | | |- views.cljs - Demo app views. You can do whatever you want with these
| | |- core.cljs - Initialize the stateful application dependencies like reframe, reagent, and blessed
| | |- events.cljs - Re-frame event handlers and intercepters to update app state db
| | |- keys.cljs - Utils for handling key presses and global keyboard event handlers
| | |- main.cljs - Production app entrypoint
| | |- subs.cljs - Re-frame subscriptions to app state db
| | |- views.cljs - General views such as a primitive router and vertical menu components
|- test/
| |- <project_name>/
| | |- core_test.cljs - A sample test. Should fail until fixed
| | |- test_runner.cljs - Figwheel builds only. Runs tests and ensures proper exit codes for CI
|- .gitignore
|- .hgignore
|- CHANGELOG.md
|- dev.cljs.edn - Figwheel-main development config.
|- figwheel-main.edn - Figwheel-main config only
|- LICENSE
|- package.json - Used to manage your npm modules and config for publishing to npm
|- prod.cljs.edn - Figwheel-main production config
|- project.clj - Figwheel builds only
|- README.md
|- shadow-cljs.edn - Shadow-cljs builds only. ClojureScript build configuration
Usage
Generating a project template
lein new cljs-tui <project-name> [+<build-tool>]
Generate a demo cljs-tui application for the specified build-tool.
Supported build-tool
options:
+shadow
(default)
Generate a ClojureScript Terminal-User-Interface app template using shadow-cljs. This is the default build-tool.
+figwheel-main
Generate a ClojureScript Terminal-User-Interface app template using figwheel-main.
+lein-figwheel
https://github.com/bhauman/lein-figwheel
Generate a ClojureScript Terminal-User-Interface app template using lein-figwheel.
Getting Started
The following steps work for any supported build tool you choose. The tool specific commands have been placed into the project package.json
scripts to make the commands consistent between each tool.
All of the following commands assume you are running them in a terminal from the root directory of your generated project.
Installing dependencies
To get started, install the required npm modules into a local node_modules folder.
npm install
Build for development
Start a development build server to watch your files for changes and automatically recompile and reload your app. Your re-frame app state will remain the same but the UI will re-render allowing you to make changes to components deeper into a flow without a tedious manual testing process.
npm start
Running your app
When a development build is first finished the generated JS file is written to target/js/compiled/<project-name>.js
. You can execute the following program to start your app and connect to the development server and\or REPL:
node --inspect target/js/compiled/<project-name>.js
Build for production
When you are ready to release your app you will need to do a production build to produce a smaller JS footprint and build a single file the project's bin/<project-name>
file will be able to find and use.
npm run build
This will build your ClojureScript app for production, run loose-envify to ensure your code runs in a production environment, and finally moves the compiled JS file to the lib folder as an npm convention. The build process can be found in the scripts/build file in your generated project folder.
Updating NPM dependencies
Your npm modules are managed through your project's package.json
file. There are two common ways to update your project's deps.
NPM CLI
Run the following in the project directory:
npm install --save [@scope/]<package-name>[@<version>]
Example:
npm install --save highland
You can find more at npm's install docs
Manual
- Update your package.json file
dependencies
map. - Save your updated package.json.
- Run
npm install
Using NPM modules
To use a npm module in ClojureScript use JS interop require:
(def stream (js/require "highland"))
Updating Clojure dependencies
Clojure dependencies are handled a bit differently and vary a bit between figwheel and shadow-cljs projects.
After updating your dependencies it's recommended to restart your build server.
figwheel-main or lein-figwheel
Update the dependencies map in your project's project.clj
file.
shadow-cljs
Update the dependencies map in your project's shadow-cljs.edn
file.
Working with nREPL
All supported build tools do offer an nREPL server option if you would like to develop against the live state of your app. However, the process differs between build tools.
Both figwheel-main and lein-figwheel have an extra development file located in env/dev/user.clj
which defines a (start)
and (repl)
function for working with nREPL.
Starting a nREPL server with figwheel-main or lein-figwheel
- Shutdown any running build servers, we're going to run them from the REPL.
- Run
npm run repl
- When the Clojure REPL loads eval
(start)
to start the build server, nREPL server, and ClojureScript REPL interface. - Once your application compiles, run the compiled node app in another terminal to initialize the ClojureScript REPL:
node --inspect target/js/compiled/<my-project>.js
Starting a nREPL server with shadow-cljs
Amazingly, shadow-cljs starts an nREPL server automatically when you run the build server. When the build server starts it creates an .nrepl-port file most Clojure editor REPL plugins can use to connect with.
Connecting your editor to the REPL
You can use the .nrepl-port file to connect to the running nREPL server. Note when you connect it will be in Clojure and not ClojureScript. You can enter the ClojureScript REPL by evaluating the following form:
user=> (repl)
This behavior is defined in env/dev/user.clj
and can be customized with your chosen build-tool's API.
Testing
To run tests run the command following from your project directory:
npm test
This will compile the test build, run the test runner, which will asynchronously run the imported tests files and print the results. The test runner will exit in a non-zero exit code if the tests fail making it compatible with various continuous integration (CI) services. In the near future I would like to setup auto testing so tests rerun upon save.
Dependencies and Further Documentation
This template makes use of many open source projects which may have more docs to support your development with a bigger community behind it.
Clojure
Name | Description |
---|---|
Clojure | Base tooling runs on Clojure |
ClojureScript | Language of choice for this template |
figwheel-main | One of the supported cljs build tools |
leiningen | Clojure\ClojureScript project management |
lein-figwheel | Another cljs build tool option, leiningen plugin |
Clojure \ ClojureScript
Name | Description |
---|---|
Mount | Used to manage stateful dependencies |
Reagent | ClojureScript view layer |
Reframe | Manages application state and side effects |
tools.cli | Parse CLI args into hash-maps |
npm modules
Name | Description |
---|---|
Blessed | Abstraction layer over terminal UI |
Create-React-Class | Bridge react and reagent |
React | nodejs view layer |
React-Blessed | Blessed backend for React views |
React DOM | Required by reagent |
Shadow CLJS | Default build tool, manages builds, compiles cljs |
FAQs
Why ClojureScript?
I really enjoy working with ClojureScript, it's been a fun, productive, and empowering experience so far. My reasoning can be found in the announcement blog post.
Why is Shadow the default build-tool?
After working with all three tools, shadow-cljs provides the smoothest development experience, great docs, and fewest layers. I covered this more in the announcement blog post.
Does this template support boot?
Currently this template only supports leiningen. However, the shadow-cljs build option does not use leiningen or boot at all. Please create an issue if supporting boot would be of interest to you.
Credits
In early 2019 I saw this link on Reddit in r/clojure:
https://github.com/denisidoro/floki
Denis Isidoro created this beautiful terminal-user-interface JSON explorer app using ClojureScript, Reagent, React, and Blessed. It completely blew my mind! This template would not exist without him or his work on Floki.
Camilo Polymeris came up with the concept and first implementation of this stack in the gist https://gist.github.com/polymeris/5e117676b79a505fe777df17f181ca2e. It then became the basis for Floki, and elements of it even made their way into this template.
Thanks
Big thanks to the Clojurians on Slack and Zulip! They've endured countless beginner Clojure and ClojureScript questions to help put this project together.
Also shout out to VenueBook for supporting my development efforts of this open-source project.
Support
Please create issues on related dependencies unless it is specific to this template. The library communities are more active and have more support resources. If you do run into problems with this template please create an issue on this repo. For the quickest response, share your issue or questions in the cljs-tui-template topic on the Clojurians Zulip chat.
Contributing
Contributors are welcome, please create pull requests or file issues if you would like to discuss it first. There's a lot of potential in this project and it would be better to provide more library views for prototyping apps quickly.
License
Copyright © 2019 Jay Zawrotny
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.