Skip to content
Juho Teperi edited this page Feb 26, 2019 · 30 revisions

Packaging up a JavaScript library for use in ClojureScript is usually not too difficult.

While it's the rare case please note that some JS libraries are written in a Google Closure compatible way and do require a slightly different process. See this discussion

The following list goes through the process, step-by-step. If you encounter any problems, consult README for link to project discussion channel.

1. Fork this repository and create a branch

You'll do your work in this branch in order to submit the package later.

2. Find the library's source code

  1. Best place is the official browserified files from npm package, often under dist/, these can be downloaded from unpkg, e.g. https://unpkg.com/react-datepicker@0.53.0/dist/
  2. Links from libraries homepage
  3. Cdnjs
  4. Archive file from Github
  5. As last option, one can download the source and build browserified code using webpack or other tools (using official files is less error prone and simpler).

3. Create a directory structure for the package

Use the library's name as the top-level directory and create the following directory structure:

└── resources
    └── cljsjs
        └── <library-name>
            └── common

Folder name, Cljsjs artifact name and foreign-lib names should match the librarys name e.g. on npm.

4. Add an externs file for the library

If you aren't familiar with externs, read about what they are and how to create them.

Add your externs file to the following location:

└── resources
    └── cljsjs
        └── <library-name>
            └── common
                └── <library-name>.ext.js

Note: In addition to your externs file, any additional assets (css, images) that ship with the JS library should be added to the common directory.

5. Add build.boot and README.md files

Consult existing library packages for examples. Files can be copied from existing packages to get started.

5.1 build.boot

This file will describe how the package can be built and should reside in the library's top-level directory.

All builds should minimally contain the following:

  • Version vars:
(def +lib-version+ "x.y.z")
(def +version+ (str +lib-version+ "-0"))

The names of vars should be these, as our build script uses simple regex to parse versions from build.boot files. The lib version should be exactly the same as version used by the library. You can use +lib-version+ var to construct the URL to be downloaded. Cljsjs version (used for Clojars) is based on lib version with a build identifier appended.

If the library depends on other JS libs packaged on Cljsjs, you should also declare the dependencies.

5.2 README.md

This file should reside in the library's top-level directory and contain the following:

  • the package's name and a placeholder for the version number.
  • any specific instructions for its usage

6. Writing the package task

After running the package task in any CLJSJS package, the fileset contents should be as follows. You can check these by using show --fileset task or by writing fileset contents to a directory using target task. So the command boot package show --fileset will output something like:

├── cljsjs
│   └── <library-name>
│       ├── common
│       │   └── <library-name>.ext.js
│       ├── development
│       │   ├── <library-name>.inc.js
│       │   └── <library-name>.inc.css
│       └── production
│           ├── <library-name>.min.inc.js
│       │   └── <library-name>.min.inc.css
└── deps.cljs

File details:

  • *.inc.js and *.min.inc.js were copied from a downloaded file
  • *.inc.css and *.min.inc.css were copied from a downloaded file
  • If only minified file is available, you can only include that for JS/CSS. If no minified file is available, you can add just the normal file or create minified file using minify task. If only one file is available, you could add it to common directory, or either of specific directories.
  • *.ext.js was copied from your resources directory
  • deps.cljs was generated by boot-cljsjs deps-cljs task or copied from resource-paths

Thus, the package task basically needs to be a program doing the following:

  • download your library from a URL, and unzip if needed
  • move downloaded files to proper path
  • optionally minify files, if no minified files are available upstream
  • generate the deps.cljs
  • call validate task (this will validate checksums, deps.cljs file and that the JS files can be parsed by Google Closure)

deps-cljs only knows how to generate deps.cljs file for simple single file libraries. If you need something else, consult for example following examples:

validate-checksums can check file checksums against boot-cljsjs-checksums.edn file in the working directory. Task will automatically ask the user if the file should be updated if the checksums don't match with the current checksum file. Checksum validation ensures that CI will download exactly the same files as intended. (And also forces the user to run package task locally, to validate it works.)

7. Install your package locally and try it out

You test the package by locally installing the package to your local Maven repository with the following command:

boot package install target

You can now use the package by depending on it on your project. To include the library in build artifact, you need to require provided namespaces from one of Cljs namespaces, e.g. (:require cljsjs.react). To check that it is working as excpected, try accessing the variables defined by the library (e.g. js/React). To test the externs, you need to compile your app using :advanced optimizations.

8. Submit a Pull Request

That's it! Thank you for making the effort! Please do open issues if you have any suggestions how to improve the packaging process or have other ideas you'd like to discuss.

(optional) Update CODEOWNERS file

If you are prepared to provide support (i.e. prepared to provide updates when package updates or users ask for them) for the package, you can add the package folder and your Github handle to CODEOWNERS -file.