How to leverage npm modules in your next node.cljs project

npm, the official node.js package manager, is arguably one of the reasons why node has been so successful. The ability to easily pull in libraries from the internet provides a strictly more awesome developer experience as we know from using Leiningen to manage dependencies for Clojure projects.

In your node.cljs project you'll probably end up using Leiningen to manage your CLJS dependencies, and npm to manage your node dependencies. Ideally you'd have to use only one dependency management system, but we're not quite there yet in the node.cljs community.

Initialize your npm

To use npm for dependency management in your project the quickest way to get started is to run:

npm init # in your project directory

This will interactively prompt you for things like a project name and a version number, and then generate a package.json file for you. Feel free to just accept the defaults while messing around.

npm init is clever enough to work out a default project name from the name of the current working directory, pull your name and email address from ~/.npmrc, and get the git repo URL from .git if one exists.

Pull in some libraries

The main selling point of npm is the ability to pull in dependencies. One might use npm install -g <package name> to install a dependency globally on your dev box. Or npm install <package name> to install a dependency locally in the ./node_modules directory. But neither command makes a note of the dependency that you've just installed. Instead, you should use

npm install <package name> --save

This command will install the given package to ./node_modules, and it will also record the dependency in package.json. Now if you commit package.json to your source control repo (as you should), the next person checking out the project can run npm install to install all the project's dependencies.

It's all very similar to Leiningen and project.clj except you don't have to manually edit your package.json file - npm does it for you.

Don't commit node_modules

You've probably worked this our already, but please don't commit the node_modules directory or any of its contents to your source control repo. Instead you should list all your dependencies in package.json and use npm install to retrieve them after a clean checkout.

echo node_modules >> .gitignore

Update: read Marek's comment below on committing node_modules to source control.

Update your outdated dependencies

These days, when projects commonly depend on multiple external dependencies, it's difficult to keep on top new versions. npm has an outdated command that will list any dependencies that have more recent versions available:

% npm outdated
Package    Current  Wanted  Latest  Location
next-tick    0.1.0   0.1.0   0.2.1

In this case we want to use version 0.1.0, but there is a version 0.2.1 available if we wanted to upgrade.

The update command allows you to update the installed package based on the version string in your package.json file, but it will not modify you package.json file for you.

npm uses semantic versioning and the package.json dependency version string can optionally specify which version ranges are acceptable for satisfying that dependency. For example, a version string of ~0.2.1 matches versions that are reasonably close to 0.2.1, which means versions 0.2.1 <= x < 0.3.0 according to semantic versioning.

Scripts

npm isn't just a dependency manager, it can also be used to run your app. The scripts section in package.json can be used to define your own commands, which can, for example, run your app or your test, or execute some build step. The following example simply runs the app with node:

"scripts": {
  "start": "node <script.js>"
},

Now you can run your app with npm run start.

You might want to move all your development tasks from Leiningen to npm with a few scripts:

"scripts": {
  "start": "node <script.js>",
  "build": "lein cljsbuild once",
  "watch": "lein cljsbuild auto",
  "clean": "lein cljsbuild clean"
},

Requiring npm modules

Great: we've pulled in a bunch of dependencies from the interwebs, but how do we actually use them from our CLJS code?

JavaScript the language doesn't have a concept of modules, so node provides it's own module system in the form of the require() function. Pulling in modules in your CLJS code is simply a matter of calling the require() function in CLJS, eg:

(def fs (nodejs/require "fs"))

The symbols exported by the fs module are now bound to the fs var, which is slightly weird when you're used to Clojure namespaces. Eg the fs.rename() function is available as (.rename fs ...). Hopefully in the future we'll come up with some CLJS magic that will make requiring node modules feel a bit more natural.

An example app

Let's write a quick demo app as an example of all of the above. We'll use the nodecljs Leiningen template to generate a hello world app and modify the app to output colored text with the help of the cli-color npm package.

# generate a demo project
% lein new nodecljs npm-demo; cd npm-demo
# generate package.json
% npm init
# pull in the cli-color package
% npm install cli-color --save

Next modify src/npm_demo/core.cljs to look like this:

(ns npm-demo.core
    (:require [cljs.nodejs :as nodejs]))

(def clc (nodejs/require "cli-color"))

(defn -main []
  (println (.red clc "Hello") (.blue clc "world!")))

(nodejs/enable-util-print!)
(set! *main-cli-fn* -main)

That is, we'll pull in the cli-color library and use it in -main.

Compile and run the code:

% lein cljsbuild once
% node npm_demo.js
Hello world!

(imagine that Hello is red and world! is in blue - my blog doesn't do color in code blocks ;-)

I haven't bothered to specify any scripts, but you could totally paste in the above build, clean, watch scripts to your package.json file if you felt so inclined.

More than just dependency management

In addition to managing dependencies and running scripts, npm is also used to publish modules to the npm module repository. I haven't published any packages yet, so I won't write about it here. Your local search engine should be able to help you.

Conclusions

Similarly to the JVM and Maven central, a large part of the value in the node ecosystem comes from the npm module repository. Leverage npm to your advantage and make awesome node.cljs apps!


Want to read more about ClojureScript on Node?
Sign up for our mailing list!

Unsubscribe at any time. No spam, ever.
comments powered by Disqus