- 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
10. Targeting node.js
There is built-in support for generating code that is intended to be used as a stand-alone script, and also for code that is intended to be used as a library. See the section on common configuration for the base settings needed in a configuration file.
10.1. node.js Scripts
The :target :node-script
produces single-file stand-alone output that can be run using node.js
. The code is just ClojureScript, and an entry point is easy to define:
(ns demo.script)
(defn main [& cli-args]
(prn "hello world"))
10.1.1. Build Options
You will need the same basic main configuration as in other targets (like :source-paths
), but you’ll need some node-specific build target options:
:main | (required). The namespace-qualified symbol of your script’s entry point function. |
:output-to | (required). The path and filename for the generated script. |
:output-dir | (optional). The path for supporting files in development mode. Defaults to a cache directory. |
{:source-paths [...]
...
:builds
{:script
{:target :node-script
:main demo.script/main
:output-to "out/demo-script/script.js"}}}
When compiled this results in a standalone out/demo-script/script.js
file intended to be called via node script.js <command line args>
. When run it will call (demo.script/main <command line args>)
function on startup. This only ever produces the file specified in :output-to
. Any other support files (e.g. for development mode) are written to a temporary support directory.
10.1.2. Hot Code Reload
You will often write scripts that run as servers or some other long-running process. Hot code reload can be quite useful when working with these, and it is simple to set up:
Add start/stop callback functions.
Configure the build use those hooks.
Here is an example http server in node:
Sample node script with start/stop hooks for hot code reload.(ns demo.script
(:require ["http" :as http]))
(defn request-handler [req res]
(.end res "foo"))
; a place to hang onto the server so we can stop/start it
(defonce server-ref
(volatile! nil))
(defn main [& args]
(js/console.log "starting server")
(let [server (http/createServer #(request-handler %1 %2))]
(.listen server 3000
(fn [err]
(if err
(js/console.error "server start failed")
(js/console.info "http server running"))
))
(vreset! server-ref server)))
(defn start
"Hook to start. Also used as a hook for hot code reload."
[]
(js/console.warn "start called")
(main))
(defn stop
"Hot code reload hook to shut down resources so hot code reload can work"
[done]
(js/console.warn "stop called")
(when-some [srv @server-ref]
(.close srv
(fn [err]
(js/console.log "stop completed" err)
(done)))))
(js/console.log "__filename" js/__filename)
The associated configuration is (shadow-cljs.edn
):
{...
:builds
{ :script {... as before
; add in reload hooks
:devtools {:before-load-async demo.script/stop
:after-load demo.script/start}}}}
Warning | Many libraries hide state or do actions that prevent hot code reloading from working well. There is nothing the compiler can do to improve this since it has no idea what those libraries are doing. Hot code reload will only work well in situations where you can cleanly "stop" and "restart" the artifacts used. |
10.2. node.js Libraries
The :target :node-library
emits code that can be used (via require
) as a standard node library, and is useful for publishing your code for re-use as a compiled Javascript artifact.
As with other modes the main configuration options apply and must be set. The target-specific options are:
:target | Use :node-library |
:output-to | (required). The path and filename for the generated library. |
:output-dir | (optional). The path for supporting files in development mode. Defaults to a cache directory. |
The hot code reload story is similar to the script target, but may not work as well since it cannot as easily control all of the code that is loaded.
Controlling what code is actually exported is done via one of the following options:
:exports
- a map of keyword to fully qualified symbols:exports-var
- a fully qualified symbol:exports-fn
- a fully qualified symbol
10.2.1. Single static "default" export
:exports-var
will just return whatever is declared under that var. It can point to a defn
or normal def
.
:exports-var
{...
:builds {:lib {:output-to "lib.js"
:exports-var demo.ns/f
...}}}
Example CLJS(ns demo.ns)
(defn f [...] ...)
;; OR
(def f #js {:foo ...})
Consuming the generated code$ node
> var f = require('./lib.js');
f(); // the actual demo.ns/f function
It is effectively generating module.exports = demo.ns.f;
10.2.2. Multiple static named exports
Build configuration with multiple exports{...
:builds {:lib {:exports {:g demo.ns/f
:h other.ns/thing
:ns/ok? another.ns/ok?}
...}}}
The keyword is used as the name of the entry in the exported object. No munging is done to this keyword name (but namespaces are dropped). So, the above example maps cljs f
to g
, etc.:
$ node
> var lib = require('./lib.js');
lib.g(); // call demo-ns/f
lib["ok?"](); // call another-ns/ok?
You can achieve the exact same thing by using :exports-var
pointing to a def
(def exports #js {:g f
...})
10.2.3. "Dynamic" exports
In addition you may specify :exports-fn
as a fully qualified symbol. This should point to a function with no arguments which should return a JS object (or function). This function will only ever be called ONCE as node
caches the return value.
(ns demo.ns
(:require [demo.other :as other]))
(defn generate-exports []
#js {:hello hello
:foo other/foo})
{...
:builds {:lib {:exports-fn demo.ns/generate-exports
...}}}
Note | The exports config automatically tracks exported symbols and passes them on to the optimization stage. This means that anything listed in :exports will not be renamed by Google Closure optimizations. |
10.2.4. Full Example
The example below creates a lib.js
file intended to be consumed via the normal Node require
mechanism.
(ns demo.lib)
(defn hello []
(prn "hello")
"hello")
The build configuration would be:
{...
:builds {:library {:target :node-library
:output-to "out/demo-library/lib.js"
:exports {:hello demo.lib/hello}}}}
and the runtime use is as you would expect:
$ cd out/demo-library
$ node
> var x = require('./lib');
undefined
> x.hello()
hello
'hello'
As :node-script
this will only create the file specified in :output-to
. The :exports
map maps CLJS vars to the name they should be exported to.
Note | Development mode has the same setup as for node scripts (extra dependencies). |
10.3. Creating npm
packages
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论