Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
abafa2d
feat: allow an array of configs in openapi-ts.config
carson2222 Sep 4, 2025
bf3af31
chore: replace unconfig to forked updated c12
carson2222 Sep 5, 2025
d3c2666
feat: multiple input support
carson2222 Sep 8, 2025
a49c62b
feat: multiple output support
carson2222 Sep 10, 2025
c1eeb67
chore: update @hey-api/json-schema-ref-parser to version 1.2.0
carson2222 Sep 10, 2025
1450461
chore: replace c12 dependency with @hey-api/c12 version 1.0.3
carson2222 Sep 11, 2025
e877adf
Merge branch 'main' into feat--multiple-configs-functionality
carson2222 Sep 11, 2025
9135044
chore: test formatting update
carson2222 Sep 11, 2025
68e0a10
chore: update tests, docs and address reviews
carson2222 Sep 11, 2025
bf91298
chore: build fix
carson2222 Sep 11, 2025
c84f10e
chore: add changeset
carson2222 Sep 11, 2025
1e0df71
refactor: streamline output path handling and enhance multi-output co…
carson2222 Sep 18, 2025
0ff1680
chore: bump c12 back to unjs original vesrion, update error handling …
carson2222 Sep 19, 2025
383756c
Merge branch 'main' of https://github.com/hey-api/openapi-ts into fea…
carson2222 Sep 22, 2025
86b5ba8
fix: update UserConfigMultiOutputs to use UserOutput type for output …
carson2222 Sep 22, 2025
e1202c8
chore: test update after placement api
carson2222 Sep 22, 2025
c1b2a8c
Merge branch 'main' into feat--multiple-configs-functionality
mrlubos Sep 22, 2025
597a298
Merge branch 'main' of https://github.com/hey-api/openapi-ts into fea…
mrlubos Sep 22, 2025
4ebe34c
Merge branch 'main' into feat--multiple-configs-functionality
mrlubos Sep 23, 2025
71588cb
refactor: clean up some types and diff
mrlubos Sep 23, 2025
e34fbf9
Merge branch 'main' into feat--multiple-configs-functionality
mrlubos Sep 23, 2025
b296d50
Merge branch 'main' of https://github.com/hey-api/openapi-ts into fea…
mrlubos Oct 2, 2025
41900bd
Merge branch 'main' into feat--multiple-configs-functionality
mrlubos Oct 3, 2025
4bb0b94
feat: clean up multi-config matrix
mrlubos Oct 4, 2025
d22a26e
chore: upgrade turborepo
mrlubos Oct 5, 2025
eb7a6fd
test: update types
mrlubos Oct 5, 2025
d20b54f
chore: improve logs
mrlubos Oct 5, 2025
e35dc0b
test: update tests
mrlubos Oct 6, 2025
458441d
docs: add advanced section to configuration
mrlubos Oct 6, 2025
c71e0e8
chore: remove unused specs
mrlubos Oct 6, 2025
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
5 changes: 5 additions & 0 deletions .changeset/dry-jeans-ring.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hey-api/openapi-ts': patch
---

feat: support multiple configurations
127 changes: 102 additions & 25 deletions docs/openapi-ts/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,31 +38,6 @@ export default {

Alternatively, you can use `openapi-ts.config.js` and configure the export statement depending on your project setup.

<!--
TODO: uncomment after c12 supports multiple configs
see https://github.com/unjs/c12/issues/92
-->
<!-- ### Multiple Clients

If you want to generate multiple clients with a single `openapi-ts` command, you can provide an array of configuration objects.

```js
import { defineConfig } from '@hey-api/openapi-ts';

export default defineConfig([
{
input: 'path/to/openapi_one.json',
output: 'src/client_one',
plugins: ['legacy/fetch'],
},
{
input: 'path/to/openapi_two.json',
output: 'src/client_two',
plugins: ['legacy/axios'],
},
])
``` -->

## Input

You must provide an input so we can load your OpenAPI specification.
Expand Down Expand Up @@ -163,6 +138,108 @@ Plugins are responsible for generating artifacts from your input. By default, He

You can learn more on the [Output](/openapi-ts/output) page.

## Advanced

More complex configuration scenarios can be handled by providing an array of inputs, outputs, or configurations.

### Multiple jobs

Throughout this documentation, we generally reference single job configurations. However, you can easily run multiple jobs by providing an array of configuration objects.

::: code-group

```js [config]
export default [
{
input: 'foo.yaml',
output: 'src/foo',
},
{
input: 'bar.yaml',
output: 'src/bar',
},
];
```

```md [example]
src/
├── foo/
│ ├── client/
│ ├── core/
│ ├── client.gen.ts
│ ├── index.ts
│ ├── sdk.gen.ts
│ └── types.gen.ts
└── bar/
├── client/
├── core/
├── client.gen.ts
├── index.ts
├── sdk.gen.ts
└── types.gen.ts
```

:::

### Job matrix

Reusing configuration across multiple jobs is possible by defining a job matrix. You can create a job matrix by providing `input` and `output` arrays of matching length.

::: code-group

```js [config]
export default {
input: ['foo.yaml', 'bar.yaml'],
output: ['src/foo', 'src/bar'],
};
```

```md [example]
src/
├── foo/
│ ├── client/
│ ├── core/
│ ├── client.gen.ts
│ ├── index.ts
│ ├── sdk.gen.ts
│ └── types.gen.ts
└── bar/
├── client/
├── core/
├── client.gen.ts
├── index.ts
├── sdk.gen.ts
└── types.gen.ts
```

:::

### Merging inputs

You can merge inputs by defining multiple inputs and a single output.

::: code-group

```js [config]
export default {
input: ['foo.yaml', 'bar.yaml'],
output: 'src/client',
};
```

```md [example]
src/
└── client/
├── client/
├── core/
├── client.gen.ts
├── index.ts
├── sdk.gen.ts
└── types.gen.ts
```

:::

## API

You can view the complete list of options in the [UserConfig](https://github.com/hey-api/openapi-ts/blob/main/packages/openapi-ts/src/types/config.d.ts) interface.
Expand Down
2 changes: 2 additions & 0 deletions docs/openapi-ts/configuration/input.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ export default {

:::

You can learn more about complex use cases in the [Advanced](/openapi-ts/configuration#advanced) section.

::: info
If you use an HTTPS URL with a self-signed certificate in development, you will need to set [`NODE_TLS_REJECT_UNAUTHORIZED=0`](https://github.com/hey-api/openapi-ts/issues/276#issuecomment-2043143501) in your environment.
:::
Expand Down
2 changes: 2 additions & 0 deletions docs/openapi-ts/configuration/output.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export default {

:::

You can learn more about complex use cases in the [Advanced](/openapi-ts/configuration#advanced) section.

## File Name

You can customize the naming and casing pattern for files using the `fileName` option.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"rollup": "4.31.0",
"rollup-plugin-dts": "6.1.1",
"tsup": "8.4.0",
"turbo": "2.5.6",
"turbo": "2.5.8",
"typescript": "5.8.3",
"typescript-eslint": "8.29.1",
"vitest": "3.1.1"
Expand Down
4 changes: 3 additions & 1 deletion packages/nuxt/src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,11 @@ export default defineNuxtModule<ModuleOptions>({
config.watch = false;
}

const output =
config.output instanceof Array ? config.output[0] : config.output;
const folder = path.resolve(
nuxt.options.rootDir,
typeof config.output === 'string' ? config.output : config.output.path,
typeof output === 'string' ? output : (output?.path ?? ''),
);

nuxt.options.alias[options.alias!] = folder;
Expand Down
16 changes: 8 additions & 8 deletions packages/openapi-ts-tests/main/test/2.0.x.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ const version = '2.0.x';
const outputDir = path.join(__dirname, 'generated', version);

describe(`OpenAPI ${version}`, () => {
const createConfig = (userConfig: UserConfig): UserConfig => {
const createConfig = (userConfig: UserConfig) => {
const input =
userConfig.input instanceof Array
? userConfig.input[0]
: userConfig.input;
const inputPath = path.join(
getSpecsPath(),
version,
typeof userConfig.input === 'string'
? userConfig.input
: (userConfig.input.path as string),
typeof input === 'string' ? input : ((input?.path as string) ?? ''),
);
return {
plugins: ['@hey-api/typescript'],
Expand All @@ -40,7 +42,7 @@ describe(`OpenAPI ${version}`, () => {
outputDir,
typeof userConfig.output === 'string' ? userConfig.output : '',
),
};
} as const satisfies UserConfig;
};

const scenarios = [
Expand Down Expand Up @@ -391,9 +393,7 @@ describe(`OpenAPI ${version}`, () => {
it.each(scenarios)('$description', async ({ config }) => {
await createClient(config);

const outputPath =
typeof config.output === 'string' ? config.output : config.output.path;
const filePaths = getFilePaths(outputPath);
const filePaths = getFilePaths(config.output);

await Promise.all(
filePaths.map(async (filePath) => {
Expand Down
16 changes: 8 additions & 8 deletions packages/openapi-ts-tests/main/test/3.0.x.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ const version = '3.0.x';
const outputDir = path.join(__dirname, 'generated', version);

describe(`OpenAPI ${version}`, () => {
const createConfig = (userConfig: UserConfig): UserConfig => {
const createConfig = (userConfig: UserConfig) => {
const input =
userConfig.input instanceof Array
? userConfig.input[0]
: userConfig.input;
const inputPath = path.join(
getSpecsPath(),
version,
typeof userConfig.input === 'string'
? userConfig.input
: (userConfig.input.path as string),
typeof input === 'string' ? input : ((input?.path as string) ?? ''),
);
return {
plugins: ['@hey-api/typescript'],
Expand All @@ -40,7 +42,7 @@ describe(`OpenAPI ${version}`, () => {
outputDir,
typeof userConfig.output === 'string' ? userConfig.output : '',
),
};
} as const satisfies UserConfig;
};

const scenarios = [
Expand Down Expand Up @@ -668,9 +670,7 @@ describe(`OpenAPI ${version}`, () => {
it.each(scenarios)('$description', async ({ config }) => {
await createClient(config);

const outputPath =
typeof config.output === 'string' ? config.output : config.output.path;
const filePaths = getFilePaths(outputPath);
const filePaths = getFilePaths(config.output);

await Promise.all(
filePaths.map(async (filePath) => {
Expand Down
22 changes: 13 additions & 9 deletions packages/openapi-ts-tests/main/test/3.1.x.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,20 @@ const version = '3.1.x';
const outputDir = path.join(__dirname, 'generated', version);

describe(`OpenAPI ${version}`, () => {
const createConfig = (userConfig: UserConfig): UserConfig => {
const createConfig = (userConfig: UserConfig) => {
const input =
userConfig.input instanceof Array
? userConfig.input[0]
: userConfig.input;
const inputPath = path.join(
getSpecsPath(),
version,
typeof userConfig.input === 'string'
? userConfig.input
: (userConfig.input.path as string),
typeof input === 'string' ? input : ((input?.path as string) ?? ''),
);
const output =
userConfig.output instanceof Array
? userConfig.output[0]
: userConfig.output;
return {
plugins: ['@hey-api/typescript'],
...userConfig,
Expand All @@ -38,9 +44,9 @@ describe(`OpenAPI ${version}`, () => {
},
output: path.join(
outputDir,
typeof userConfig.output === 'string' ? userConfig.output : '',
typeof output === 'string' ? output : (output?.path ?? ''),
),
};
} as const satisfies UserConfig;
};

const scenarios = [
Expand Down Expand Up @@ -969,9 +975,7 @@ describe(`OpenAPI ${version}`, () => {
it.each(scenarios)('$description', async ({ config }) => {
await createClient(config);

const outputPath =
typeof config.output === 'string' ? config.output : config.output.path;
const filePaths = getFilePaths(outputPath);
const filePaths = getFilePaths(config.output);

await Promise.all(
filePaths.map(async (filePath) => {
Expand Down
Loading
Loading