返回介绍

5. Configuration

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

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.

Example :dependencies
{: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

https://docs.npmjs.com/

yarn

https://yarnpkg.com/en/docs

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.

Installing a JavaScript package
# 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.

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

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

Example ~/.shadow-cljs/config.edn
{: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)
TipUse :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.

Example Leiningen 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]}
  ...)
TipYou 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.

  • OpenSSL instructions for Linux and Windows (via WSL)

  • macOS instructions

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

Startup message indicating the Port used
shadow-cljs - server running at http://0.0.0.0:9630

When :ssl is configured the server will be available via https:// instead.

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

Basic example serving the 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.

Serve directory from filesystem root
: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.

Serve from multiple roots
: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 to shadow.http.push-state/handle (this handler will only respond to requests with Accept: 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 vs some.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.

Example limited RAM use to 1GB
{: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.

ImportantWhen using deps.edn or project.clj the :jvm-opts need to be configured there.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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

发布评论

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