Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add documentation for the Bun and pnpm package managers. #98

Closed
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
FROM ruby:2-alpine
RUN apk upgrade && apk add make
FROM ruby:3-alpine
RUN apk upgrade && apk add make
20 changes: 15 additions & 5 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
GEM
remote: https://rubygems.org/
specs:
asciidoctor (1.5.6.1)
asciidoctor-diagram (1.5.7)
asciidoctor (~> 1.5.0)
coderay (1.1.2)
asciidoctor (2.0.23)
asciidoctor-diagram (2.3.1)
asciidoctor (>= 1.5.7, < 3.x)
asciidoctor-diagram-ditaamini (~> 1.0)
asciidoctor-diagram-plantuml (~> 1.2021)
rexml
asciidoctor-diagram-batik (1.17)
asciidoctor-diagram-ditaamini (1.0.3)
asciidoctor-diagram-plantuml (1.2024.5)
asciidoctor-diagram-batik (~> 1.17)
coderay (1.1.3)
rexml (3.3.4)
strscan
strscan (3.1.0)

PLATFORMS
ruby
@@ -15,4 +25,4 @@ DEPENDENCIES
coderay

BUNDLED WITH
2.1.4
2.5.17
122 changes: 100 additions & 22 deletions docs/UsersGuide.adoc
Original file line number Diff line number Diff line change
@@ -96,38 +96,87 @@ Code examples may be similarly shortened.

= Installation

== Standalone via `npm`
== Standalone via the Node.js package [[install_standalone]]

You will need:

- https://nodejs.org[node.js] (v6.0.0+, most recent version preferred)
- https://www.npmjs.com[npm] (comes with `node` by default) or https://www.yarnpkg.com[yarn]
- Any Java SDK (Version 11 or higher, LTS release recommended). https://adoptium.net/
- https://nodejs.org[Node.js] or https://bun.sh[Bun]
- https://www.npmjs.com[npm] (comes with Node.js), https://bun.sh[bun] (comes with Bun), https://pnpm.io/[pnpm], or https://www.yarnpkg.com[yarn]
- Any Java SDK (Version 11 or higher, LTS release recommended). https://adoptium.net

In your project directory you'll need a `package.json`. If you do not have one yet you can create one by running `npm init -y`. If you don't have a project directory yet consider creating it by running
In your project directory you'll need a `package.json`. If you do not have one yet you can create one by running

.npm
```bash
$ npm init -y
```

.Bun
```bash
$ bun init -y
```

.pnpm
```bash
$ pnpm init
```

.Yarn
```bash
$ yarn init -y
```

If you don't have a project directory yet consider creating it by running

.npm
```bash
$ npx create-cljs-project my-project
```

.Bun
```bash
$ bunx create-cljs-project my-project
```

.pnpm
```bash
$ pnpx create-cljs-project my-project
```

.Yarn
```bash
$ yarn create cljs-project my-project
```

This will create all the necessary basic files and you can skip the following commands.

If you have a `package.json` already and just want to add `shadow-cljs` run

.NPM
.npm
```bash
$ npm install --save-dev shadow-cljs
```

.Bun
```bash
$ bun add --dev shadow-cljs
```

.pnpm
```bash
$ pnpm add --save-dev shadow-cljs
```

.Yarn
```bash
$ yarn add --dev shadow-cljs
```

For convenience, you can run `npm install -g shadow-cljs` or `yarn global add shadow-cljs`. This will let you run the `shadow-cljs` command directly later. There should always be a shadow-cljs version installed in your project, the global install is optional.
For convenience, you can run `npm install -g shadow-cljs`, `bun add -g shadow-cljs`, `pnpm add -g shadow-cljs`, or `yarn global add shadow-cljs`. This will let you run the `shadow-cljs` command directly later. There should always be a shadow-cljs version installed in your project, the global install is optional.

== Library

Although it is recommended to run the standalone version via `npm` you can also embed `shadow-cljs` into any other Clojure JVM tool (eg. `lein`, `boot`, ...).
Although it is recommended to run the standalone version via the Node.js package you can also embed `shadow-cljs` into any other Clojure JVM tool (eg. `lein`, `boot`, ...).

The artifact can be found at:

@@ -223,28 +272,58 @@ Notice that the source path is *only* specified once in the entire configuration

=== JavaScript [[npm-install]]

`shadow-cljs` integrates fully with the https://www.npmjs.com/[`npm`] ecosystem to manage JavaScript dependencies.
`shadow-cljs` integrates fully with the https://www.npmjs.com/[Node.js package] ecosystem to manage JavaScript dependencies.

You can use `npm` or `yarn` to manage your dependencies, please refer to their respective documentation.
You can use `npm`, `bun`, `pnpm` or `yarn` to manage your dependencies, please refer to their respective documentation.

[horizontal]
npm:: https://docs.npmjs.com/
yarn:: https://yarnpkg.com/en/docs
npm:: https://docs.npmjs.com
Bun:: https://bun.sh/docs
pnpm:: https://pnpm.io/motivation
Yarn:: https://yarnpkg.com/en/docs

Both manage your dependencies via a `package.json` file in your project directory. Almost every package available via `npm` will explain how to install it. Those instructions now apply to `shadow-cljs` as well.

.Installing a JavaScript package
.npm
```bash
# npm
$ npm install the-thing
```

.Bun
```bash
$ bun add the-thing
```

# yarn
.pnpm
```bash
$ pnpm add the-thing
```

.Yarn
```bash
$ yarn add the-thing
```

Nothing more is required. Dependencies will be added to the `package.json` file and this will be used to manage them.

TIP: If you don’t have a `package.json` yet run `npm init` from a command line.
TIP: If you don’t have a `package.json` yet see <<install_standalone>>.

`shadow-cljs` will try to use the same Node.js package manager to install Node.js packages specified in the `deps.cljs` files of dependencies in the classpath. It will try to determine which one to use by looking for the corresponding lock file for each package manager.

You can override this guessing behavior with the `:node-modules` key in the `shadow-cljs.edn` file.

```
{:node-modules
{; Override the guessed Node.js package manager.
;
; Not needed if you specify :install-cmd.
:managed-by
:bun

; Override the guessed install command for the Node.js package manager.
:install-cmd
["bun" "add" "--exact"]}}
```

==== Missing JS Dependency?

@@ -551,23 +630,22 @@ include::target-esm.adoc[]

include::target-react-native.adoc[]

= Targeting node.js [[target-node]]
= Targeting Node.js [[target-node]]

There is built-in support for generating code that is intended to be used as a stand-alone
script, and also for code that is intended to be used as a library. See the
section on <<config,common configuration>> for the base settings needed in
a configuration file.

== node.js Scripts [[target-node-script]]
== Node.js Scripts [[target-node-script]]

include::target-node-script.adoc[]

== node.js Libraries [[target-node-library]]
== Node.js Libraries [[target-node-library]]

include::target-node-library.adoc[]


== Creating `npm` packages
== Creating Node.js packages

// TODO: Thomas: I think it would be useful to show a package.json and a little bit of an example
// on how you could set up to deploy this on NPM.
@@ -595,7 +673,7 @@ The default `:ns-regexp` is `"-test$"`, so your first test could look like:

In the Clojure world it is common to keep test files in their own source paths so the above example assumes you have configured `:source-paths ["src/main" "src/test"]` in your `shadow-cljs.edn` config. Your usual app code goes into `src/main` and the tests go into `src/test`. This however is optional and it is totally fine to keep everything in `src` and just use `:source-paths ["src"]`.

== Testing in node.js [[target-node-test]]
== Testing in Node.js [[target-node-test]]

include::target-node-test.adoc[]

736 changes: 434 additions & 302 deletions docs/UsersGuide.html

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions docs/build-config.adoc
Original file line number Diff line number Diff line change
@@ -23,12 +23,12 @@ Here are some of them:

[Horizontal]
<<target-browser, `:browser`>>:: Output code suitable for running in a web browser.
<<target-bootstrap, `:bootstrap`>>:: Output code suitable for running in bootstrapped cljs environment.
<<target-bootstrap, `:bootstrap`>>:: Output code suitable for running in bootstrapped CLJS environment.
<<target-browser-test, `:browser-test`>>:: Scan for tests to determine required files, and output tests suitable for running in the browser.
<<target-karma, `:karma`>>:: Scan for tests to determine required files, and output karma-runner compatible tests. See http://karma-runner.github.io/2.0/index.html[Karma].
<<target-node-library, `:node-library`>>:: Output code suitable for use as a node library.
<<target-node-script, `:node-script`>>:: Output code suitable for use as a node script.
<<target-npm-module, `:npm-module`>>:: Output code suitable for use as an NPM module.
<<target-node-library, `:node-library`>>:: Output code suitable for use as a Node.js library.
<<target-node-script, `:node-script`>>:: Output code suitable for use as a Node.js script.
<<target-npm-module, `:npm-module`>>:: Output code suitable for use as a Node.js module.

Each target is covered in more detail in its own chapter since the remaining build options vary on
the target you select.
@@ -468,7 +468,7 @@ By default the generated JS output will be compatible with ES6 and all "newer" f

You can select other output options if you only care about more modern environments and want to keep the original code without replacements (eg. `node`, Chrome Extensions, ...)

IMPORTANT: Note that this mostly affects imported JS code from <<npm, npm>> or `.js` files from the <<classpath-js, classpath>>. CLJS will currently only generate ES5 output and is not affected by setting higher options.
IMPORTANT: Note that this mostly affects imported JS code from <<npm, Node.js packages>> or `.js` files from the <<classpath-js, classpath>>. CLJS will currently only generate ES5 output and is not affected by setting higher options.

You can configure this via the `:output-feature-set` in `:compiler-options`. The older `:language-out` option should not be used as `:output-feature-set` replaced it.

@@ -507,7 +507,7 @@ CAUTION: This feature only works in `shadow-cljs`. It was officially https://dev

`shadow-cljs` lets you configure additional reader features in `.cljc` files. By default you can only use reader conditionals to generate separate code for `:clj`, `:cljs` or `:cljr`. In many CLJS builds however it is also desirable to select which code is generated based on your `:target`.

Example: Some `npm` packages only work when targeting the `:browser`, but you may have a `ns` that you also want to use in a `:node-script` build. This might happen frequently when trying to use Server-Side Rendering (SSR) with your React App. `codemirror` is one such package.
Example: Some Node.js packages only work when targeting the `:browser`, but you may have a `ns` that you also want to use in a `:node-script` build. This might happen frequently when trying to use Server-Side Rendering (SSR) with your React App. `codemirror` is one such package.

```clojure
(ns my.awesome.component
10 changes: 5 additions & 5 deletions docs/intro.adoc
Original file line number Diff line number Diff line change
@@ -7,16 +7,16 @@
`shadow-cljs` is composed of 2 parts:

- The https://clojars.org/thheller/shadow-cljs[shadow-cljs] Clojure library which handles all the actual work.
- The https://www.npmjs.com/package/shadow-cljs[shadow-cljs] `npm` package which provides a convenient interface for running most of the build functionality directly from command line.
- The https://www.npmjs.com/package/shadow-cljs[shadow-cljs] Node.js package which provides a convenient interface for running most of the build functionality directly from command line.

If desired you can easily integrate the `shadow-cljs` Clojure library into any other Clojure/JVM build tool (eg. https://leiningen.org/[leiningen] or the https://clojure.org/guides/deps_and_cli[Clojure CLI] tools).

It is recommended to use the `npm` package as that provides a more optimized development experience tailored towards CLJS development.
It is recommended to use the Node.js package as that provides a more optimized development experience tailored towards CLJS development.


== Basic Workflow

When working with `shadow-cljs` you will be defining one or more builds in the `shadow-cljs.edn` configuration file. Each build will have a `:target` property which represents a configuration preset optimized for the target environment (eg. the Browser, a `node.js` application or a Chrome Extension).
When working with `shadow-cljs` you will be defining one or more builds in the `shadow-cljs.edn` configuration file. Each build will have a `:target` property which represents a configuration preset optimized for the target environment (eg. the Browser, a Node.js application or a Chrome Extension).

Each build can either produce development or release output depending on the command used to trigger the compilation. The standard build commands are: `compile`, `watch` and `release`.

@@ -46,11 +46,11 @@ There are several important concepts that you should familiarize yourself with w

In the Clojure(Script) everything is namespaced and each name is expected to resolve to a file. If you have a `(ns demo.app)` namespace the compiler expects to find a `demo/app.cljs` (or `.cljc`) on the classpath. The classpath will be searched in order until it is found. Suppose you configured the `:source-paths ["src/main" "src/test"]` the compiler will first look for a `src/main/demo/app.cljs` and then `src/test/demo/app.cljs`. When the file is not found on any source path the JVM will begin looking into the `.jar` files on the classpath. When it finds a `demo/app.cljs` at the root of any of the libraries that file it will be used.

IMPORTANT: When a filename exists multiple times on the classpath then only the first one is used. Everything on the JVM and Clojure(Script) is namespaced to avoid such conflicts. Very similar to `npm` where each package must have a unique name.
IMPORTANT: When a filename exists multiple times on the classpath then only the first one is used. Everything on the JVM and Clojure(Script) is namespaced to avoid such conflicts. Very similar to Node.js where each package must have a unique name.

It is therefore recommended to be very disciplined about the names you choose and about properly namespacing everything. It may seem repetitive to always use `(ns your-company.components.foo)` over `(ns components.foo)` but it will save you from lot of headaches later on.

This is unlike `npm` where the package name itself is never used inside the package itself and only relative paths are used.
This is unlike Node.js where the package name itself is never used inside the package itself and only relative paths are used.


=== Server Mode
48 changes: 23 additions & 25 deletions docs/js-deps.adoc
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
== NPM [[npm]]
== Node.js [[npm]]

https://www.npmjs.com/[npm] has become the de-facto standard package manager for JavaScript. Almost all JS libraries can be found there and shadow-cljs provides seamless integration for accessing those packages.
Node.js packages have become the de-facto standard packaging format for JavaScript. Almost all JS libraries can be found at the https://www.npmjs.com[npm public registry] (the default package registry) and shadow-cljs provides seamless integration for accessing those packages.

=== Using npm packages
=== Using Node.js packages

Most npm packages will also include some instructions on how to use the actual code. The “old” CommonJS style just has `require` calls which translate directly:
Most Node.js packages will also include some instructions on how to use the actual code. The “old” CommonJS style just has `require` calls which translate directly:

```js
var react = require("react");
@@ -157,7 +157,7 @@ Since printing arbitrary JS objects is not always useful (as seen above) you can

=== Package Provider [[js-provider]]

`shadow-cljs` supports several different ways to include `npm` packages into your build. They are configurable via the `:js-options :js-provider` setting. Each `:target` usually sets the one appropriate for your build most often you won't need to touch this setting.
`shadow-cljs` supports several different ways to include Node.js packages into your build. They are configurable via the `:js-options :js-provider` setting. Each `:target` usually sets the one appropriate for your build most often you won't need to touch this setting.

Currently there are 3 supported JS Providers:

@@ -169,7 +169,7 @@ Currently there are 3 supported JS Providers:

.`:shadow` vs `:closure`
****
Ideally we want to use `:closure` as our primary JS Provider since that will run the entire application through `:advanced` giving us the most optimized output. In practice however lots of code available via `npm` is not compatible with the aggressive optimizations that `:advanced` compilation does. They either fail to compile at all or expose subtle bugs at runtime that are very hard to identify.
Ideally we want to use `:closure` as our primary JS Provider since that will run the entire application through `:advanced` giving us the most optimized output. In practice however lots of code available via Node.js packages are not compatible with the aggressive optimizations that `:advanced` compilation does. They either fail to compile at all or expose subtle bugs at runtime that are very hard to identify.
`:shadow` is sort of a stopgap solution that only processes code via `:simple` and achieves much more reliable support while still getting reasonably optimized code. The output is comparable (or often better) to what other tools like `webpack` generate.
@@ -190,7 +190,7 @@ Until support in Closure gets more reliable `:shadow` is the recommend JS Provid

=== CommonJS vs ESM [[js-entry-keys]]

Nowadays many `npm` packages ship multiple build variants. `shadow-cljs` will by default pick the variant linked under the `main` or `browser` key in `package.json`. This most commonly refers to CommonJS code. Some modern packages also provide a `module` entry which usually refers to ECMAScript code (meaning "modern" JS). Interop between CommonJS and ESM can be tricky so `shadow-cljs` defaults to using CommonJS but it can be beneficial to use ESM.
Nowadays many Node.js packages ship multiple build variants. `shadow-cljs` will by default pick the variant linked under the `main` or `browser` key in `package.json`. This most commonly refers to CommonJS code. Some modern packages also provide a `module` entry which usually refers to ECMAScript code (meaning "modern" JS). Interop between CommonJS and ESM can be tricky so `shadow-cljs` defaults to using CommonJS but it can be beneficial to use ESM.

It is largely dependent on the packages you use whether this will work or not. You can configure `shadow-cljs` to prefer the `module` entry via the `:entry-keys` JS option. It takes a vector of string keys found in `package.json` which will be tried in order. The default is `"["browser" "main" "module"]`.

@@ -209,7 +209,7 @@ Make sure to test thoroughly and compare the <<build-report, build report>> outp

=== Resolving Packages [[js-resolve]]

By default `shadow-cljs` will resolve all `(:require ["thing" :as x])` requires following the `npm` convention. This means it will look at `<project>/node_modules/thing/package.json` and follow the code from there. To customize how this works `shadow-cljs` exposes a `:resolve` config option that lets you override how things are resolved.
By default `shadow-cljs` will resolve all `(:require ["thing" :as x])` requires following the Node.js convention. This means it will look at `<project>/node_modules/thing/package.json` and follow the code from there. To customize how this works `shadow-cljs` exposes a `:resolve` config option that lets you override how things are resolved.

==== Using a CDN [[js-resolve-global]]

@@ -232,11 +232,11 @@ Here is a sample `shadow-cljs.edn` config for such a build:
...}}}
```

The `:app` build will now use the global `React` instance while the `:server` build continues using the "react" npm package! No need to fiddle with the code to make this work.
The `:app` build will now use the global `React` instance while the `:server` build continues using the "react" Node.js package! No need to fiddle with the code to make this work.

==== Redirecting “require” [[js-resolve-npm]]

Sometimes you want more control over which `npm` package is actually used depending on your build. You can "redirect" certain requires from your build config without changing the code. This is often useful if you either don't have access to the sources using such packages or you just want to change it for one build.
Sometimes you want more control over which Node.js package is actually used depending on your build. You can "redirect" certain requires from your build config without changing the code. This is often useful if you either don't have access to the sources using such packages or you just want to change it for one build.

```
{...
@@ -318,15 +318,15 @@ Relative paths will be resolved relative to the project root directory. Paths wi
CLJS has an alternate https://clojurescript.org/guides/javascript-modules[implementation] which in turn is not supported by `shadow-cljs`. I found this implementation to be lacking in certain aspects so I opted for the different solution. Happy to discuss the pros/cons of both approaches though.
****

We covered how <<npm, npm>> packages are used but you may be working on a codebase that already has lots of plain JavaScript and you don't want to rewrite everything in ClojureScript just yet. `shadow-cljs` provides 100% full interop between JavaScript and ClojureScript. Which means your JS can use your CLJS and CLJS can use your JS.
We covered how Node.js packages are used but you may be working on a codebase that already has lots of plain JavaScript and you don't want to rewrite everything in ClojureScript just yet. `shadow-cljs` provides 100% full interop between JavaScript and ClojureScript. Which means your JS can use your CLJS and CLJS can use your JS.

There are only a few conventions you need to follow in order for this to work reliably but chances are that you are already doing that anyways.



=== Requiring JS

We already covered how `npm` packages are accessed by their name but on the classpath we access `.js` files by either a full path or relative to the current namespace.
We already covered how Node.js packages are accessed by their name but on the classpath we access `.js` files by either a full path or relative to the current namespace.

.Loading JS from the classpath
```clojure
@@ -360,19 +360,19 @@ IMPORTANT: The files must not be physically located in the same directory. The l

=== Language Support

IMPORTANT: It is expected that the classpath only contains JavaScript that can be consumed without any pre-processing by the Compiler. `npm` has a very similar convention.
IMPORTANT: It is expected that the classpath only contains JavaScript that can be consumed without any pre-processing by the Compiler. Node.js has a very similar convention.

The Closure Compiler is used for processing all JavaScript found on the classpath using its `ECMASCRIPT_NEXT` language setting. What exactly this setting means is not well documented but it mostly represents the next generation JavaScript code which might not even be supported by most browsers yet. ES6 is very well supported as well as most ES8 features. Similarly to standard CLJS this will be compiled down to ES5 with polyfills when required.

Since the Closure Compiler is getting constant updates newer features will be available over time. Just don't expect to use the latest cutting edge preview features to be available immediately. Somewhat recent additions like `async/await` already work quite well.

The JS should be written using ES Module Syntax using `import` and `export`. JS files can include other JS files and reference CLJS code directly. They may also access `npm` packages directly with one caveat.
The JS should be written using ES Module Syntax using `import` and `export`. JS files can include other JS files and reference CLJS code directly. They may also access Node.js packages directly with one caveat.

```js
// regular JS require
import Foo, { something } from "./other.js";

// npm require
// Node.js require
import React from "react";

// require CLJS or Closure Library JS
@@ -383,7 +383,7 @@ export function inc(num) {
}
```

IMPORTANT: Due to strict checking of the Closure Compiler it is not possible to use the `import * as X from "npm";` syntax when requiring CLJS or npm code. It is fine to use when requiring other JS files.
IMPORTANT: Due to strict checking of the Closure Compiler it is not possible to use the `import * as X from "npm";` syntax when requiring CLJS or Node.js code. It is fine to use when requiring other JS files.

=== JavaScript Dialects

@@ -463,9 +463,9 @@ TIP: The `goog:` prefix currently only works for ES6 file. `require("goog:cljs.c

> CLJSJS is an effort to package Javascript libraries to be able to use them from within ClojureScript.

Since `shadow-cljs` can access <<npm, npm packages>> directly we do not need to rely on re-packaged https://github.com/cljsjs/packages[CLJSJS] packages.
Since `shadow-cljs` can access <<npm, Node.js packages>> directly we do not need to rely on re-packaged https://github.com/cljsjs/packages[CLJSJS] packages.

However many CLJS libraries are still using CLJSJS packages and they would break with `shadow-cljs` since it doesn't support those anymore. It is however very easy to mimick those `cljsjs` namespaces since they are mostly build from `npm` packages anyways. It just requires one shim file that maps the `cljsjs.thing` back to its original `npm` package and exposes the expected global variable.
However many CLJS libraries are still using CLJSJS packages and they would break with `shadow-cljs` since it doesn't support those anymore. It is however very easy to mimick those `cljsjs` namespaces since they are mostly build from Node.js packages anyways. It just requires one shim file that maps the `cljsjs.thing` back to its original Node.js package and exposes the expected global variable.

For React this requires a file like `src/cljsjs/react.cljs`:

@@ -484,20 +484,18 @@ Since this would be tedious for everyone to do manually I created the https://gi
library which provides just that. It does not include every package but I’ll keep adding
them and contributions are very welcome as well.

NOTE: The `shadow-cljsjs` library only provides the shim files. You’ll still need to
`npm install` the actual packages yourself.

NOTE: The `shadow-cljsjs` library only provides the shim files. You’ll still need to <<npm-install, install>> the actual packages yourself.

=== Why not use CLJSJS?

CLJSJS packages basically just take the package from `npm` and put them into a `.jar` and re-publish them via https://clojars.org[clojars]. As a bonus they often bundle Externs. The compiler otherwise does nothing with these files and only prepends them to the generated output.
CLJSJS packages basically just take the package from the <<npm, npm public registry>> and put them into a `.jar` and re-publish them via https://clojars.org[clojars]. As a bonus they often bundle Externs. The compiler otherwise does nothing with these files and only prepends them to the generated output.

This was very useful when we had no access to `npm` directly but has certain issues since not all packages are easily combined with others. A package might rely on `react` but instead of expressing this via `npm` https://github.com/cljsjs/packages/tree/master/material-ui[they] bundle their own `react`. If you are not careful you could end up including 2 different `react` versions in your build which may lead to very confusing errors or at the very least increase the build size substantially.
This was very useful when we had no access to the <<npm, npm public registry>> directly but has certain issues since not all packages are easily combined with others. A package might rely on `react` but instead of expressing this via `package.json` https://github.com/cljsjs/packages/tree/master/material-ui[they] bundle their own `react`. If you are not careful you could end up including 2 different `react` versions in your build which may lead to very confusing errors or at the very least increase the build size substantially.

Apart from that not every `npm` package is available via CLJSJS and keeping the package versions in sync requires manual work, which means packages are often out of date.
Apart from that not every Node.js package is available via CLJSJS and keeping the package versions in sync requires manual work, which means packages are often out of date.

`shadow-cljs` does not support CLJSJS at all to avoid conflicts in your code. One library might attempt to use the "old" `cljsjs.react` while another uses the newer `(:require ["react"])` directly. This would again lead to 2 versions of `react` on your page again.

So the only thing we are missing are the bundled Externs. In many instances these are not required due to improved <<infer-externs, externs inference>>. Often those Externs are generated using third-party tools which means they are not totally accurate anyways.

Conclusion: Use <<npm, npm>> directly. Use <<infer-externs, :infer-externs auto>>.
Conclusion: Use <<npm, Node.js packages>> directly. Use <<infer-externs, :infer-externs auto>>.
4 changes: 2 additions & 2 deletions docs/maven-publish.adoc
Original file line number Diff line number Diff line change
@@ -55,9 +55,9 @@ IMPORTANT: You can and should verify that everything is clean by running `lein j

## Declaring JS dependencies [[publish-deps-cljs]]

Please note that currently only `shadow-cljs` has a clean automatic interop story with `npm`. That may represent a problem for users of your libraries using other tools. You may want to consider providing a CLJSJS fallback and/or publishing extra documentation for `webpack` related workflows.
Please note that currently only `shadow-cljs` has a clean automatic interop story with Node.js. That may represent a problem for users of your libraries using other tools. You may want to consider providing a CLJSJS fallback and/or publishing extra documentation for `webpack` related workflows.

You can declare `npm` dependencies directly by including a `deps.cljs` with `:npm-deps` in your project (eg. `src/main/deps.cljs`).
You can declare Node.js dependencies directly by including a `deps.cljs` with `:npm-deps` in your project (eg. `src/main/deps.cljs`).

.Example src/main/deps.cljs
```clojure
2 changes: 1 addition & 1 deletion docs/repl.adoc
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ When you quickly want to test out some code the built-in REPLs should be enough.

== ClojureScript REPL

By default you can choose between a `node-repl` and a `browser-repl`. They both work similarly and the differentiating factor is that one runs in a managed `node.js` process while the others opens a Browser Window that will be used to eval the actual code.
By default you can choose between a `node-repl` and a `browser-repl`. They both work similarly and the differentiating factor is that one runs in a managed Node.js process while the others opens a browser window that will be used to eval the actual code.

=== Node REPL [[node-repl]]

10 changes: 5 additions & 5 deletions docs/target-browser.adoc
Original file line number Diff line number Diff line change
@@ -603,12 +603,12 @@ IMPORTANT: The requests must be forwarded to the main <<http, HTTP server>>, not

== Using External JS Bundlers [[js-provider-external]]

Sometimes npm packages you may wish to use may use features that `shadow-cljs` itself does not support. Some packages are even written with the explicit expectation to be processed by `webpack`. In these cases it might be simpler to just use `webpack` (or similar), instead of letting `shadow-cljs` try to bundle those packages and working arround the issues that may occur.
Sometimes Node.js packages you may wish to use may use features that `shadow-cljs` itself does not support. Some packages are even written with the explicit expectation to be processed by `webpack`. In these cases it might be simpler to just use `webpack` (or similar), instead of letting `shadow-cljs` try to bundle those packages and working arround the issues that may occur.

`shadow-cljs` supports an option to let it focus on compiling CLJS code, but let something else process the npm/JS requires. You can do so via `:js-provider :external`. I wrote more on this subject https://code.thheller.com/blog/shadow-cljs/2020/05/08/how-about-webpack-now.html#option-2-js-provider-external:[in this blogpost].
`shadow-cljs` supports an option to let it focus on compiling CLJS code, but let something else process the Node.js/JS requires. You can do so via `:js-provider :external`. I wrote more on this subject https://code.thheller.com/blog/shadow-cljs/2020/05/08/how-about-webpack-now.html#option-2-js-provider-external:[in this blogpost].


IMPORTANT: This will limit certain dynamic interaction. Adding new npm requires will require reloading the page, since they can no longer be hot-loaded in by shadow-cljs. Requiring npm packages at the REPL will also be limited to those already provided by the external JS file. Which often is not a big deal, but something to be aware of.
IMPORTANT: This will limit certain dynamic interaction. Adding new Node.js requires will require reloading the page, since they can no longer be hot-loaded in by shadow-cljs. Requiring Node.js packages at the REPL will also be limited to those already provided by the external JS file. Which often is not a big deal, but something to be aware of.

In your build config you add:

@@ -623,7 +623,7 @@ In your build config you add:
:external-index "target/index.js"}}}}
```

`shadow-cljs` will then just output all required `npm` package requires in a format that regular JS tools can understand. You'll then need to run `webpack` (or similar) manually, and include the output of that build separately from the `shadow-cljs` output.
`shadow-cljs` will then just output all required Node.js package requires in a format that regular JS tools can understand. You'll then need to run `webpack` (or similar) manually, and include the output of that build separately from the `shadow-cljs` output.

So, instead of just including one `script` tag in your HTML, you include two.

@@ -638,7 +638,7 @@ IMPORTANT: Note that `webpack` (or similar) sometimes output more than one file,

=== JS Tree Shaking

Tools like `webpack` can potentially tree-shake npm dependencies to make their build output smaller. For this the `:external-index` file needs to generate ESM code, instead of the current default CommonJS, i.e. `require()`.
Tools like `webpack` can potentially tree-shake Node.js dependencies to make their build output smaller. For this the `:external-index` file needs to generate ESM code, instead of the current default CommonJS, i.e. `require()`.

```clojure
{:builds
2 changes: 1 addition & 1 deletion docs/target-esm.adoc
Original file line number Diff line number Diff line change
@@ -215,4 +215,4 @@ This is done by setting `:js-provider` in your build config.
:modules {:demo {:exports {default demo.lib/hello}}}}}}
```

For this build shadow-cljs will only compile and bundle CLJS code, but leave all other JS code to be provided by some other tool later. Note that if you have `(:require ["react"])` or any other `npm` dependency in your build the output from `shadow-cljs` MUST be processed by another tool first before it becomes loadable in the Browser. Only set this if some other tool is actually going to provide the required dependencies.
For this build shadow-cljs will only compile and bundle CLJS code, but leave all other JS code to be provided by some other tool later. Note that if you have `(:require ["react"])` or any other Node.js package dependency in your build the output from `shadow-cljs` MUST be processed by another tool first before it becomes loadable in the Browser. Only set this if some other tool is actually going to provide the required dependencies.
2 changes: 1 addition & 1 deletion docs/target-npm-module.adoc
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ create-react-app, ...) with little configuration.

With a JS file sitting in your project root, you may `require("./out/demo.foo")` to load the CLJS namespace and access it from JS. The JS requires must be the relative path from the JS file location to the CLJS output.

If you plan to distribute code on NPM, then you may want to use the <<target-node-library, `:node-library` target>> instead since it allows for a finer level of control over exports and optimization.
If you plan to distribute code on the <<npm, npm public registry>>, then you may want to use the <<target-node-library, `:node-library` target>> instead since it allows for a finer level of control over exports and optimization.

== Working with Optimizations

22 changes: 18 additions & 4 deletions docs/usage.adoc
Original file line number Diff line number Diff line change
@@ -15,16 +15,30 @@ If <<Installation, installed>> globally, you can use the `shadow-cljs` command d
$ shadow-cljs help
```

If you prefer to only use the local `npm` install you can invoke it via `npx` or `yarn`.
If you prefer to only use the local Node.js package install you can invoke it via `npx` (comes with Node.js), `bunx` (comes with Bun), `pnpx`, or `yarn`.

.npm
```bash
# npm
$ npx shadow-cljs help
```

.Bun
```bash
$ bunx shadow-cljs help
```

.pnpm
```bash
$ pnpm shadow-cljs help
```

# yarn
.Yarn
```bash
$ yarn shadow-cljs help
```

# manually
.Manually
```bash
$ ./node_modules/.bin/shadow-cljs help
```