# @theorchard/openapi-typegen-cli

A CLI tool to generate TypeScript types from OpenAPI schemas. It wraps
[`openapi-typescript`](https://github.com/openapi-ts/openapi-typescript) with a
config-file workflow and an interactive terminal UI, so you can keep several
schemas in one place and regenerate their types with a single command.

## Requirements

-   Node.js >= 23

## Install

```bash
pnpm add -D @theorchard/openapi-typegen-cli
```

This exposes the `openapi-typegen-cli` binary.

## Quick start

1. Create a config file (interactive):

    ```bash
    pnpm openapi-typegen-cli init
    ```

2. Generate types from it:

    ```bash
    pnpm openapi-typegen-cli generate
    ```

## Commands

```
openapi-typegen-cli                  Interactive launcher (pick a command)
openapi-typegen-cli generate [opts]  Generate types (interactive when in a TTY)
openapi-typegen-cli generate --yes   Non-interactive generate (for CI)
openapi-typegen-cli init             Create a config file (interactive)
openapi-typegen-cli --help           Show help
```

### Options

| Option            | Description                                                        |
| ----------------- | ------------------------------------------------------------------ |
| `--config <path>` | Config file path to use (default: `openapi-typescript.config.ts`). |
| `--yes`           | Skip all prompts and use the existing config(s) as-is.             |

### `init`

Walks you through creating an `openapi-typescript.config.ts` file:

1. Asks where generated types should live (an output directory, default
   `generated`; leave empty to write next to the config).
2. Asks for one or more schema URLs. After each URL it suggests an output file
   name derived from the schema host (for example `petstore.d.ts`); press enter
   to finish adding entries.
3. Writes the config. A single entry produces an object export; multiple
   entries produce an array export.

`init` requires an interactive terminal. If a config already exists it asks
before overwriting.

### `generate`

1. Finds config file(s) (see [Config resolution](#config-resolution)).
2. If multiple config entries are found in an interactive terminal, lets you
   pick which ones to generate via a multi-select list.
3. Fetches each schema, generates types, and writes them to the configured
   output file.

In a TTY this runs as an interactive UI. With `--yes` or when output is not a
TTY (such as CI), it runs non-interactively and generates every entry. The
process exits with code `1` if any generation fails.

## Configuration

The config file is a TypeScript module (`openapi-typescript.config.ts` by
default) that default-exports an object with a `sources` array. Each source is
one schema to generate types for.

```ts
import type { OpenAPITypescriptConfig } from '@theorchard/openapi-typegen-cli';

export default {
    sources: [
        {
            schemaUrl: 'https://api.example.com/openapi.json',
            outputFile: 'generated/example.d.ts',
        },
    ],
} satisfies OpenAPITypescriptConfig;
```

Multiple schemas:

```ts
import type { OpenAPITypescriptConfig } from '@theorchard/openapi-typegen-cli';

export default {
    sources: [
        {
            schemaUrl: 'https://api.example.com/openapi.json',
            outputFile: 'generated/example.d.ts',
        },
        {
            schemaUrl: 'https://petstore.swagger.io/v2/swagger.json',
            outputFile: 'generated/petstore.d.ts',
        },
    ],
} satisfies OpenAPITypescriptConfig;
```

Global options with a per-source override:

```ts
import type { OpenAPITypescriptConfig } from '@theorchard/openapi-typegen-cli';

export default {
    options: { alphabetize: true }, // applied to every source
    sources: [
        {
            schemaUrl: 'https://api.example.com/openapi.json',
            outputFile: 'generated/example.d.ts',
        },
        {
            schemaUrl: 'https://petstore.swagger.io/v2/swagger.json',
            outputFile: 'generated/petstore.d.ts',
            options: { alphabetize: false }, // overrides global for this source
        },
    ],
} satisfies OpenAPITypescriptConfig;
```

Environment variables are supported in config files. The CLI loads `.env` from
the current working directory before importing the config:

```ts
export default {
    sources: [
        {
            schemaUrl: process.env.API_URL!,
            outputFile: 'generated/api.d.ts',
        },
    ],
} satisfies OpenAPITypescriptConfig;
```

### Config fields

**Root object (`OpenAPITypescriptConfig`)**

| Field     | Type                        | Required | Description                              |
| --------- | --------------------------- | -------- | ---------------------------------------- |
| `sources` | `OpenAPITypescriptSource[]` | yes      | List of schemas to generate types for.   |
| `options` | `OpenAPITSOptions`          | no       | Default options applied to every source. |

**Source entry (`OpenAPITypescriptSource`)**

| Field        | Type               | Required | Description                                                                                |
| ------------ | ------------------ | -------- | ------------------------------------------------------------------------------------------ |
| `schemaUrl`  | `string`           | yes      | URL or path to the OpenAPI schema.                                                         |
| `outputFile` | `string`           | no       | Output path for the generated `.d.ts`, relative to the config file. Default `schema.d.ts`. |
| `options`    | `OpenAPITSOptions` | no       | Overrides the root `options` for this source (shallow merge, source keys win).             |

Generated files are prefixed with a banner noting they are auto-generated and
should not be edited by hand. Output directories are created automatically.

### Config resolution

`--config` is a single file path. With no flag, the tool loads
`openapi-typescript.config.ts` from the current directory. Pass a path (for
example `--config configs/api.config.ts`) to load that exact file instead. Each
entry in `sources` becomes a generation target.

## Development

Run all commands from this package directory (or with
`pnpm --filter @theorchard/openapi-typegen-cli <script>` from the monorepo
root):

```bash
pnpm build        # compile to dist/ with tsc
pnpm dev          # run the CLI from source with tsx
pnpm test:unit    # run the test suite with vitest
pnpm lint         # eslint
pnpm format       # prettier --write
pnpm format:check # prettier --check
pnpm test         # lint + unit tests
```

The build uses the shared `tsconfig.esm.json`

### Running the tool locally

`pnpm dev` runs the CLI entry (`src/bin.cts`) through `tsx`, so there is no
build step while iterating. Pass CLI arguments straight after it:

```bash
pnpm dev                       # interactive launcher
pnpm dev --help                # show usage
pnpm dev init                  # config wizard
pnpm dev generate              # interactive generate
pnpm dev generate --yes        # non-interactive generate (CI path)
pnpm dev generate --config configs/api.config.ts --yes
```

To try a real generate run, create a config in the package root (it is
gitignored, so it stays a local scratch file):

```ts
// openapi-typescript.config.ts
export default {
    schemaUrl: 'https://petstore3.swagger.io/api/v3/openapi.json',
    outputFile: 'generated/petstore.d.ts',
};
```

Then run `pnpm dev generate --yes` and inspect the file written under
`generated/` (also gitignored).

### Testing the built binary

To exercise the actual bin entry instead of the source:

```bash
pnpm build
node dist/bin.cjs generate --yes
```

Note: the interactive UI needs a real TTY. When stdin/stdout are not a terminal
(CI, piped output), `generate` runs non-interactively and `init` exits with an
error, so use `--yes` for scripted generate runs.
