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
- Create-react-class, simple single name foreign-lib: https://github.com/cljsjs/packages/blob/master/create-react-class/build.boot#L25-L27
- Highlight.js, multiple names provided: https://github.com/cljsjs/packages/blob/master/highlight/build.boot#L28-L35 (only main name uses global-exports, other names don't export anything)
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