mkdir hello-bundler
cd hello-bundler
This guide requires ClojureScript 1.10.741 or later and assumes familiarity with the Quick Start.
This page documents how to integrate ClojureScript with a typical JavaScript bundler such as Webpack. You should have Node.js installed. This guide assumes you have read through the Quick Start. This guide borrows liberally from this excellent guide on Webpack 2.
While we happen to use Webpack here, these instructions are easily adapted to other bundlers like Metro for React Native.
First create a project directory:
mkdir hello-bundler
cd hello-bundler
Create a deps.edn
file the following contents:
{:deps {org.clojure/clojurescript {:mvn/version "1.10.741"}}}
Create an empty package.json
file:
echo "{}" > package.json
Add webpack and its command line tools:
npm install --save-dev webpack webpack-cli
We’re now ready to setup our JS dependencies.
Install react and react-dom:
npm install --save react react-dom
Now create src/hello_bundler/core.cljs
with the following contents:
(ns hello-bundler.core
(:require [react]))
(.log js/console react/Component)
Notice that we are requiring React as if it was a normal require and a normal namespace.
In order for this to work we need to set a couple of compiler options. Create
a build.edn
file with the following:
{:main hello-bundler.core
:output-to "out/index.js"
:output-dir "out"
:target :bundle
:bundle-cmd {:none ["npx" "webpack" "./out/index.js" "-o" "out" "--mode=development"]
:default ["npx" "webpack" "./out/index.js" "-o" "out"]}
:closure-defines {cljs.core/*global* "window"}} ;; needed for advanced
Our build will generate out/index.js
which is exactly the entry file that
Webpack is looking for. We’ll write the bundler result back into the output
directory.
Note the new :target :bundle
option. This ensures that the generated code
is compatible with popular JavaScript bundlers that can handle Node.js style
require
. It also sets a bunch of other sensible defaults like externs
inference, so that advanced compilation will just work. :bundle-cmd
is just
an arbitrary shell command to run after the ClojureScript build completes.
In the case of watching bundlers like Metro you probably won’t bother with
:bundle-cmd
.
Let’s see this in action, the following will build your project then start a REPL:
clj -M -m cljs.main -co build.edn -v -c -r
Your default browser will open http://localhost:9000. Open the Developer Console,
you should see that React.Component
got logged.
At the REPL you can require react
and interact with it:
user> (require 'react)
You may find that you want to use React from node_modules
perhaps because
the latest bundled version hasn’t yet appeared on CLJSJS. Yet, you still want to
use some ClojureScript React binding like Reagent. ClojureScript supports this
out of the box.
To demonstrate this, change your deps.edn
to the following:
{:deps {org.clojure/clojurescript {:mvn/version "1.10.741"}
reagent {:mvn/version "0.10.0" :exclusions [cljsjs/react cljsjs/react-dom]}}}
Change your source file to the following:
(ns hello-bundler.core
(:require [goog.dom :as gdom]
[reagent.dom :as dom]))
(defn simple-component []
[:div
[:p "I am a component!"]
[:p.someclass
"I have " [:strong "bold"]
[:span {:style {:color "red"}} " and red "] "text."]])
(dom/render [simple-component] (gdom/getElement "app"))
Rebuild your project, run the REPL:
clj -M -m cljs.main -co build.edn -v -c -r
To verify that externs inference allows advanced compilation to work, let’s make an advanced build. REPLs don’t work under advanced compilation so you’ll have to manually open http://localhost:9000:
clj -M -m cljs.main -co build.edn -O advanced -v -c -s
That’s it!
When building webworkers with :target :bundle
, you use webpack (or your
preferred bundler) to add the webworker bootstrap.
So your build’s :target
will still be :bundle
(not :webworker
), but you
will tell your bundler to build a webworker. For example, with webpack you add the
--target=webworker
argument to your :bundle-cmd
entries.
You also need to define cljs.core/global
as "self"
(as opposed to
"window"
in browser builds).
An example build-webworker.edn
might look like:
{:main hello-bundler.webworker
:output-to "out/worker/index.js"
:output-dir "out/worker"
:target :bundle
:bundle-cmd {:none ["npx" "webpack" "out/worker/index.js" "-o" "out/worker/main.js" "--target=webworker" "--mode=development"]
:default ["npx" "webpack" "out/worker/index.js" "-o" "out/worker/main.js" "--target=webworker"]}
:closure-defines {cljs.core/*global* "self"}} ;; needed for advanced
Original author: David Nolen