Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
6 changes: 6 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 7 additions & 4 deletions docs/content/docs/guides/hono.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,13 @@ export const listPetsHandlers = factory.createHandlers(

Add your business logic to the generated handlers:

> Re-running orval refreshes the file header, imports, and `factory` line so
> changes to your config (paths, validators, etc.) take effect — but the body
> of each `factory.createHandlers(...)` call is preserved verbatim, so your
> business logic is safe across regeneration.
> Re-running orval reconciles your handler files according to
> [`override.hono.handlerGenerationStrategy`](/docs/reference/configuration/output#handlergenerationstrategy)
> (default `smart`). With `smart`, orval only updates the parts it owns — its own
> imports (names, paths, casing) and the `zValidator(...)` arguments, and appends
> handlers for new operations — while preserving your custom imports, middleware,
> handler bodies, and top-level helpers. Use `skip` to freeze a file, or `full`
> for the legacy behavior that rebuilds the wrapper and keeps only the body.

```ts title="src/handlers/listPets.ts"
export const listPetsHandlers = factory.createHandlers(
Expand Down
36 changes: 36 additions & 0 deletions docs/content/docs/reference/configuration/output.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1400,6 +1400,7 @@ export default defineConfig({
override: {
hono: {
handlers: 'src/handlers',
handlerGenerationStrategy: 'smart',
validatorOutputPath: 'src/validator.ts',
compositeRoute: 'src/routes.ts',
},
Expand All @@ -1415,6 +1416,41 @@ export default defineConfig({

Changes output path for Hono handlers.

### handlerGenerationStrategy

**Type:** `'smart' | 'skip' | 'full'`

**Default:** `'smart'`

Controls how an **existing** handler file is treated when you re-run orval. A
file that does not exist yet is always generated fresh.

- `smart` (default) — non-destructively reconcile only the parts orval owns: its
own imports (names, module paths, casing) and the `zValidator(...)` arguments,
and append handlers for new operations. Your custom imports, middleware,
handler bodies, and top-level helpers are preserved. Requires the optional
`typescript` peer dependency (see note below); if it is absent, smart falls
back to `skip` with a warning.
- `skip` — leave an existing handler file byte-for-byte unchanged. New operations
still get fresh files (in `split` mode).
- `full` — rebuild the file header, imports, and validator chain from the spec,
splicing back only each handler body. **Destructive:** custom imports,
middleware, and top-level helpers are dropped. Use only if you keep handlers
minimal and want maximal sync with the spec.

<Callout type="info">
`smart` and `full` use the TypeScript compiler API to parse existing handler
files. `typescript` is an optional peer dependency — virtually every orval
project already has it, so nothing extra is installed.
</Callout>

<Callout type="warn">
If `output.clean` is enabled and the handlers directory lives under the output
target directory, handler files are deleted before generation runs, which
defeats `smart`/`skip` preservation. Disable `clean` (or scope it) when
relying on handler preservation.
</Callout>

### validatorOutputPath

**Type:** `String`
Expand Down
7 changes: 6 additions & 1 deletion packages/core/src/test-utils/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,12 @@ export function createTestContextSpec({
parameters: { suffix: '' },
requestBodies: { suffix: '' },
},
hono: { compositeRoute: '', validator: false, validatorOutputPath: '' },
hono: {
handlerGenerationStrategy: 'smart',
compositeRoute: '',
validator: false,
validatorOutputPath: '',
},
query: {
useQuery: false,
useSuspenseQuery: false,
Expand Down
15 changes: 15 additions & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -698,8 +698,22 @@ export interface OverrideOutputContentType {
exclude?: string[];
}

/**
* Strategy controlling how an existing hono handler file is treated on
* regeneration.
*
* - `smart` (default): non-destructively reconcile orval-owned imports and
* `zValidator` arguments and append handlers for new operations, preserving
* all user-authored imports, middleware, bodies, and top-level code.
* - `skip`: leave an existing handler file byte-for-byte unchanged.
* - `full`: rebuild the preamble and validator chain from the spec, splicing
* back only the handler body. Drops custom imports/middleware/helpers.
*/
export type HonoHandlerStrategy = 'smart' | 'skip' | 'full';

export interface NormalizedHonoOptions {
handlers?: string;
handlerGenerationStrategy: HonoHandlerStrategy;
compositeRoute: string;
validator: boolean | 'hono';
validatorOutputPath: string;
Expand Down Expand Up @@ -856,6 +870,7 @@ export type MutationInvalidatesConfig = MutationInvalidatesRule[];

export interface HonoOptions {
handlers?: string;
handlerGenerationStrategy?: HonoHandlerStrategy;
compositeRoute?: string;
validator?: boolean | 'hono';
validatorOutputPath?: string;
Expand Down
8 changes: 8 additions & 0 deletions packages/hono/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@
"fs-extra": "^11.3.2",
"remeda": "^2.33.6"
},
"peerDependencies": {
"typescript": ">=5"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
},
"devDependencies": {
"@hono/zod-validator": "catalog:hono",
"@types/fs-extra": "^11.0.4",
Expand Down
Loading
Loading