- 1. Introduction
- 2. Installation
- 3. Usage
- 4. REPL
- 5. Configuration
- 6. Build Configuration
- 7. Targeting the Browser
- 8. Targeting JavaScript Modules
- 9. Targeting React Native
- 10. Targeting node.js
- 11. Embedding in the JS Ecosystem The :npm-module Target
- 12. Testing
- 13. JavaScript Integration
- 14. Generating Production Code All Targets
- 15. Editor Integration
- 16. Troubleshooting
- 17. Publishing Libraries
- 18. What to do when things don’t work?
- 19. Hacking
3. Usage
shadow-cljs
can be used in many different ways but the general workflow stays the same.
During development you have the option to compile
a build once or run a watch
worker which watches your source files for changes and re-compiles them automatically. When enabled the watch
will also hot-reload your code and provide a REPL. During development the focus is on developer experience with fast feedback cycles. Development code should never be shipped to the public.
When it is time to get serious you create a release
build which creates an optimized build suitable for production. For this the Closure Compiler is used which applies some seriously :advanced
optimizations to your code to create the most optimal output available. This may require some tuning to work properly when using lots of interop with native JavaScript but works flawlessly for ClojureScript (and the code from the Closure Library).
3.1. Command Line
If installed globally, you can use the shadow-cljs
command directly.
$ shadow-cljs help
If you prefer to only use the local npm
install you can invoke it via npx
or yarn
.
# npm
$ npx shadow-cljs help
# yarn
$ yarn shadow-cljs help
# manually
$ ./node_modules/.bin/shadow-cljs help
The guide will assume there is a global install to keep examples short but this is not required.
Commonly used shadow-cljs commands during development# compile a build once and exit
$ shadow-cljs compile app
# compile and watch
$ shadow-cljs watch app
# connect to REPL for the build (available while watch is running)
$ shadow-cljs cljs-repl app
# connect to standalone node repl
$ shadow-cljs node-repl
Running a release build optimized for production use.$ shadow-cljs release app
Sometimes you may run into some release issues due to :advanced
compilation. These commands can help track down the causes.
$ shadow-cljs check app
$ shadow-cljs release app --debug
3.1.1. Server Mode
A shadow-cljs
command can be fairly slow to start. To improve this shadow-cljs
can run in "server mode" which means that a dedicated process is started which all other commands can use to execute a lot faster since they won’t have to start a new JVM/Clojure instance.
Commands that do long-running things implicitly start a server instance (eg. watch
) but it is often advisable to have a dedicated server process running.
You can run the process in the foreground in a dedicated terminal. Use CTRL+C
to terminate the server.
$ shadow-cljs server
# or (if you'd like REPL to control the server process)
$ shadow-cljs clj-repl
You can also run the server in the background controlled via the common start|stop|restart
functions.
$ shadow-cljs start
$ shadow-cljs stop
$ shadow-cljs restart
Once any server is running every other command will use that and run much faster.
3.2. Build Tool Integration
shadow-cljs
can integrate with other Clojure tools since the primary distribution is just a .jar
file available via Clojars. By default your :dependencies
are managed via shadow-cljs.edn
but you can use other builds tools to manage your dependencies as well.
Caution | It is strongly recommended to use the standalone shadow-cljs version. The command does a lot of things to optimize the user experience (e.g. faster startup) which are not done by other tools. You’ll also save yourself a lot of headaches dealing with dependency conflicts and other related errors. |
3.2.1. Leiningen
If you’d like to use Leiningen to manage your dependencies, you can do so by adding a :lein
entry to your shadow-cljs.edn
config. With this setting, the shadow-cljs
command will use lein
to launch the JVM, ignoring any :source-paths
and :dependencies
in shadow-cljs.edn
; relying instead on lein
to set them from project.clj
.
{:lein true
; :source-paths and :dependencies are now ignored in this file
; configure them via project.clj
:builds { ... }
Using a dedicated lein
profile{:lein {:profile "+cljs"}
:builds {...}}
Sample project.clj(defproject my-awesome-project
...
:profiles
{:cljs
{:source-paths ["src/cljs"]
:dependencies [[thheller/shadow-cljs "..."]
[reagent "0.8.1"]]}})
When using project.clj
to manage your :dependencies
you must manually include the thheller/shadow-cljs artifact in your :dependencies
(directly or in a profile).
Important | When you are running into weird Java Stackstraces when starting shadow-cljs or trying compile builds you may have a dependency conflict. It is very important that shadow-cljs is used with proper matching org.clojure/clojurescript and closure-compiler versions. You can check via lein deps :tree and the required versions are listed on clojars (on the right side). |
Running Tasks Directly From Leiningen
You may also directly execute shadow-cljs
commands via lein
if you prefer to not use the shadow-cljs
command itself.
Important | It is recommended to still use the shadow-cljs command to run commands since that will take full advantage of a running server mode instance. This will run commands substantially faster than launching additional JVMs when using lein directly. |
$ lein run -m shadow.cljs.devtools.cli compile build-id
Create a :release mode optimized build:$ lein run -m shadow.cljs.devtools.cli release build-id
3.2.2. tools.deps / deps.edn
The new deps.edn can also be used to manage your :dependencies
and :source-paths
instead of using the built-in methods or lein
. All shadow-cljs
commands will then be launched via the new clojure
utility instead.
Important | tools.deps is still changing quite frequently. Make sure you are using the latest version. |
To use this set the :deps true
property in your config. It is also possible to configure which deps.edn
aliases should be used.
You must add the thheller/shadow-cljs
artifact to your deps.edn
manually.
shadow-cljs.edn
example{:deps true
:builds ...}
Simple deps.edn
example{:paths [...]
:deps {thheller/shadow-cljs {:mvn/version <latest>}}}
Example shadow-cljs.edn
with :cljs alias{:deps {:aliases [:cljs]}
:builds ...}
Example deps.edn
{:paths [...]
:deps {...}
:aliases
{:cljs
{:extra-deps {thheller/shadow-cljs {:mvn/version <latest>}}}}
With this you are all set, and can run shadow-cljs
as normal.
Option: Running via clj directly
Optionally, if you want to skip running the shadow-cljs
command line tool directly, you may as well just run directly via clj
.
Important | This bypasses the "server mode". Meaning that everything you run will run a new JVM instance and potentially be much slower. You’ll lose out on some features outlined here. Other than that the compilation results will be identical. |
{:paths [...]
:deps {...}
:aliases
{:shadow-cljs
{:extra-deps {thheller/shadow-cljs {:mvn/version <latest>}}
:main-opts ["-m" "shadow.cljs.devtools.cli"]}}}
clj -A:shadow-cljs watch app
You may also specify additional aliases via the command line using -A
, eg. shadow-cljs -A:foo:bar …
.
3.2.3. Boot
The authors have little Boot experience, so this chapter is in need of contributions. We understand that Boot allows you to build your tool chain out of functions. Since shadow-cljs
is a normal JVM library, you can call functions within it to invoke tasks.
Some boot tasks are available here: https://github.com/jgdavey/boot-shadow-cljs
3.3. Running Clojure Code
You can use the shadow-cljs
CLI to call specific Clojure functions from the command line. This is useful when you want to run some code before/after certain tasks. Suppose you wanted to rsync
the output of your release
build to a remote server.
src/my/build.clj
(ns my.build
(:require
[shadow.cljs.devtools.api :as shadow]
[clojure.java.shell :refer (sh)]))
(defn release []
(shadow/release :my-build)
(sh "rsync" "-arzt" "path/to/output-dir" "my@server.com:some/path"))
Running the release
function$ shadow-cljs clj-run my.build/release
# or
$ shadow-cljs run my.build/release
You can pass arguments to the invoked functions via the command line.
Using arguments via normal Clojure fn args...
(defn release [server]
(shadow/release :my-build)
(sh "rsync" "-arzt" "path/to/output-dir" server))
Passing the server from the command line$ shadow-cljs clj-run my.build/release my@server.com:some/path
Tip | The usual (defn release [& args]) structure also works if you want to parse the args with something like tools.cli. |
You have access to the full power of Clojure here. You can build entire tools on top of this if you like. As a bonus everything you write this way is also directly available via the Clojure REPL.
Important | When the server is running the namespace will not be reloaded automatically, it will only be loaded once. It is recommended to do the development using a REPL and reload the file as usual (eg. (require 'my.build :reload) ). You may also run shadow-cljs clj-eval "(require 'my.build :reload)" to reload manually from the command line. |
3.3.1. Calling watch via clj-run
By default the functions called by clj-run
only have access to a minimal shadow-cljs
runtime which is enough to run compile
, release
and any other Clojure functionality. The JVM will terminate when your function completes.
If you want to start a watch
for a given build you need to declare that the function you are calling requires a full server. This will cause the process to stay alive until you explicitly call (shadow.cljs.devtools.server/stop!)
or CTRL+C
the process.
(ns demo.run
(:require [shadow.cljs.devtools.api :as shadow]))
;; this fails because a full server instance is missing
(defn foo
[& args]
(shadow/watch :my-build))
;; this metadata will ensure that the server is started so watch works
(defn foo
{:shadow/requires-server true}
[& args]
(shadow/watch :my-build))
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论