- 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
5. Configuration
shadow-cljs
is configured by a shadow-cljs.edn
file in your project root directory. You can create a default one by running shadow-cljs init
. It should contain a map with some global configuration and a :builds
entry for all your builds.
{:source-paths [...]
:dependencies [...]
:builds {...}}
An example config could look like this:
{:dependencies
[[reagent "0.8.0-alpha2"]]
:source-paths
["src"]
:builds
{:app {:target :browser
:output-dir "public/js"
:asset-path "/js"
:modules {:main {:entries [my.app]}}}}}
The file structure for this example should look like this:
.
├── package.json
├── shadow-cljs.edn
└── src
└── my
└── app.cljs
5.1. Source Paths
:source-paths
configures your JVM classpath. The compiler will use this config to find Clojure(Script) source files (eg. .cljs
).
It is fine to put everything into one source path but you can use multiple if you want to "group" source files in certain ways. It is useful if you want to keep your tests separate for example.
Using multiple source paths{:source-paths ["src/main" "src/test"]
...}
File Structure.
├── package.json
├── shadow-cljs.edn
└── src
└── main
└── my
└── app.cljs
└── test
└── my
└── app_test.cljs
It is not recommended to separate source files by extension (eg. src/clj
, src/cljs
, src/cljc
). For some reason this is widely used in CLJS project templates but it just makes things harder to use.
5.2. Dependencies
5.2.1. Clojure(Script)
Your dependencies are managed via the :dependencies
key at the root of the shadow-cljs.edn
config file. They are declared in the same notation that other Clojure tools like lein
or boot
use.
Each dependency is written as a vector using [library-name "version-string"]
nested in one outer vector.
{:source-paths ["src"]
:dependencies [[reagent "0.9.1"]]
:builds ...}
Notice that the source path is only specified once in the entire configuration. The system will use namespace dependency graphs to determine what code is needed in the final output of any given build.
5.2.2. JavaScript
shadow-cljs
integrates fully with the npm
ecosystem to manage JavaScript dependencies.
You can use npm
or yarn
to manage your dependencies, please refer to their respective documentation.
npm | |
yarn |
Both manage your dependencies via a package.json
file in your project directory. Almost every package available via npm
will explain how to install it. Those instructions now apply to shadow-cljs
as well.
# npm
$ npm install the-thing
# yarn
$ yarn add the-thing
Nothing more is required. Dependencies will be added to the package.json
file and this will be used to manage them.
Tip | If you don’t have a package.json yet run npm init from a command line. |
Missing JS Dependency?
You might run into errors related to missing JS dependencies. Most ClojureScript libraries do not yet declare the npm
packages they use since they still expect to use CLJSJS. We want to use npm
directly which means you must manually install the npm
packages until libraries properly declare the :npm-deps
themselves.
The required JS dependency "react" is not available, it was required by ...
This means that you should npm install react
.
Tip | In the case of react you probably need these 3 packages: npm install react react-dom create-react-class . |
5.3. User Configuration
Most configuration will be done in the projects themselves via shadow-cljs.edn
but some config may be user-dependent. Tools like CIDER may require the additional cider-nrepl
dependency which would be useless for a different team member using Cursive when adding that dependency via shadow-cljs.edn
.
A restricted set of config options can be added to ~/.shadow-cljs/config.edn
which will then apply to all projects built on this users machine.
Adding dependencies is allowed via the usual :dependencies
key. Note that dependencies added here will apply to ALL projects. Keep them to a minimum and only put tool related dependencies here. Everything that is relevant to a build should remain in shadow-cljs.edn
as otherwise things may not compile for other users. These dependencies will automatically be added when using deps.edn
or lein
as well.
{:dependencies
[[cider/cider-nrepl "0.21.1"]]}
;; this version may be out of date, check whichever is available
When using deps.edn
to resolve dependencies you may sometimes want to activate additional aliases. This can be done via :deps-aliases
.
;; shadow-cljs.edn in project
{:deps {:aliases [:cljs]}}
;; ~/.shadow-cljs/config.edn
{:deps-aliases [:cider]}
This will make the shadow-cljs
command use the [:cider :cljs]
aliases in projects using deps.edn
. This might be useful if you have an additional :cider
alias in your ~/.clojure/deps.edn
.
By default the shadow-cljs
server-mode will launch an embedded nREPL server which you might not need. You can disable this by setting :nrepl false
in user config.
The only other currently accepted value in the user config is the :open-file-command. No other options are currently have any effect.
5.4. Server Options
This section is for other options that configure the shadow-cljs
server instance. They are optional.
5.4.1. nREPL
The shadow-cljs
server provides a nREPL server via TCP. If you look at the startup message you’ll see the port of nREPL, and the port will also be stored in target/shadow-cljs/nrepl.port
:
$ shadow-cljs watch app
shadow-cljs - HTTP server available at http://localhost:8600
shadow-cljs - server version: <version> running at http://localhost:9630
shadow-cljs - nREPL server started on port 64967
shadow-cljs - watching build :app
[:app] Configuring build.
[:app] Compiling ...
You can configure the port and additional middleware with shadow-cljs.edn
:
{...
:nrepl {:port 9000
:middleware []} ; optional list of namespace-qualified symbols
...}
The default global config file in ~/.nrepl/nrepl.edn
or the local .nrepl.edn
will also be loaded on startup and can be used to configure :middleware
.
If the popular middleware cider-nrepl is found on the classpath (e.g. it’s included in :dependencies
), it will be used automatically. No additional configuration required. This can be disabled by setting :nrepl {:cider false}
.
You may configure the namespace you start in when connecting by setting :init-ns
in the :nrepl
options. It defaults to shadow.user
.
{...
:nrepl {:init-ns my.repl}
...}
The nREPL server can be disabled by setting :nrepl false
.
nREPL Usage
When connecting to the nREPL server the connection always starts out as a Clojure REPL. Switching to a CLJS REPL works similarly to the non-nREPL version. First the watch
for the given build needs to be started and then we need to select this build to switch the current nREPL session to that build. After selecting the build everything will be eval’d in ClojureScript instead of Clojure.
(shadow/watch :the-build)
(shadow/repl :the-build)
Tip | Use :cljs/quit to return to Clojure. |
Embedded nREPL Server
When you use shadow-cljs
embedded in other tools that provide their own nREPL server (eg. lein
) you need to configure the shadow-cljs
middleware. Otherwise you won’t be able to switch between CLJ and CLJS REPLs.
project.clj
(defproject my-amazing-project "1.0.0"
...
:repl-options
{:init-ns shadow.user ;; or any of your choosing
:nrepl-middleware
[shadow.cljs.devtools.server.nrepl/middleware]}
...)
Tip | You still need to start the embedded server manually before using the CLJS REPL. |
5.4.2. Socket REPL
A Clojure Socket REPL is started automatically in server-mode and uses a random port by default. Tools can find the port it was started under by checking .shadow-cljs/socket-repl.port
which will contain the port number.
You can also set a fixed port by via shadow-cljs.edn
.
{...
:socket-repl
{:port 9000}
...}
The Socket REPL can be disabled by setting :socket-repl false
.
5.4.3. SSL
The shadow-cljs
HTTP servers support SSL. It requires a Java Keystore that provides a matching private key and certificate.
shadow-cljs.edn
with SSL configured{...
:ssl {:keystore "ssl/keystore.jks"
:password "shadow-cljs"}
...}
The above are the defaults so if you want to use those it is fine to just set :ssl {}
.
You can create a Keystore using the java keytool
command. Creating a trusted self-signed certificate is also possible but somewhat complicated.
The created Certificates.p12
(macOS) or localhost.pfx
(Linux, Windows) file can be turned into the required keystore.jks
via the keytool
utility.
$ keytool -importkeystore -destkeystore keystore.jks -srcstoretype PKCS12 -srckeystore localhost.pfx
Important | You must generate the Certificate with a SAN (Subject Alternative Name) for "localhost" (or whichever host you want to use). SAN is required to get Chrome to trust the Certificate and not show warnings. The password used when exporting must match the password assigned to the Keystore. |
5.4.4. Primary HTTP(S)
The shadow-cljs
server starts one primary HTTP server. It is used to serve the UI and websockets used for Hot Reload and REPL clients. By default it listens on Port 9630. If that Port is in use it will increment by one and attempt again until an open Port is found.
shadow-cljs - server running at http://0.0.0.0:9630
When :ssl
is configured the server will be available via https://
instead.
Tip | The server automatically supports HTTP/2 when using :ssl . |
If you prefer to set your own port instead you can do this via the :http
config.
shadow-cljs.edn
with :http
config{...
:http {:port 12345
:host "my.machine.local"}
...}
:ssl
switches the server to server https://
only. If you want to keep the http://
version you can configure a separate :ssl-port
as well.
{...
:http {:port 12345
:ssl-port 23456
:host "localhost"}
...}
5.4.5. Development HTTP(S)
shadow-cljs
can provide additional basic HTTP servers via the :dev-http
config entry. By default these will serve all static files from the configured paths, and fall back to index.html
when a resource is not found (this is what you typically want when developing an application which uses browser push state).
These servers are started automatically when shadow-cljs
is running in server mode. They are not specific to any build and can be used to serve files for multiple builds as long as a unique :output-dir
is used for each.
- IMPORTANT
These are just generic web servers that server static files. They are not required for any live-reload or REPL logic. Any webserver will do, these are just provided for convenience.
public
directory via http://localhost:8000
{...
:dev-http {8000 "public"}
:builds {...}}
:dev-http
expects a map of port-number
to config
. The config
supports several shortcuts for the most common scenarios.
:dev-http {8000 "public"}
Serve from classpath root:dev-http {8000 "classpath:public"}
This would attempt to find a request to /index.html
via public/index.html
on the classpath. Which may include files in .jar
files.
:dev-http {8000 ["a" "b" "classpath:c"]}
This would first attempt to find <project-root>/a/index.html
then <project-root>/b/index.html
then c/index.html
on the classpath. If nothing is found the default handler will be called.
The longer config version expects a map and the supported options are:
:root
(String) The path from which to serve requests. Paths starting with
classpath:
will serve from the classpath instead of the filesystem. All filesystem paths are relative to the project root.:roots
(Vector of Strings) If you need multiple root paths, use instead of
:root
.:ssl-port
When
:ssl
is configured use this port for ssl connections and server normal HTTP on the regular port. If:ssl-port
is not set but:ssl
is configured the default port will only server SSL requests.:host
Optional. The hostname to listen on. Defaults to localhost.
:handler
Optional. A fully qualified symbol. A
(defn handler [req] resp)
that is used if a resource is not found for the given request. Defaults toshadow.http.push-state/handle
(this handler will only respond to requests withAccept: text/html
header.)
The following two options only apply when using the default, built-in handler and typically do not need to be changed:
:push-state/headers
(optional) A map of HTTP headers to respond with. Defaults to
text/html
standard headers.:push-state/index
(optional) The file to serve. Defaults to
index.html
.
{...
:dev-http
{8080 {:root "public"
:handler my.app/handler}}}
Reverse Proxy Support
By default the dev server will attempt to serve requests locally but sometimes you may want to use an external web server to serve requests (eg. API request). This can be configured via :proxy-url
.
{...
:dev-http
{8000
{:root "public"
:proxy-url "https://some.host"}}}
A request going to http://localhost:8000/api/foo
will serve the content returned by https://some.host/api/foo
instead. All request that do not have a local file will be served by the proxied server.
Additional optional Options to configure the connection handling are:
:proxy-rewrite-host-header
boolean, defaults to true. Determines whether the original Host header will be used or the one from the
:proxy-url
.localhost
vssome.host
using the example above.:proxy-reuse-x-forwarded
boolean, defaults to false. Configures if the proxy should add itself to
X-Forwarded-For
list or start a new one.:proxy-max-connection-retries
int, defaults to 1.
:proxy-max-request-time
ms as int, defaults to 30000. 30sec request timeout.
5.5. JVM Configuration
When shadow-cljs.edn
is used in charge of starting the JVM you can configure additional command line arguments to be passed directly to the JVM. For example you may want to decrease or increase the amount of RAM used by shadow-cljs.
This is done by configuring :jvm-opts
at the root of shadow-cljs.edn
expecting a vector of strings.
{:source-paths [...]
:dependencies [...]
:jvm-opts ["-Xmx1G"]
:builds ...}
The arguments that can be passed to the JVM vary depending on the version but you can find an example list here. Please note that assigning too little or too much RAM can degrade performance. The defaults are usually good enough.
Important | When using deps.edn or project.clj the :jvm-opts need to be configured there. |
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论