# CLI Reference

URL: https://docs.forms.saastro.io/docs/installation/cli
> Every command shipped with @saastro/forms — init and install-deps.

# CLI Reference

`@saastro/forms` ships a small CLI that runs through `npx`. It scaffolds
example forms and installs the shadcn/ui components those forms need.

```bash
npx @saastro/forms <command> [options]
```

Run with no arguments to print the inline help.

| Command / option | What it does |
| --- | --- |
| `init` | Scaffold zero-config example forms |
| `install-deps` | Install required shadcn components + peer deps, skipping ones that already exist |
| `--force` | With `install-deps`: reinstall and overwrite components that already exist |

---

## `init`

Scaffold two zero-config example forms in `src/components/`.

```bash
npx @saastro/forms init
```

`init` requires a `components.json` (the shadcn config) in the directory you
run it from — it exits with an error otherwise:

```
❌ No components.json found in the current directory.
   Make sure you run this command from your project root and that shadcn/ui is initialized (npx shadcn@latest init).
```

If you don't have one yet, run `npx shadcn@latest init` first.

**Creates:**

- `src/components/ExampleForm.tsx` — programmatic form built with
  [`FormBuilder`](/docs/formbuilder)
- `src/components/ExampleFormJson.tsx` — same form expressed as JSON config
  (useful as a starting point for storing forms in a database or CMS)

If either file already exists, it is left untouched and the CLI reports
`exists  <path>` instead of `create  <path>`.

After running `init`, you'll be reminded to call:

```bash
npx @saastro/forms install-deps
```

This installs the shadcn components `@saastro/forms` needs (plus any missing
peer dependencies), skipping ones already in your project.

### Use the generated form

```tsx

  return <ExampleForm />;
}
```

The example uses Vite's `import.meta.glob` so every `.tsx` file in
`@/components/ui/` is automatically wired into the form. Add new components
to that folder and they'll be available without any registration step.

> **Vite only:** `import.meta.glob` is a Vite feature — it does not exist in
> Next.js, webpack, or other bundlers. On non-Vite setups, replace the glob in
> the generated files with explicit imports passed via the `components` prop —
> see the [Components guide](/docs/components).

---

## `install-deps`

Read your project's `components.json` (the shadcn config) and install every
shadcn component `@saastro/forms` field types use — the full set (input,
button, calendar, command, …), not just what the example forms reference —
skipping any that are already present.

```bash
npx @saastro/forms install-deps
```

**What it does:**

1. Verifies you're running it inside your own project (not inside the
   `@saastro/forms` package itself).
2. Reads `components.json` to discover the project's path aliases and
   component directory (see [Alias resolution](#alias-resolution) below).
3. Ensures `lib/utils.ts` exists (the shadcn `cn()` helper). Creates it if not.
4. Checks which components already exist in your `ui` directory and skips
   them, logging `✓ <component> skipped (exists)` for each one. Existing
   files are **never overwritten** unless you pass `--force`.
5. Runs `shadcn@latest add <missing-components> --yes` to install whichever
   components are missing, then rewrites their imports to match the aliases
   configured in your `components.json`.
6. If the batch install fails (rare), retries one component at a time so a
   single broken package doesn't block the rest.

**When to run it:**

- After the first `init`.
- Any time you add a field type whose required components you haven't
  installed yet — the runtime fallback also tells you to run this command.

### `--force`

By default, components that already exist in your `ui` directory are skipped
so your local customizations are safe. Pass `--force` to reinstall everything
and let shadcn overwrite the existing files:

```bash
npx @saastro/forms install-deps --force
```

When everything is already installed and you didn't pass `--force`, the CLI
tells you so:

```
✅ All required components are already installed.
   Pass --force to reinstall and overwrite them.
```

### Alias resolution

The CLI reads the `aliases` field of your `components.json` (the shadcn
schema: `aliases.ui`, `aliases.components`, `aliases.utils`, …) and resolves
each alias to a real filesystem path:

1. The alias root (e.g. `@` in `@/components/ui`) is looked up in your
   tsconfig-style path mappings — `compilerOptions.paths` in
   `tsconfig.json`, `tsconfig.app.json`, or `jsconfig.json` (JSONC comments
   and trailing commas are handled). A mapping like
   `"@/*": ["./app/*"]` resolves `@/components/ui` to `app/components/ui`.
2. If no mapping is found, the shadcn/Vite convention applies: the alias
   root maps to `src` (so `@/components/ui` → `src/components/ui`).
3. If `components.json` has no usable `aliases` at all, the CLI falls back
   to the legacy `@/` → `src/` replacement and warns once:

```
⚠ components.json has no usable "aliases" entries. Falling back to the default "@/" → "src/" mapping.
```

Imports inside freshly installed components are rewritten to your configured
aliases (e.g. `../../lib/utils` becomes whatever `aliases.utils` says —
`@/lib/utils`, `~/lib/utils`, …).

### What if I don't have a `components.json`?

`install-deps` skips gracefully (exit code 0) instead of failing — it doubles
as a postinstall hook, so a monorepo root or server-only package shouldn't
error out:

```
ℹ️  No components.json found. Skipping automatic component installation.
   Initialize shadcn/ui first (npx shadcn@latest init), then run:
   npx @saastro/forms install-deps
```

Run `npx shadcn@latest init`, answer the prompts, then re-run `install-deps`.

---

## `add` (removed)

Earlier versions documented an `add <name>` command that copied custom
components bundled inside the package. The package bundles no custom
components, so the command could never succeed and has been removed. Running
it now prints a pointer to the right tools:

```
This package has no bundled components. Use `npx shadcn@latest add <name>` for shadcn components, or `npx @saastro/forms install-deps` to install everything the form needs.
```

To install a single shadcn component, use the shadcn CLI directly:

```bash
npx shadcn@latest add <component-name>
```

To install everything `@saastro/forms` needs, use `install-deps`.

---

## Troubleshooting

The literal CLI output is quoted below so you can match it to what you see in
your terminal.

### "You are inside the @saastro/forms package itself."

You ran `install-deps` from inside the `@saastro/forms` package source instead
of your own project. Switch to your project root (the directory where your
`package.json` lives) and run the command from there.

### "No components.json found"

shadcn isn't initialised in this project yet (this affects both `init` and
`install-deps` — they both read `components.json`). `init` exits with an
error; `install-deps` skips gracefully. Either way, run:

```bash
npx shadcn@latest init
```

…answer the prompts, then re-run `npx @saastro/forms install-deps`.

### "Could not parse components.json"

Your `components.json` exists but isn't valid JSON. Fix the syntax error
mentioned in the message and re-run the command.

### A specific component fails to install

`install-deps` falls back to one-at-a-time installation when the batch run
fails ("⚠️  Batch install failed, retrying one component at a time..."). If a
particular component still fails, install it manually:

```bash
npx shadcn@latest add <component-name>
```

…and re-run `install-deps` to verify everything else is in place.

### A component was skipped but I want a fresh copy

That's the overwrite guard: `✓ <component> skipped (exists)` means the file
is already in your `ui` directory and was left untouched. Re-run with
`--force` to overwrite it (this discards local edits to that file).

---

## Peer dependencies (installed automatically since 0.2.1)

`install-deps` also verifies that the peer dependencies are declared in your
`package.json` and installs any that are missing:

- `zod` — installs `^4` if missing (the published peer range also accepts
  `^3.25` if you already have it)
- `react-hook-form` (`^7.67`)
- `react-day-picker` (`^9.11.3`)
- `date-fns` (`^4.1`)

The check is by presence in your `package.json`, not by version — already
declared dependencies are never touched.

The package manager is detected from your lockfile in this priority order:

1. `pnpm-lock.yaml` → `pnpm add ...`
2. `bun.lock` or `bun.lockb` → `bun add ...`
3. `yarn.lock` → `yarn add ...`
4. `package-lock.json` (or none) → `npm install ...`

`react` and `react-dom` are intentionally not auto-installed — every React
project already has them, and choosing between React 18 and 19 is a
project decision.
