这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions .changeset/beige-rocks-remain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hey-api/openapi-ts': patch
---

fix(plugin): every plugin extends Plugin.Hooks interface
9 changes: 9 additions & 0 deletions .changeset/eleven-beans-sort.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@hey-api/openapi-ts': patch
---

fix(renderer): group and sort imported modules

### TypeScript renderer

We ship a dedicated TypeScript renderer for `.ts` files. This release improves the renderer's ability to group and sort imported modules, resulting in a more polished output.
33 changes: 33 additions & 0 deletions .changeset/healthy-experts-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
'@hey-api/openapi-ts': patch
---

fix(config): add `output.fileName` option

## File Name

You can customize the naming and casing pattern for files using the `fileName` option.

```js
export default {
input: 'hey-api/backend', // sign up at app.heyapi.dev
output: {
fileName: '{{name}}',
path: 'src/client',
},
};
```

By default, we append every file name with a `.gen` suffix to highlight it's automatically generated. You can customize or disable this suffix using the `fileName.suffix` option.

```js
export default {
input: 'hey-api/backend', // sign up at app.heyapi.dev
output: {
fileName: {
suffix: '.gen',
},
path: 'src/client',
},
};
```
5 changes: 5 additions & 0 deletions .changeset/selfish-hornets-beg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hey-api/codegen-core': minor
---

feat: Symbol API
15 changes: 15 additions & 0 deletions .changeset/two-buses-dance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
'@hey-api/openapi-ts': minor
---

feat: Symbol API

### Symbol API

This release improves the Symbol API, which adds the capability to place symbols in arbitrary files. We preserved the previous output structure for all plugins except Angular.

You can preserve the previous Angular output by writing your own [placement function](https://heyapi.dev/openapi-ts/configuration/parser#hooks-symbols).

### Removed `output` plugin option

Due to the Symbol API release, this option has been removed from the Plugin API.
2 changes: 1 addition & 1 deletion docs/.vitepress/config/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ export default defineConfig({
text: 'Custom',
},
],
text: 'Guides and Concepts',
text: 'Plugins',
},
{
items: [
Expand Down
72 changes: 72 additions & 0 deletions docs/openapi-ts/configuration/output.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,78 @@ export default {

:::

## File Name

You can customize the naming and casing pattern for files using the `fileName` option.

::: code-group

```js [default]
export default {
input: 'hey-api/backend', // sign up at app.heyapi.dev
output: {
fileName: '{{name}}', // [!code ++]
path: 'src/client',
},
};
```

```js [snake_case]
export default {
input: 'hey-api/backend', // sign up at app.heyapi.dev
output: {
fileName: {
case: 'snake_case', // [!code ++]
},
path: 'src/client',
},
};
```

:::

By default, we append every file name with a `.gen` suffix to highlight it's automatically generated. You can customize or disable this suffix using the `fileName.suffix` option.

::: code-group

```js [default]
export default {
input: 'hey-api/backend', // sign up at app.heyapi.dev
output: {
fileName: {
suffix: '.gen', // [!code ++]
},
path: 'src/client',
},
};
```

```js [off]
export default {
input: 'hey-api/backend', // sign up at app.heyapi.dev
output: {
fileName: {
suffix: null, // [!code ++]
},
path: 'src/client',
},
};
```

```js [custom]
export default {
input: 'hey-api/backend', // sign up at app.heyapi.dev
output: {
fileName: {
suffix: '.generated', // [!code ++]
},
path: 'src/client',
},
};
```

:::

## Format

To format your output folder contents, set `output.format` to a valid formatter.
Expand Down
86 changes: 71 additions & 15 deletions docs/openapi-ts/configuration/parser.md
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,36 @@ export default {

## Hooks

Hooks affect runtime behavior but aren’t tied to any single plugin. They can be configured globally via `parser.hooks` or per plugin through the `~hooks` property.
Hooks affect runtime behavior but aren’t tied to any single plugin. They can be configured globally via `hooks` or per plugin through the `~hooks` property.

::: code-group

```js [parser]
export default {
input: 'hey-api/backend', // sign up at app.heyapi.dev
output: 'src/client',
parser: {
hooks: {}, // configure global hooks here // [!code ++]
},
};
```

```js [plugin]
export default {
input: 'hey-api/backend', // sign up at app.heyapi.dev
output: 'src/client',
plugins: [
{
name: '@tanstack/react-query',
'~hooks': {}, // configure plugin hooks here // [!code ++]
},
],
};
```

:::

We always use the first hook that returns a value. If a hook returns no value, we fall back to less specific hooks until one does.

### Operations {#hooks-operations}

Expand All @@ -478,12 +507,12 @@ By default, DELETE, PATCH, POST, and PUT operations are classified as `mutation`

Imagine your API has a POST `/search` endpoint that accepts a large payload. By default, it's classified as a `mutation`, but in practice it behaves like a `query`, and your [state management](/openapi-ts/state-management) plugin should generate query hooks.

You can fix this by classifying the operation as `query` in a matcher. If a matcher returns no value, we fall back to less specific matchers until one does.
You can achieve this by classifying the operation as `query` in a matcher.

::: code-group

<!-- prettier-ignore-start -->
```js [parser]
```js [isQuery]
export default {
input: 'hey-api/backend', // sign up at app.heyapi.dev
output: 'src/client',
Expand All @@ -502,29 +531,56 @@ export default {
```
<!-- prettier-ignore-end -->
<!-- prettier-ignore-start -->
```js [plugin]
```js [getKind]
export default {
input: 'hey-api/backend', // sign up at app.heyapi.dev
output: 'src/client',
plugins: [
{
name: '@tanstack/react-query',
'~hooks': {
operations: {
getKind: (op) => {
if (op.method === 'post' && op.path === '/search') { // [!code ++]
return ['query']; // [!code ++]
} // [!code ++]
},
parser: {
hooks: {
operations: {
getKind: (op) => {
if (op.method === 'post' && op.path === '/search') { // [!code ++]
return ['query']; // [!code ++]
} // [!code ++]
},
},
},
],
},
};
```
<!-- prettier-ignore-end -->

:::

### Symbols {#hooks-symbols}

Each symbol can have a placement function deciding its output location.

#### Example: Alphabetic sort

While we work on a better example, let's imagine a world where it's desirable to place every symbol in a file named after its initial letter. For example, a function named `Foo` should end up in the file `f.ts`.

You can achieve this by using the symbol's name.

<!-- prettier-ignore-start -->
```js [getKind]
export default {
input: 'hey-api/backend', // sign up at app.heyapi.dev
output: 'src/client',
parser: {
hooks: {
symbols: {
getFilePath: (symbol) => {
if (symbol.name) { // [!code ++]
return symbol.name[0]?.toLowerCase(); // [!code ++]
} // [!code ++]
},
},
},
},
};
```
<!-- prettier-ignore-end -->

<!--@include: ../../partials/examples.md-->
<!--@include: ../../partials/sponsors.md-->
2 changes: 1 addition & 1 deletion docs/openapi-ts/get-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Launch demo

- runs in CLI, Node.js 18+, or npx
- works with OpenAPI 2.0, 3.0, and 3.1
- customizable types and SDKs
- core plugins for types, SDKs, and schemas
- clients for your runtime (Fetch API, Angular, Axios, Next.js, Nuxt, etc.)
- plugin ecosystem to reduce third-party boilerplate
- custom plugins and custom clients
Expand Down
16 changes: 16 additions & 0 deletions docs/openapi-ts/migrating.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ description: Migrating to @hey-api/openapi-ts.

While we try to avoid breaking changes, sometimes it's unavoidable in order to offer you the latest features. This page lists changes that require updates to your code. If you run into a problem with migration, please [open an issue](https://github.com/hey-api/openapi-ts/issues).

## v0.84.0

### Symbol API

This release improves the Symbol API, which adds the capability to place symbols in arbitrary files. We preserved the previous output structure for all plugins except Angular.

You can preserve the previous Angular output by writing your own [placement function](/openapi-ts/configuration/parser#hooks-symbols).

### TypeScript renderer

We ship a dedicated TypeScript renderer for `.ts` files. This release improves the renderer's ability to group and sort imported modules, resulting in a more polished output.

### Removed `output` plugin option

Due to the Symbol API release, this option has been removed from the Plugin API.

## v0.83.0

### Symbol API
Expand Down
2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@
"vitepress-plugin-llms": "1.7.3",
"vue": "3.5.13"
},
"packageManager": "pnpm@10.15.0"
"packageManager": "pnpm@10.17.0"
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,5 @@
"typescript-eslint": "8.29.1",
"vitest": "3.1.1"
},
"packageManager": "pnpm@10.15.1"
"packageManager": "pnpm@10.17.0"
}
66 changes: 66 additions & 0 deletions packages/codegen-core/src/__tests__/bimap.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { describe, expect, it } from 'vitest';

import { BiMap } from '../bimap/bimap';

describe('BiMap', () => {
it('covers the full public interface', () => {
const bimap = new BiMap<number, string>();
// set and get
expect(bimap.set(1, 'a')).toBe(bimap);
expect(bimap.set(2, 'b')).toBe(bimap);
// get, getKey
expect(bimap.get(1)).toBe('a');
expect(bimap.get(2)).toBe('b');
expect(bimap.getKey('a')).toBe(1);
expect(bimap.getKey('b')).toBe(2);
// hasKey, hasValue
expect(bimap.hasKey(1)).toBe(true);
expect(bimap.hasKey(2)).toBe(true);
expect(bimap.hasKey(3)).toBe(false);
expect(bimap.hasValue('a')).toBe(true);
expect(bimap.hasValue('b')).toBe(true);
expect(bimap.hasValue('c')).toBe(false);
// keys, values, entries
expect(Array.from(bimap.keys())).toEqual([1, 2]);
expect(Array.from(bimap.values())).toEqual(['a', 'b']);
expect(Array.from(bimap.entries())).toEqual([
[1, 'a'],
[2, 'b'],
]);
// Symbol.iterator
expect(Array.from(bimap)).toEqual([
[1, 'a'],
[2, 'b'],
]);
// size
expect(bimap.size).toBe(2);
// delete by key
expect(bimap.delete(1)).toBe(true);
expect(bimap.hasKey(1)).toBe(false);
expect(bimap.hasValue('a')).toBe(false);
// delete by value
expect(bimap.deleteValue('b')).toBe(true);
expect(bimap.hasKey(2)).toBe(false);
expect(bimap.hasValue('b')).toBe(false);
// After all deletes
expect(bimap.size).toBe(0);
// Setting again to check overwrite
bimap.set(1, 'x');
bimap.set(2, 'y');
expect(bimap.get(1)).toBe('x');
expect(bimap.get(2)).toBe('y');
// Overwrite value for existing key
bimap.set(1, 'z');
expect(bimap.get(1)).toBe('z');
expect(bimap.getKey('z')).toBe(1);
// Overwrite key for existing value
bimap.set(3, 'z');
expect(bimap.getKey('z')).toBe(3);
expect(bimap.get(1)).toBeUndefined();
// Iteration after overwrite
expect(Array.from(bimap)).toEqual([
[2, 'y'],
[3, 'z'],
]);
});
});
Loading
Loading