Npm style names and global exports - cljsjs/packages GitHub Wiki

ClojureScript now supports using Node Modules. Unfortunately if a library uses existing Cljsjs packages, and requires cljsjs.react or similar foreign-lib, and accesses JS globals like js/React, it is not possible to use Node Modules with the library.

Naming foreign-libs

When using Node Modules, e.g. React is referred just by the npm package name: (:require [react]). If libraries start to use this, the library won't work with (old) Cljsjs packages.

Solution: Cljsjs packages can provide a foreign-lib matching the npm name:

{:file "cljsjs/react/development/react.inc.js"
 :provides ["react"]
 :file-min "cljsjs/react/production/react.min.inc.js"}

The foreign-lib entry can also provide the old cljsjs.* name, for compatibility with old code: https://github.com/cljsjs/packages/blob/master/react/resources/react-deps.cljs

  :provides ["react" "cljsjs.react"]

When other foreign-libs (like react-dom) depend on this package, it is best to depend on the new name: :requires ["react"]. ClojureScript should only include a JS file once in the build, even if the same file is accessed by several names (e.g. both react and cljsjs.react are required).

Global-exports

Being able to require foreign libs with the same name alone is not enough, as JS libraries have been accessed through global JS objects, and Node Package code is accessed through alias, similar to ClojureScript namespaces or Closure JS code:

(ns example
  (:require [react :as react]))

(react/createElement ...)

This is why :global-exports was created. Using this option, the foreign-lib can create pseudo namespace and define which global it should refer to. Cljs compiler can then generate proper JS code when the foreign-lib is used like above.

{:file "cljsjs/react/development/react.inc.js"
 :provides ["react"]
 :global-exports {react React}
 :file-min "cljsjs/react/production/react.min.inc.js"}

In this case, react namespace will be mapped to React JS object.

Importance for Cljs libraries

Using these npm-style names is important when creating Cljsjs packages you are going to use from Cljs libraries. For example Reagent and Re-frame-10x. Using npm-style names ensures the library users are able to use the library both with Cljsjs packages, and Node packages using both ClojureScript Node package support and Shadow-CLJS.

Examples

Notes

  • It is possible changes in ClojureScript will allow skipping some of these changes: https://dev.clojure.org/jira/browse/CLJS-2331
  • Currently, I think it best that if package uses non-prefixed requires, like react, it should also provide foreign-lib without the prefix