# `NPM`
[🔗](https://github.com/elixir-volt/npm_ex/blob/v0.7.4/lib/npm.ex#L1)

npm package manager for Elixir.

Resolves, fetches, and installs npm packages using Mix tasks.
Dependencies are declared in `package.json` and locked in `npm.lock`.

## Mix tasks

    mix npm.install              # Install all deps from package.json
    mix npm.install lodash       # Add latest version
    mix npm.install lodash@^4.0  # Add with specific range
    mix npm.install --frozen     # Fail if lockfile is stale (CI mode)
    mix npm.get                  # Fetch locked deps without resolving
    mix npm.remove lodash        # Remove a package
    mix npm.list                 # List installed packages

Packages are cached globally in `~/.npm_ex/cache/` and linked into
`node_modules/` via symlinks (macOS/Linux) or copies (Windows).

# `add`

```elixir
@spec add(String.t(), String.t(), keyword()) :: :ok | {:error, term()}
```

Add a package to `package.json` and install all dependencies.

## Options

  * `:dev` - when `true`, adds to `devDependencies` instead of `dependencies`

# `get`

```elixir
@spec get() :: :ok | {:error, term()}
```

Fetch locked dependencies without re-resolving.

Reads `npm.lock` and populates the global cache and `node_modules/`
for any missing packages.

# `install`

```elixir
@spec install(keyword()) :: :ok | {:error, term()}
```

Install all dependencies from `package.json` (project context).

## Options

  * `:frozen` - when `true`, fails if `npm.lock` doesn't match
    `package.json` instead of re-resolving. Useful for CI.
  * `:production` - when `true`, skips `devDependencies`.

# `install`

```elixir
@spec install(
  map(),
  keyword()
) :: :ok
```

Install npm packages in a script context, without a Mix project.

Works like `Mix.install/2` — installs to a content-addressed cache directory,
is idempotent, and can only be called once per VM (raises on different deps).

    NPM.install(%{"tailwindcss" => "^4.2.2"})

After installation, use `NPM.install_dir!/0` and `NPM.node_modules_dir!/0`
to locate the installed packages.

## Options

  * `:force` — reinstall even if cached (default: `false`)

# `install_dir!`

```elixir
@spec install_dir!() :: String.t()
```

Returns the root directory of the current `NPM.install/2` installation.

Raises if `NPM.install/2` has not been called.

# `installed?`

```elixir
@spec installed?() :: boolean()
```

Returns whether `NPM.install/2` has been called in this VM.

# `list`

```elixir
@spec list() :: {:ok, [{String.t(), String.t()}]} | {:error, term()}
```

List installed packages with versions.

Returns a list of `{name, version}` tuples.

# `node_modules_dir!`

```elixir
@spec node_modules_dir!() :: String.t()
```

Returns the `node_modules` path of the current `NPM.install/2` installation.

Raises if `NPM.install/2` has not been called.

# `remove`

```elixir
@spec remove(String.t()) :: :ok | {:error, term()}
```

Remove a package from `package.json` and re-install.

# `update`

```elixir
@spec update() :: :ok | {:error, term()}
```

Update all packages to the latest versions matching their ranges.

Clears the resolver cache and re-resolves from scratch.

# `update`

```elixir
@spec update(String.t()) :: :ok | {:error, term()}
```

Update a specific package to the latest version matching its range.

Only re-resolves the named package; other locked versions are preserved.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
