返回介绍

3. Usage

发布于 2023-07-23 16:03:27 字数 13518 浏览 0 评论 0 收藏 0

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.

Release debugging commands.
$ 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.

CautionIt 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).

ImportantWhen 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.

ImportantIt 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.
Just compile :dev mode once, no REPL or live-reload:
$ 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.

Importanttools.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.

Simple 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.

ImportantThis 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.

Example Clojure Namespace in 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
TipThe 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.

ImportantWhen 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文