---
title: "Node.js Management"
vignette: >
  %\VignetteIndexEntry{Node.js Management}
  %\VignetteEngine{quarto::html}
  %\VignetteEncoding{UTF-8}
knitr:
  opts_chunk:
    collapse: true
    comment: "#>"
---

```{r}
#| include: false
library(shinyelectron)
```

Packaging a Shiny app into an installer needs Node.js. Most R and Python users do not have Node.js on their machines and do not want to install it system-wide just to ship one app. shinyelectron solves the awkward middle by downloading a copy of Node into your user cache: no admin rights, no conflict with anything else, and you can pin or remove it cleanly later.

If you already have a recent Node.js LTS (22 or higher) on your `PATH`, that works too. shinyelectron uses whichever copy it can find.

## Why a local install

A local install lives in your user cache rather than under `/usr/local` or `C:\Program Files`. Four things follow from that.

| Benefit | What it means |
|---------|---------------|
| **No admin rights** | No `sudo` or Administrator prompts during install |
| **Isolation** | Stays out of the way of any system Node.js or other projects |
| **Pinnability** | Lock to a specific version when reproducibility matters |
| **Portability** | Works on locked-down machines where a system install is not allowed |

The minimum supported version is **Node.js 22.0.0** because Electron 41 requires it. shinyelectron's installer always defaults to the latest LTS, which is well above that floor.

## Installing

`install_nodejs()` fetches the latest LTS build from nodejs.org, verifies the checksum, and unpacks it into the cache.

```{r}
#| eval: false
install_nodejs()
```

```
i Detecting latest Node.js LTS version...
v Latest LTS: v22.11.0
-- Installing Node.js v22.11.0 ----------------------------------------------
i Platform: mac
i Architecture: arm64
i Fetching checksums...
i Downloading from https://nodejs.org/dist/v22.11.0/node-v22.11.0-darwin-arm64.tar.gz
v Checksum verified
i Extracting archive...
v Node.js v22.11.0 installed successfully
i Location: /Users/you/Library/Caches/shinyelectron/assets/nodejs/v22.11.0/darwin-arm64
i shinyelectron will automatically use this installation
```

Two arguments worth knowing:

```{r}
#| eval: false
# Pin a specific version for reproducible builds
install_nodejs(version = "22.11.0")

# Force a fresh download when a previous one is corrupt
install_nodejs(force = TRUE)
```

You can install several versions side by side; they sit in separate directories in the cache.

```{r}
#| eval: false
install_nodejs(version = "22.11.0")
install_nodejs(version = "20.18.0")
```

When more than one is present, **the highest semantic version wins** at resolution time. Install order does not matter.

## Which Node.js shinyelectron uses

Whenever shinyelectron needs to run `node` or `npm`, it picks one in this order:

1. **The local install** under the shinyelectron cache. If several local versions are present, the highest wins.
2. **The system Node.js** found on `PATH`.
3. If neither is available, the call fails with a message naming both options.

<figure><picture><source srcset="../man/figures/nodejs-resolution-dark.svg" media="(prefers-color-scheme: dark)" /><img src="../man/figures/nodejs-resolution.svg" alt="Flow diagram showing export() checking the local shinyelectron cache first, falling back to the system PATH, and erroring if neither Node.js install is available." /></picture><figcaption>Node.js resolution: local cache first, then system PATH, then error.</figcaption></figure>

`sitrep_electron_system()` shows what it finds:

```{r}
#| eval: false
sitrep_electron_system()
```

```
-- System Requirements Report ------------------------------------------------
v Platform: mac
v Architecture: arm64
v Local Node.js (shinyelectron): v22.11.0
  Other versions: 20.18.0
v Active Node.js: v22.11.0 (local)
v npm: v11.5.0
```

## Auto-install from `_shinyelectron.yml` (planned)

::: {.callout-warning}
## Not yet implemented

`auto_install` is reserved for a future release and has no effect today. A missing Node.js aborts the build with a message directing you to run `install_nodejs()` or install Node.js manually from <https://nodejs.org/>.
:::

When implemented, setting `auto_install: true` in `_shinyelectron.yml` will let `export()` fetch Node.js automatically when it cannot find one and then continue the build. For now, call `install_nodejs()` before running `export()`.

```{r}
#| eval: false
install_nodejs()
```

## Cache layout

Everything lives under the shinyelectron user cache, organised by version and then platform/architecture. The base directory comes from `rappdirs::user_cache_dir("shinyelectron")`, so the exact path is platform-specific:

| Platform | Typical cache base |
|----------|--------------------|
| macOS | `~/Library/Caches/shinyelectron/assets/` |
| Linux | `~/.cache/shinyelectron/assets/` |
| Windows | `%LOCALAPPDATA%\shinyelectron\Cache\assets\` |

Call `cache_dir()` to get the actual path on your machine. Inside it, the Node.js layout looks like this:

```
{cache_dir()}/nodejs/
|-- v22.11.0/
|   `-- darwin-arm64/      # macOS Apple Silicon
|       |-- bin/
|       |   |-- node
|       |   |-- npm
|       |   `-- npx
|       |-- include/
|       `-- lib/
|-- v20.18.0/
|   `-- win-x64/           # Windows 64-bit
|       |-- node.exe
|       |-- npm.cmd
|       `-- npx.cmd
`-- ...
```

The platform and architecture come from `Sys.info()` and map to Node.js's own naming:

| Detected (R) | Node.js build |
|--------------|---------------|
| Darwin / arm64 | darwin-arm64 |
| Darwin / x86_64 | darwin-x64 |
| Windows / arm64 | win-arm64 |
| Windows / x86_64 | win-x64 |
| Linux / arm64 | linux-arm64 |
| Linux / x86_64 | linux-x64 |

## Download integrity

Every download is checked against the official SHA256 manifest before it is unpacked:

1. Fetch `SHASUMS256.txt` from nodejs.org.
2. Download the Node.js archive.
3. Compute the SHA256 of the downloaded file.
4. Compare against the expected checksum.
5. Abort on any mismatch; the archive is never extracted.

This catches both ordinary corruption and anything tampered with in transit. If `SHASUMS256.txt` cannot be fetched (a transient network error), shinyelectron warns and continues without verifying rather than failing the install. The portable R and Python runtimes are verified the same way, against their own published checksums.

## Clearing the cache

Remove every Node.js install shinyelectron has downloaded:

```{r}
#| eval: false
cache_clear("nodejs")
```

```
v Cleared Node.js installation cache
```

Remove everything shinyelectron has cached (Node.js, R/Python runtimes, npm modules):

```{r}
#| eval: false
cache_clear("all")
```

Useful when an install is broken, when disk space is tight, or when you want to test a fresh download from scratch.

## Local versus system

| Aspect | Local (shinyelectron) | System |
|--------|------------------------|--------|
| Installs with | `install_nodejs()` | OS package manager or nodejs.org |
| Lives under | `cache_dir()` | `/usr/local`, `C:\Program Files\nodejs` |
| Admin rights | No | Usually yes |
| Updates | `install_nodejs()` | OS package manager |
| Multiple versions | Yes | Usually one |
| Affects other tools | No | Yes |

Prefer the local install for most work. It is isolated, reproducible, and cannot be broken by an OS update. The system install makes sense when your team standardises on a Node version that is already installed everywhere.

## Troubleshooting

### Network error during download

```
x Failed to download Node.js
x URL: https://nodejs.org/dist/v22.11.0/node-v22.11.0-darwin-arm64.tar.gz
x Error: could not resolve host
```

Check your connection. If you are behind a corporate proxy, make sure R sees it (set `HTTPS_PROXY` in your `.Renviron`). If nodejs.org itself is unreachable, retry later.

### Checksum mismatch

```
x Checksum verification failed
x Downloaded file may be corrupted
```

Force a re-download:

```{r}
#| eval: false
install_nodejs(force = TRUE)
```

If the mismatch persists, nodejs.org may be serving a bad mirror. Wait and retry, or pin to a known-good earlier version with `version = "..."`.

### Wrong architecture

Symptom: Node.js runs but Electron fails with an architecture mismatch (for example, `node was compiled for darwin-x64 but the system is darwin-arm64`).

The cached install is the wrong build for your machine. Clear and reinstall:

```{r}
#| eval: false
sitrep_electron_system()
cache_clear("nodejs")
install_nodejs()
```

### Local install skipped

Symptom: shinyelectron keeps reaching for the system Node.js even though `install_nodejs()` reported success.

The local install is probably incomplete. Clear and reinstall, then verify with `sitrep_electron_system()`:

```{r}
#| eval: false
cache_clear("nodejs")
install_nodejs()
sitrep_electron_system()
```

## Next steps

- **[Getting Started](getting-started.html)**: first-time walkthrough.
- **[Configuration](configuration.html)**: full `_shinyelectron.yml` reference.
- **[Troubleshooting](troubleshooting.html)**: diagnose other build issues.
