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