Liking cljdoc? Tell your friends :D

title: Using NPM layout: docs category: docs order: 12

Using NPM

NPM is the defacto package repository for the JavaScript ecosystem. It holds a tremendous amount of valuable functionality. This guide will show you how to include and consume NPM packages in your ClojureScript codebase.

This guide is modeled after the ClojureScript Webpack Guide. If you prefer a more concise guide, feel free to head over there now.


**Quick Instructions**

Set the :target compiler option to :bundle. This will cause the compiler to emit an output file file that can be bundled a JavaScript bundler like webpack.

Optionally set the :bundle-cmd compiler option to

{:none {"npx" "webpack" "--mode=development" :output-to "-o"
:final-output-to}}

to ensure the output file is bundled after a compile. Figwheel will fill in :output-to and :final-output-to.

Your host page will need to load the final bundled asset.

Relevant Figwheel Options


Npm usage in Figwheel has changed significantly. For reference purposes the original version of this document can be found here

What?

NPM is a package repository for the JavaScript ecosystem. Almost all available JavaScript libraries are packaged, stored, and retrieved via NPM.

We want to use these libraries inside our ClojureScript codebase, but there is some friction because ClojureScript embraced the Google Closure Compiler and its method of declaring libraries, which is quite different than NPM's.

{::comment} We could get into a debate about why ClojureScript designers decided to embrace the less popular ecosystem, but that is largely academic at this point. I will say that the advantages of effortless interactive development via hot-reloading and the amazing capabilities of the Google Closure Compiler's advanced mode are direct benefits of using the GCC's (Google Closure Compiler's) method of defining modules via simple JavaScript object literals. In other words, without the GCC there would most likely not be a Figwheel.

Nevertheless, experiencing friction while importing libraries from the dominant JavaScript ecosystem is a very unfortunate trade-off. {:/comment}

However, with recent changes in the ClojureScript compiler (along with changes in Figwheel) it is now becoming much more straightforward to include NPM modules in your codebase.

Importing NPM libraries into your project

We are going to assume you are starting from the base example.

I'm going to use npm for this example but if you prefer yarn go ahead and use that. It doesn't really matter for this.

There are four steps that we are going to follow to add some libraries to our hello-world.core project.

  1. Initialize NPM in our project by adding a package.json file
  2. Install Webpack to bundle the needed dependencies into a single JS file.
  3. Add the needed libraries as NPM dependencies.
  4. Configure our ClojureScript build to use the bundle generated by Webpack.

Initialize NPM

We will need to initialize npm for our hello-world project.

One way to do this is to use the npm init command. You can of course just create the file from scratch but npm init is faster.

Make sure you are in the root directory of the project and execute:

$ npm init -y

This will create a package.json file in the root directory of your project along side your dev.cljs.edn file.

Install Webpack

We will need webpack to bundle our application along with our moment dependency to make it available to our ClojureScript code.

Install webpack and webpack-cli:

$ npm add --save-dev webpack webpack-cli

This add webpack and webpack-cli as development dependencies in your package.json. It will also download them to a node_modules directory in your project.

Add the needed libraries

Let's say we want to use the moment library in our application.

We'll use npm to add a moment dependency to our package.json file in the usual manner:

$ npm add moment

This should download and install the moment library along with its dependencies if it has any.

Configure our ClojureScript build

Everything we have done up until this point is very similar to what we would normally do if we were using NPM and Webpack for a simple JavaScript project.

In the dev.cljs.edn file we'll add the following config:

{:main hello-world.core
 :target :bundle
 :bundle-cmd {:none ["npx" "webpack" "--mode=development" :output-to "-o" :final-output-to]}}	

Understanding the above configuration is important, so I'm going to explain each part.

The :target compiler option is set to :bundle to instruct the ClojureScript compiler to produce an output file that can be bundled by a JavaScript bundler like Webpack.

The :bundle-cmd compiler option

Figwheel adds some additional functionality to the :bundle-cmd. It interpolates the keywords :output-to and :final-output-to into the command. In this case the :output-to is going to be replaced by the default :output-to path target/public/cljs-out/dev/main.js. The :final-ouput-to is replaced by the default value of :output-to with a _bundle added before the extension or target/public/cljs-out/dev/main_bundle.js.

Or stated more simply in this case:

  • :output-to is replaced with target/public/cljs-out/dev/main.js
  • :final-output-to is replaced with target/public/cljs-out/dev/main_bundle.js

If you supply your own :output-to cljs compiler option, it will be used instead of the default.

Thus after the ClojureScript compiler is finished compiling it will call the :bundle-cmd to bundle up the output.

In this case it will call:

$ npx webpack --mode=development target/public/cljs-out/dev/main.js -o target/public/cljs-out/dev/main_bundle.js

This will bundle up the main.js file and pull in the moment dependency along with it.

Now let's modify our source file to use moment so that we can make sure that things are working.

Edit the src/hello_world/core.cljs to look like:

(ns hello-world.core
  (:require [moment]))

(js/console.log moment)
(println (str "Hello there it's "
              (.format (moment) "dddd")))

Run the build

OK now that we've setup everything up we can run the build.

$ clojure -m figwheel.main -b dev -r

The browser and REPL should launch as usual:

Repl

Except now you can see one additional line in the output which notifies us that the bundle command was called.

And if you look at the dev tools console of the browser window that just popped open you should see similar output printed as below verifying that we were able to use moment successfully.

Repl

Well we successfully used an NPM package from our ClojureScript code. Now you can npm add other JavaScript NPM packages and use them from ClojureScript

Can you improve this documentation? These fine people already did:
Bruce Hauman, bpringe, Michael Camilleri, Alan Thompson, Jindrich Mynarz & Jonathon McKitrick
Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close