Skip to content

fix(hono): refresh handler preamble and fix import paths for hono.handlers option - #3292

Merged
melloware merged 7 commits into
orval-labs:masterfrom
zeriong:fix/hono-handlers-imports-and-overwrite-2989
May 6, 2026
Merged

fix(hono): refresh handler preamble and fix import paths for hono.handlers option#3292
melloware merged 7 commits into
orval-labs:masterfrom
zeriong:fix/hono-handlers-imports-and-overwrite-2989

Conversation

@zeriong

@zeriong zeriong commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #2989.

Two bugs caused generated Hono handler files to drift out of sync with the rest of the output:

  1. When override.hono.handlers was set, the zod/context module paths used the tags-split layout (dirname/tag/tag.zod) for every non-tags mode. In split/single modes the generated imports therefore pointed at ../<tag>/<tag>.context, while generateContextFiles actually emitted ../<filename>.context. getHonoHeader had the symmetrical mistake for tags mode, joining a tag sub-directory that does not exist on disk.
  2. Once a handler file existed, the generator skipped it entirely and only appended missing handlers. Stale imports, wrong casing, or paths left over from earlier configurations were never corrected — exactly the behavior reported in the issue.

This PR splits path resolution by mode (tags / tags-split / split-or-single) so it mirrors where generateZodFiles and generateContextFiles actually write, and rewrites the existing-file branch to always regenerate the file header, imports, and const factory = createFactory(); preamble while preserving each factory.createHandlers(...) body verbatim via a paren-aware extractor. New handlers are appended at the end as before.

Changes

  • packages/hono/src/index.ts: per-mode zodModule / contextModule resolution inside the override.hono.handlers branch; corrected getHonoHeader per-tag join (only tags-split joins a sub-directory); generateHandlerFile now refreshes preamble and preserves bodies via a new exported extractExistingHandlers helper.
  • packages/hono/src/index.test.ts: unit tests for extractExistingHandlers (multi-handler files, nested parens in user-edited bodies, empty input).
  • tests/configs/hono.config.ts: three new regression fixtures exercising override.hono.handlers with mode: 'split', mode: 'tags', and mode: 'tags-split'.
  • docs/content/docs/guides/hono.mdx: brief note that re-running orval refreshes the preamble while preserving handler bodies.

Test plan

  • bun vitest run — new extractExistingHandlers tests pass.
  • bun run test:snapshots — 3776 snapshots pass, including the three new petstore-*-with-handlers fixtures.
  • bun run lint and bun run typecheck — clean.
  • tests/scripts/typecheck-generated.mjs — all 15 generated clients (including the new hono fixtures) typecheck.
  • Manual scenario: edit a generated handler body and a stale import path, regenerate with clean: false. Imports get refreshed; the user-edited body is preserved verbatim.

Summary by CodeRabbit

  • Bug Fixes

    • Handler import paths now resolve correctly for tag-based generation; regeneration refreshes headers/imports/factory while keeping user-edited handler bodies.
  • New Features

    • Tooling preserves existing handler implementation bodies across regenerations and exposes extraction support to enable that preservation.
    • Response-validation middleware now short-circuits when a hook supplies a Response, preserving hook-provided outputs.
  • Tests

    • Added tests to verify extraction and preservation of existing handler bodies.
  • Documentation

    • Clarified which parts of generated files are refreshed vs. preserved.

@coderabbitai

coderabbitai Bot commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Regenerates Hono handler preamble (header/imports/factory/validator wiring) while preserving existing user-authored handler bodies by parsing and splicing them back in; fixes tags-split import resolution; exports extractExistingHandlerBodies and adds parsing utilities and tests; also short-circuits zValidator response-hook when the hook returns a Response.

Changes

Hono handler regeneration (preamble refresh + body preservation)

Layer / File(s) Summary
Data / Shape
packages/hono/src/index.ts
Introduce DEFAULT_HANDLER_BODY and per-handler bodyOverride to accept spliced-in bodies during generation.
Core Implementation
packages/hono/src/index.ts
Add/export extractExistingHandlerBodies(source: string): Map<string,string> and parsing helpers (findMatchingClose, skipString, skipRegex, isRegexContext, extractAsyncArrowBody) to extract inner bodies from existing handler wrappers.
Generation Flow / Wiring
packages/hono/src/index.ts
Refactor generateHandlerFile / generateHandlerFiles to always rebuild preamble (header/imports/factory/validators) and splice in extracted bodies via bodyOverride; compute zodModule/contextModule per mode including tags-split; compute per-operation handler paths for override.hono.handlers.
Tests
packages/hono/src/index.test.ts
Add Vitest tests verifying extractExistingHandlerBodies extracts handler bodies, preserves nested syntax, handles strings/templates/comments/regex delimiters, and returns empty map when absent.
Docs / Config
docs/content/docs/guides/hono.mdx, tests/configs/hono.config.ts
Document regeneration behavior in Handler Template note; add test configs for split, tags, and tags-split with override.hono.handlers directories.
Snapshots / Samples
tests/__snapshots__/hono/**, samples/hono/**
Add/refresh many generated snapshots (contexts, schemas, zod validators, validators, handlers, apps) reflecting new naming/import layout and handler directories.

Response-hook short-circuit (zValidator)

Layer / File(s) Summary
Core Logic
packages/hono/src/zValidator.ts
When a response-validation hook returns a Response or { response: Response }, set c.res and return immediately to avoid later rewriting of the hook-provided response.
Samples / Snapshots
samples/**/petstore.validator.ts, tests/__snapshots__/hono/**/endpoints.validator.ts
Apply matching early-return behavior in generated/sample validator snapshots so hook-returned Responses are honored.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Orval as Orval Generator
    participant FS as File System
    participant Parser as extractExistingHandlerBodies
    participant Generator as Preamble Generator

    User->>Orval: run generation (existing handler file present)
    Orval->>FS: read existing handler file
    FS-->>Orval: file contents
    Orval->>Parser: extractExistingHandlerBodies(source)
    Parser->>Parser: parse exports, track nested braces/strings/regex/comments
    Parser-->>Orval: Map<handlerName, handlerBody>
    Orval->>Generator: compute fresh preamble (header/imports/factory/validators)
    Generator-->>Orval: preamble text
    Orval->>Orval: merge preamble + generated wrappers, splice extracted handler bodies
    Orval->>FS: write updated handler file
    FS-->>User: handler file updated (preamble refreshed, user bodies preserved)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • snebjorn
  • melloware

Poem

🐰
I hop through code with nimble paws,
Preserving your logic, renewing the laws,
Headers refreshed, imports aligned,
Bodies kept safe — no work left behind,
A tidy regen dance, tidy and kind. ✨

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/hono/src/index.ts`:
- Around line 352-375: The current logic preserves the entire previous handler
block (including factory.createHandlers(...) and zValidator(...) entries) when
extracting existing handlers via extractExistingHandlers, causing stale
validators to survive; change the merge in the loop that builds content so that
for each handlerName you extract only the inner async handler body from existing
(not the surrounding factory.createHandlers wrapper) and then always regenerate
the wrapper and validator chain (using verbOption and getHonoHandlers) based on
current metadata; additionally detect if the list of emitted validators
(zValidator entries) differs from the current verbOption validators and in that
case ignore the old wrapper and emit a fresh handler block so imports and
middleware reflect the new configuration.
- Around line 398-424: extractExistingHandlers currently uses naive parenthesis
depth counting (exportRegex + manual loop) and will break when ) appears inside
strings, templates, regexes, or comments; update it to either (preferred) use
the TypeScript tokenizer/parser to locate the factory.createHandlers call and
extract the full node text, or (if not adding a parser) implement a
lexical-state loop that skips over single-quoted, double-quoted, template
literals, regex literals and both line and block comments while counting
parentheses so you only decrement depth for ) characters outside those ranges;
keep the exported symbol detection (exportRegex / match[1]) but replace the
character-scanning loop in extractExistingHandlers so it advances past
quoted/comment/regex ranges correctly before treating parentheses as
depth-affecting.

In
`@tests/__snapshots__/hono/petstore-tags-split-with-handlers/endpoints.validator.ts`:
- Around line 161-193: The hook result handling overwrites c.res later,
nullifying hook-provided responses; update the validator logic in
endpoints.validator.ts (the block using hook, hookResult, c.res and success) to
return early or skip the final c.res assignment when hookResult is a Response or
contains response: Response — i.e., after setting c.res from hookResult,
short-circuit out of the function (or set a flag like handled) so the subsequent
success/failure branch that rebuilds c.res is not executed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2cf7dd6e-0267-4269-8f71-b63248a0b7d5

📥 Commits

Reviewing files that changed from the base of the PR and between 56256ff and 46f4ee5.

📒 Files selected for processing (41)
  • docs/content/docs/guides/hono.mdx
  • packages/hono/src/index.test.ts
  • packages/hono/src/index.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/endpoints.context.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/endpoints.schemas.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/endpoints.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/endpoints.validator.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/endpoints.zod.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/src/handlers/createPets.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/src/handlers/deletePetById.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/src/handlers/healthCheck.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/src/handlers/listPets.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/src/handlers/showPetById.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/src/handlers/showPetWithOwner.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/endpoints.schemas.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/endpoints.validator.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/health/health.context.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/health/health.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/pets/pets.context.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/pets/pets.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/pets/pets.zod.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/src/handlers/createPets.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/src/handlers/deletePetById.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/src/handlers/healthCheck.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/src/handlers/listPets.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/src/handlers/showPetById.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/src/handlers/showPetWithOwner.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/endpoints.schemas.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/endpoints.validator.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/health.context.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/health.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/pets.context.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/pets.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/pets.zod.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/src/handlers/createPets.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/src/handlers/deletePetById.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/src/handlers/healthCheck.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/src/handlers/listPets.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/src/handlers/showPetById.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/src/handlers/showPetWithOwner.ts
  • tests/configs/hono.config.ts

Comment thread packages/hono/src/index.ts Outdated
Comment thread packages/hono/src/index.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@samples/hono/hono-with-fetch-client/hono-app/src/handlers/createPets.ts`:
- Around line 10-15: The zValidator calls use the old camelCase names
(createPetsBody, createPetsResponse) but the file imports PascalCase types
CreatePetsBody and CreatePetsResponse; update the zValidator invocations in the
createPetsHandlers call to reference the imported PascalCase identifiers
(CreatePetsBody, CreatePetsResponse) so they match the imports and resolve
correctly—look for createFactory, createPetsHandlers and zValidator to locate
and fix the mismatched identifiers.

In `@samples/hono/hono-with-fetch-client/hono-app/src/handlers/listPets.ts`:
- Around line 14-15: The validators are referencing old identifiers
listPetsQueryParams and listPetsResponse which no longer exist; update the
zValidator calls in the listPets handler to use the imported renamed Zod schemas
ListPetsQueryParams and ListPetsResponse (e.g., zValidator('query',
ListPetsQueryParams) and zValidator('response', ListPetsResponse)) so the
identifiers match the imports and resolve correctly (references: zValidator,
ListPetsQueryParams, ListPetsResponse, listPets handler).

In `@samples/hono/hono-with-fetch-client/hono-app/src/handlers/updatePets.ts`:
- Around line 10-15: The handlers import the Zod types UpdatePetsBody and
UpdatePetsResponse but pass undefined identifiers
updatePetsBody/updatePetsResponse into zValidator; fix by using the imported
symbols (pass UpdatePetsBody and UpdatePetsResponse to zValidator) or adjust the
import to match the validator calls, i.e., ensure the identifiers passed to
zValidator in factory.createHandlers match the actual exported names
(UpdatePetsBody, UpdatePetsResponse) so zValidator('json', ...) and
zValidator('response', ...) receive the correct Zod schemas.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9770e5d6-5905-482b-99c2-c867b34065ad

📥 Commits

Reviewing files that changed from the base of the PR and between 46f4ee5 and 8a56d23.

📒 Files selected for processing (16)
  • samples/hono/hono-with-fetch-client/__snapshots__/hono-app/handlers/createPets.ts
  • samples/hono/hono-with-fetch-client/__snapshots__/hono-app/handlers/listPets.ts
  • samples/hono/hono-with-fetch-client/__snapshots__/hono-app/handlers/showPetById.ts
  • samples/hono/hono-with-fetch-client/__snapshots__/hono-app/handlers/updatePets.ts
  • samples/hono/hono-with-fetch-client/hono-app/src/handlers/createPets.ts
  • samples/hono/hono-with-fetch-client/hono-app/src/handlers/listPets.ts
  • samples/hono/hono-with-fetch-client/hono-app/src/handlers/showPetById.ts
  • samples/hono/hono-with-fetch-client/hono-app/src/handlers/updatePets.ts
  • samples/hono/hono-with-zod/__snapshots__/handlers/createPets.ts
  • samples/hono/hono-with-zod/__snapshots__/handlers/listPets.ts
  • samples/hono/hono-with-zod/__snapshots__/handlers/showPetById.ts
  • samples/hono/hono-with-zod/__snapshots__/handlers/updatePets.ts
  • samples/hono/hono-with-zod/src/handlers/createPets.ts
  • samples/hono/hono-with-zod/src/handlers/listPets.ts
  • samples/hono/hono-with-zod/src/handlers/showPetById.ts
  • samples/hono/hono-with-zod/src/handlers/updatePets.ts
✅ Files skipped from review due to trivial changes (9)
  • samples/hono/hono-with-zod/src/handlers/createPets.ts
  • samples/hono/hono-with-fetch-client/hono-app/src/handlers/showPetById.ts
  • samples/hono/hono-with-fetch-client/snapshots/hono-app/handlers/listPets.ts
  • samples/hono/hono-with-zod/src/handlers/listPets.ts
  • samples/hono/hono-with-zod/src/handlers/updatePets.ts
  • samples/hono/hono-with-zod/snapshots/handlers/createPets.ts
  • samples/hono/hono-with-zod/snapshots/handlers/updatePets.ts
  • samples/hono/hono-with-zod/snapshots/handlers/showPetById.ts
  • samples/hono/hono-with-zod/snapshots/handlers/listPets.ts

Comment thread samples/hono/hono-with-fetch-client/hono-app/src/handlers/createPets.ts Outdated
Comment thread samples/hono/hono-with-fetch-client/hono-app/src/handlers/listPets.ts Outdated
Comment thread samples/hono/hono-with-fetch-client/hono-app/src/handlers/updatePets.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/hono/src/index.ts`:
- Around line 522-528: The isRegexContext function misclassifies slashes after
keywords as division; update it to, after skipping whitespace, detect whether
the preceding characters form one of the keywords or tokens that allow a regex
literal (e.g., return, throw, yield, await, case) or a spread ('...') instead of
a generic word char check. Concretely: in isRegexContext, when you hit a
non-whitespace char walking backward, if that char is '.' check for a preceding
'...' sequence and treat it as a regex context; if the char is an identifier
char (letter/underscore/$), scan further backward to extract the full token and
compare against the allowed keyword set (return, throw, yield, await, case); if
it matches, return true (regex), otherwise fall back to the existing division
detection logic. This ensures findMatchingClose and related bracket tracking
treat regex literals after those keywords correctly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 59f2d7cc-2f36-46d0-a0dc-51e09d8de35b

📥 Commits

Reviewing files that changed from the base of the PR and between 8a56d23 and 3a0ea9b.

📒 Files selected for processing (26)
  • packages/hono/src/index.test.ts
  • packages/hono/src/index.ts
  • packages/hono/src/zValidator.ts
  • samples/hono/composite-routes-with-tags-split/__snapshots__/endpoints/validator.ts
  • samples/hono/composite-routes-with-tags-split/src/endpoints/validator.ts
  • samples/hono/hono-with-fetch-client/__snapshots__/hono-app/handlers/createPets.ts
  • samples/hono/hono-with-fetch-client/__snapshots__/hono-app/handlers/listPets.ts
  • samples/hono/hono-with-fetch-client/__snapshots__/hono-app/handlers/showPetById.ts
  • samples/hono/hono-with-fetch-client/__snapshots__/hono-app/handlers/updatePets.ts
  • samples/hono/hono-with-fetch-client/__snapshots__/hono-app/petstore.validator.ts
  • samples/hono/hono-with-fetch-client/hono-app/src/handlers/createPets.ts
  • samples/hono/hono-with-fetch-client/hono-app/src/handlers/listPets.ts
  • samples/hono/hono-with-fetch-client/hono-app/src/handlers/showPetById.ts
  • samples/hono/hono-with-fetch-client/hono-app/src/handlers/updatePets.ts
  • samples/hono/hono-with-fetch-client/hono-app/src/petstore.validator.ts
  • samples/hono/hono-with-zod/__snapshots__/petstore.validator.ts
  • samples/hono/hono-with-zod/src/petstore.validator.ts
  • tests/__snapshots__/hono/endpoint-parameters/endpoints.validator.ts
  • tests/__snapshots__/hono/petstore-single/endpoints.validator.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/endpoints.validator.ts
  • tests/__snapshots__/hono/petstore-split/endpoints.validator.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/endpoints.validator.ts
  • tests/__snapshots__/hono/petstore-tags-split/endpoints.validator.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/endpoints.validator.ts
  • tests/__snapshots__/hono/petstore-tags/endpoints.validator.ts
  • tests/__snapshots__/hono/zod-schema-response/endpoints.validator.ts
✅ Files skipped from review due to trivial changes (8)
  • samples/hono/hono-with-fetch-client/snapshots/hono-app/handlers/listPets.ts
  • samples/hono/hono-with-fetch-client/snapshots/hono-app/handlers/createPets.ts
  • samples/hono/hono-with-fetch-client/hono-app/src/handlers/createPets.ts
  • samples/hono/hono-with-fetch-client/hono-app/src/handlers/updatePets.ts
  • samples/hono/hono-with-fetch-client/snapshots/hono-app/handlers/updatePets.ts
  • samples/hono/hono-with-fetch-client/hono-app/src/handlers/showPetById.ts
  • samples/hono/hono-with-fetch-client/hono-app/src/handlers/listPets.ts
  • samples/hono/hono-with-fetch-client/snapshots/hono-app/handlers/showPetById.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/hono/src/index.test.ts

Comment thread packages/hono/src/index.ts
Comment thread packages/hono/src/index.ts Outdated
@melloware melloware added the hono Hono related issue label Apr 30, 2026
@zeriong

zeriong commented Apr 30, 2026

Copy link
Copy Markdown
Contributor Author

@snebjorn
Hello~ may I ask for a review?

@melloware
melloware requested a review from snebjorn April 30, 2026 13:04
@melloware
melloware force-pushed the fix/hono-handlers-imports-and-overwrite-2989 branch from 763007a to d7cb16a Compare May 5, 2026 20:57

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
packages/hono/src/index.ts (2)

522-529: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

isRegexContext still misclassifies regex literals after keywords.

The check !/[\w)\]]/.test(c) rejects any preceding word char, so return /…/, throw /…/, yield /…/, await /…/, case /…/ are all treated as division. When such a regex contains ) or } (e.g. return /[)]/.test(x) or return /\}+/.exec(s)), findMatchingClose then mis-counts depth and the body extraction terminates inside the regex literal — corrupting or dropping the preserved body.

When walking back hits an identifier char, scan the whole token and treat the slash as a regex when the token is one of return | throw | yield | await | case | new | typeof | void | delete | in | of | instanceof (and handle the ... spread case for .).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/hono/src/index.ts` around lines 522 - 529, isRegexContext currently
treats any preceding identifier char as division; update isRegexContext to, when
walking back finds an identifier-char (/[A-Za-z0-9_$]/), scan to extract the
full preceding token (including handling a leading "..." for spread when
encountering '.'), then treat the slash as a regex literal only if that token is
one of the keywords: return, throw, yield, await, case, new, typeof, void,
delete, in, of, instanceof; keep the existing whitespace-skipping and
punctuation logic for non-identifier chars so ) ] etc still mark division;
ensure the function references the same name isRegexContext and adjust the
slashIdx/backward-scan logic so returning true/false reflects this token-check.

537-549: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Body extraction still drops handlers that contain nested arrow functions.

lastIndexOf('=>') (Line 538) scans the entire callBody and picks up any user-authored arrow inside the handler — arr.map(x => x), arr.filter(x => x.id), even type positions like Array<() => void>. When the picked => is not followed by {, Line 543 returns undefined and the user's body is silently dropped during regeneration. Arrow callbacks are extremely common in handler bodies, so this is reachable in normal usage.

Locate the trailing async arrow argument by walking the top-level argument list with the existing lex-aware scanner (skipping strings/comments/regex and nested parens/braces) instead of a raw lastIndexOf('=>').

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/hono/src/index.ts` around lines 537 - 549, The extractAsyncArrowBody
currently uses lastIndexOf('=>') which can pick up nested arrow callbacks;
change extractAsyncArrowBody to scan the callBody from the start and walk the
top-level function argument list using the existing lex-aware scanner (the same
token-skipping logic used elsewhere) so you only consider arrows at top-level
argument positions, skipping strings, comments, regexes and nested
parens/braces; when you find a top-level '=>' ensure the next non-whitespace
char is '{', use findMatchingClose(i, '{','}') to get the block end, and return
the slice between braces; keep existing fallback behavior (return undefined)
when no top-level async arrow block is found.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/hono/src/index.test.ts`:
- Around line 32-62: Add two new test cases to cover edge cases missed by the
current tests in the index.test.ts file: one that verifies extraction works
correctly with nested arrow functions inside the handler body (e.g., using
pets.map(p => p.id)) and another that ensures regex literals appearing
immediately after keywords like return are properly handled (e.g., return
/[)]/.test(...)). These tests should be added alongside existing ones using
extractExistingHandlerBodies and verify the returned body includes the nested
arrow and regex expressions as expected.

---

Duplicate comments:
In `@packages/hono/src/index.ts`:
- Around line 522-529: isRegexContext currently treats any preceding identifier
char as division; update isRegexContext to, when walking back finds an
identifier-char (/[A-Za-z0-9_$]/), scan to extract the full preceding token
(including handling a leading "..." for spread when encountering '.'), then
treat the slash as a regex literal only if that token is one of the keywords:
return, throw, yield, await, case, new, typeof, void, delete, in, of,
instanceof; keep the existing whitespace-skipping and punctuation logic for
non-identifier chars so ) ] etc still mark division; ensure the function
references the same name isRegexContext and adjust the slashIdx/backward-scan
logic so returning true/false reflects this token-check.
- Around line 537-549: The extractAsyncArrowBody currently uses
lastIndexOf('=>') which can pick up nested arrow callbacks; change
extractAsyncArrowBody to scan the callBody from the start and walk the top-level
function argument list using the existing lex-aware scanner (the same
token-skipping logic used elsewhere) so you only consider arrows at top-level
argument positions, skipping strings, comments, regexes and nested
parens/braces; when you find a top-level '=>' ensure the next non-whitespace
char is '{', use findMatchingClose(i, '{','}') to get the block end, and return
the slice between braces; keep existing fallback behavior (return undefined)
when no top-level async arrow block is found.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cce320d3-7d13-42c5-b8d2-ad009f3499d6

📥 Commits

Reviewing files that changed from the base of the PR and between 3a0ea9b and d7cb16a.

📒 Files selected for processing (5)
  • docs/content/docs/guides/hono.mdx
  • packages/hono/src/index.test.ts
  • packages/hono/src/index.ts
  • packages/hono/src/zValidator.ts
  • samples/hono/composite-routes-with-tags-split/__snapshots__/endpoints/validator.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/content/docs/guides/hono.mdx

Comment thread packages/hono/src/index.test.ts

@melloware melloware left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this PR needs to be updated from main

@zeriong
zeriong force-pushed the fix/hono-handlers-imports-and-overwrite-2989 branch from d7cb16a to cc59b57 Compare May 6, 2026 12:49
zeriong added 6 commits May 6, 2026 21:51
…rval-labs#2989)

The hono client emitted a stale handler file once it existed: imports were
never updated, so a wrong path baked in by an earlier generation (or by a
config change) stuck around forever. The path computation itself also fell
through to the tags-split layout for split/single modes, producing imports
like `../pets/pets.context` when modules actually live at `../endpoints.context`.

- Always re-emit the file header, imports, and `const factory = createFactory();`
  preamble on regeneration so config changes propagate to existing files.
- Preserve the body of each `factory.createHandlers(...)` block verbatim via
  a paren-aware extractor so user business logic survives across regenerations.
- Branch zod/context module resolution by mode (tags / tags-split / split|single)
  so each layout matches what generateZodFiles and generateContextFiles emit.
- Fix getHonoHeader's per-tag join so `tags` (flat) imports resolve relative
  to targetInfo.dirname instead of a non-existent tag sub-directory.
…plit (orval-labs#2989)

Adds three regression snapshot fixtures driven by the override.hono.handlers
option (one per mode) plus unit tests for extractExistingHandlers covering
nested parens in user-edited bodies and empty inputs.
…h fix (orval-labs#2989)

The hono-with-zod and hono-with-fetch-client samples shipped the broken
output documented in the issue (e.g. `../pets/pets.context` in split mode).
Re-running orval with the fix produces the corrected imports and the orval
file header, so the committed sample files and their snapshots are updated
to match.
…response hook (orval-labs#2989)

Two CodeRabbit findings on the previous preservation implementation:

1. Reusing the entire previous `factory.createHandlers(...)` block let stale
   `zValidator(...)` calls survive across regenerations even though their
   imports were rewritten — the visible failure was camelCase identifiers
   (e.g. `createPetsBody`) referencing PascalCase imports (`CreatePetsBody`).
   Now we extract only the user-authored async body and always rebuild the
   wrapper and validator chain from current verb metadata.

2. The naive paren-counting extractor terminated on any `)` character, so
   handler bodies containing `)` inside strings/templates/regex/comments
   produced invalid TypeScript. Replaced with a lex-aware scanner that
   skips strings, template literals, regex literals, and both line and
   block comments while tracking parenthesis/brace depth.

Also returns early in the generated zValidator wrper after a hook
supplies a `Response` so the custom body is no longer overwritten by the
default success/failure branch.
…tor (orval-labs#2989)

Re-running orval propagates the body-only preservation change and the
zValidator hook early-return into the committed sample handlers and the
generated validator middleware across every hono snapshot fixture.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
packages/hono/src/index.ts (2)

522-527: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

isRegexContext misclassifies keyword-led regex literals.

At Line 522, the one-character lookback treats return /[)]/.test(x) as division. That lets ) inside regex literals affect bracket depth in findMatchingClose, which can truncate preserved handler code.

🛠️ Proposed fix
+const REGEX_PREFIX_KEYWORDS = new Set([
+  'return',
+  'throw',
+  'yield',
+  'await',
+  'case',
+]);
+
 const isRegexContext = (source: string, slashIdx: number): boolean => {
   for (let j = slashIdx - 1; j >= 0; j--) {
     const c = source[j];
     if (c === ' ' || c === '\t' || c === '\n') continue;
-    return !/[\w)\]]/.test(c);
+    if (c === '.') {
+      return source.slice(Math.max(0, j - 2), j + 1) === '...';
+    }
+    if (/[A-Za-z0-9_$]/.test(c)) {
+      let start = j;
+      while (start - 1 >= 0 && /[A-Za-z0-9_$]/.test(source[start - 1])) {
+        start--;
+      }
+      const token = source.slice(start, j + 1);
+      return REGEX_PREFIX_KEYWORDS.has(token);
+    }
+    return !/[)\]}]/.test(c);
   }
   return true;
 };
#!/bin/bash
python - <<'PY'
import re

def is_regex_context(source, slash_idx):
    for j in range(slash_idx - 1, -1, -1):
        c = source[j]
        if c in " \t\n":
            continue
        return not re.search(r'[\w)\]]', c)
    return True

cases = [
    ("return /[)]/.test(x)", True),
    ("throw /[)]/.test(x)", True),
    ("const re = /[)]/", True),
    ("value / 2", False),
]

for src, expected in cases:
    actual = is_regex_context(src, src.index('/'))
    print(f"{src!r} -> {actual} (expected {expected})")
PY

537-543: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

extractAsyncArrowBody drops bodies containing nested arrows.

At Line 538, lastIndexOf('=>') can lock onto inner arrows (e.g., items.map((p) => p.id)), so Line 543 misses the outer {...} and returns undefined. That silently loses user handler bodies on regeneration.

🛠️ Proposed fix
 const extractAsyncArrowBody = (callBody: string): string | undefined => {
-  const arrowIdx = callBody.lastIndexOf('=>');
+  // Parse the last top-level argument of createHandlers(...) first,
+  // then find the outer async arrow inside that argument.
+  const handlerArg = getLastTopLevelArgument(callBody);
+  const arrowIdx = handlerArg.indexOf('=>');
   if (arrowIdx === -1) return undefined;
 
   let i = arrowIdx + 2;
-  while (i < callBody.length && /\s/.test(callBody[i])) i++;
-  if (callBody[i] !== '{') return undefined;
+  while (i < handlerArg.length && /\s/.test(handlerArg[i])) i++;
+  if (handlerArg[i] !== '{') return undefined;
 
-  const closeIdx = findMatchingClose(callBody, i, '{', '}');
+  const closeIdx = findMatchingClose(handlerArg, i, '{', '}');
   if (closeIdx === -1) return undefined;
 
-  return callBody.slice(i + 1, closeIdx);
+  return handlerArg.slice(i + 1, closeIdx);
 };
#!/bin/bash
python - <<'PY'
def extract_async_arrow_body(call_body):
    arrow_idx = call_body.rfind("=>")
    if arrow_idx == -1:
        return None
    i = arrow_idx + 2
    while i < len(call_body) and call_body[i].isspace():
        i += 1
    if i >= len(call_body) or call_body[i] != "{":
        return None
    return "BODY_FOUND"

samples = [
    "zValidator('json', S), async (c: Ctx) => { return c.json([]); }",
    "zValidator('json', S), async (c: Ctx) => { const ids = pets.map((p) => p.id); return c.json(ids); }",
]

for s in samples:
    print(extract_async_arrow_body(s))
PY
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/hono/src/index.ts` around lines 537 - 543, The function
extractAsyncArrowBody currently uses lastIndexOf('=>') which can match inner
arrow functions; change the logic to scan all occurrences of '=>' in callBody
(e.g., loop from start using indexOf('=>', fromPos)) and choose the rightmost
occurrence whose following non-whitespace character is '{' (ensure you check i <
callBody.length before accessing callBody[i]); update extractAsyncArrowBody to
return the block body for that occurrence and skip any '=>' whose following char
is not '{' so nested non-block arrows (like items.map((p) => p.id)) are ignored.
🧹 Nitpick comments (1)
docs/content/docs/guides/hono.mdx (1)

64-70: 💤 Low value

Optional: example identifiers no longer match generated output.

The example block still references listPetsQueryParams / listPetsResponse (camelCase) at Lines 64 and 69-70 (and again at Lines 88-89), but the regenerated handlers in this PR now import PascalCase ListPetsQueryParams / ListPetsResponse from petstore.zod (see samples/hono/hono-with-fetch-client/hono-app/src/handlers/listPets.ts Line 10). Worth refreshing the snippet so the docs match what orval actually emits.

📝 Proposed doc update
-import { listPetsQueryParams, listPetsResponse } from '../petstore.zod';
+import { ListPetsQueryParams, ListPetsResponse } from '../petstore.zod';

 const factory = createFactory();

 export const listPetsHandlers = factory.createHandlers(
-  zValidator('query', listPetsQueryParams),
-  zValidator('response', listPetsResponse),
+  zValidator('query', ListPetsQueryParams),
+  zValidator('response', ListPetsResponse),
   async (c: ListPetsContext) => {

…and the same rename in the second snippet around Lines 88-89.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/content/docs/guides/hono.mdx` around lines 64 - 70, The docs import and
handler examples reference old camelCase identifiers listPetsQueryParams and
listPetsResponse but the generated code now exports PascalCase names
ListPetsQueryParams and ListPetsResponse; update the import and usages in the
example(s) (the top import and the factory.createHandlers call that uses
zValidator('query', ...) and zValidator('response', ...)) to use
ListPetsQueryParams and ListPetsResponse so the snippet matches the actual
output from petstore.zod and the samples/hono handler file.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/__snapshots__/hono/petstore-tags-with-handlers/endpoints.validator.ts`:
- Around line 157-159: The validator currently always calls the v3 instance
method (schema as v3.ZodType).safeParseAsync(value), which breaks for v4 core
schemas that require the top-level v4.safeParseAsync(schema, value); update the
code to detect whether the provided schema is a v4 core schema (or otherwise
lacks the instance safeParseAsync) and, if so, call v4.safeParseAsync(schema,
value), otherwise retain the existing (schema as
v3.ZodType).safeParseAsync(value) call and keep the returned shape as
ZodSafeParseResult<InferredValue, Out, T>.

In
`@tests/__snapshots__/hono/petstore-tags-with-handlers/src/handlers/listPets.ts`:
- Around line 8-15: The handler only validates query params but misses the
generated header schema; update the createHandlers call for listPetsHandlers to
include the header validator by passing zValidator('header', ListPetsHeader)
(alongside the existing zValidator('query', ListPetsQueryParams)) so the
X-EXAMPLE header is validated and mapped into the request context type used by
ListPetsContext; ensure the validators are passed before the async handler in
the factory.createHandlers invocation.

---

Duplicate comments:
In `@packages/hono/src/index.ts`:
- Around line 537-543: The function extractAsyncArrowBody currently uses
lastIndexOf('=>') which can match inner arrow functions; change the logic to
scan all occurrences of '=>' in callBody (e.g., loop from start using
indexOf('=>', fromPos)) and choose the rightmost occurrence whose following
non-whitespace character is '{' (ensure you check i < callBody.length before
accessing callBody[i]); update extractAsyncArrowBody to return the block body
for that occurrence and skip any '=>' whose following char is not '{' so nested
non-block arrows (like items.map((p) => p.id)) are ignored.

---

Nitpick comments:
In `@docs/content/docs/guides/hono.mdx`:
- Around line 64-70: The docs import and handler examples reference old
camelCase identifiers listPetsQueryParams and listPetsResponse but the generated
code now exports PascalCase names ListPetsQueryParams and ListPetsResponse;
update the import and usages in the example(s) (the top import and the
factory.createHandlers call that uses zValidator('query', ...) and
zValidator('response', ...)) to use ListPetsQueryParams and ListPetsResponse so
the snippet matches the actual output from petstore.zod and the samples/hono
handler file.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a4ba7bff-e46b-49c9-aea2-e3a6279b2d74

📥 Commits

Reviewing files that changed from the base of the PR and between d7cb16a and cc59b57.

📒 Files selected for processing (70)
  • docs/content/docs/guides/hono.mdx
  • packages/hono/src/index.test.ts
  • packages/hono/src/index.ts
  • packages/hono/src/zValidator.ts
  • samples/hono/composite-routes-with-tags-split/__snapshots__/endpoints/validator.ts
  • samples/hono/composite-routes-with-tags-split/src/endpoints/validator.ts
  • samples/hono/hono-with-fetch-client/__snapshots__/hono-app/handlers/createPets.ts
  • samples/hono/hono-with-fetch-client/__snapshots__/hono-app/handlers/listPets.ts
  • samples/hono/hono-with-fetch-client/__snapshots__/hono-app/handlers/showPetById.ts
  • samples/hono/hono-with-fetch-client/__snapshots__/hono-app/handlers/updatePets.ts
  • samples/hono/hono-with-fetch-client/__snapshots__/hono-app/petstore.validator.ts
  • samples/hono/hono-with-fetch-client/hono-app/src/handlers/createPets.ts
  • samples/hono/hono-with-fetch-client/hono-app/src/handlers/listPets.ts
  • samples/hono/hono-with-fetch-client/hono-app/src/handlers/showPetById.ts
  • samples/hono/hono-with-fetch-client/hono-app/src/handlers/updatePets.ts
  • samples/hono/hono-with-fetch-client/hono-app/src/petstore.validator.ts
  • samples/hono/hono-with-zod/__snapshots__/handlers/createPets.ts
  • samples/hono/hono-with-zod/__snapshots__/handlers/listPets.ts
  • samples/hono/hono-with-zod/__snapshots__/handlers/showPetById.ts
  • samples/hono/hono-with-zod/__snapshots__/handlers/updatePets.ts
  • samples/hono/hono-with-zod/__snapshots__/petstore.validator.ts
  • samples/hono/hono-with-zod/src/handlers/createPets.ts
  • samples/hono/hono-with-zod/src/handlers/listPets.ts
  • samples/hono/hono-with-zod/src/handlers/showPetById.ts
  • samples/hono/hono-with-zod/src/handlers/updatePets.ts
  • samples/hono/hono-with-zod/src/petstore.validator.ts
  • tests/__snapshots__/hono/endpoint-parameters/endpoints.validator.ts
  • tests/__snapshots__/hono/petstore-single/endpoints.validator.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/endpoints.context.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/endpoints.schemas.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/endpoints.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/endpoints.validator.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/endpoints.zod.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/src/handlers/createPets.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/src/handlers/deletePetById.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/src/handlers/healthCheck.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/src/handlers/listPets.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/src/handlers/showPetById.ts
  • tests/__snapshots__/hono/petstore-split-with-handlers/src/handlers/showPetWithOwner.ts
  • tests/__snapshots__/hono/petstore-split/endpoints.validator.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/endpoints.schemas.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/endpoints.validator.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/health/health.context.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/health/health.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/pets/pets.context.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/pets/pets.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/pets/pets.zod.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/src/handlers/createPets.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/src/handlers/deletePetById.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/src/handlers/healthCheck.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/src/handlers/listPets.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/src/handlers/showPetById.ts
  • tests/__snapshots__/hono/petstore-tags-split-with-handlers/src/handlers/showPetWithOwner.ts
  • tests/__snapshots__/hono/petstore-tags-split/endpoints.validator.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/endpoints.schemas.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/endpoints.validator.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/health.context.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/health.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/pets.context.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/pets.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/pets.zod.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/src/handlers/createPets.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/src/handlers/deletePetById.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/src/handlers/healthCheck.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/src/handlers/listPets.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/src/handlers/showPetById.ts
  • tests/__snapshots__/hono/petstore-tags-with-handlers/src/handlers/showPetWithOwner.ts
  • tests/__snapshots__/hono/petstore-tags/endpoints.validator.ts
  • tests/__snapshots__/hono/zod-schema-response/endpoints.validator.ts
  • tests/configs/hono.config.ts
✅ Files skipped from review due to trivial changes (1)
  • tests/snapshots/hono/petstore-tags-split-with-handlers/src/handlers/showPetById.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • tests/snapshots/hono/petstore-split-with-handlers/endpoints.context.ts
  • tests/snapshots/hono/petstore-tags-split-with-handlers/health/health.ts
  • tests/snapshots/hono/petstore-tags-split-with-handlers/src/handlers/createPets.ts
  • tests/snapshots/hono/petstore-tags-split-with-handlers/endpoints.schemas.ts
  • tests/snapshots/hono/petstore-tags-split/endpoints.validator.ts
  • tests/snapshots/hono/petstore-tags-with-handlers/health.ts

Comment on lines +157 to +159
const { success, data, error } = (await (
schema as v3.ZodType
).safeParseAsync(value)) as ZodSafeParseResult<InferredValue, Out, T>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

cat -n tests/__snapshots__/hono/petstore-tags-with-handlers/endpoints.validator.ts | head -200

Repository: orval-labs/orval

Length of output: 6802


🌐 Web query:

Zod v4 core API safeParseAsync top-level function vs instance method

💡 Result:

In Zod v4, there is no top-level safeParseAsync function on the main z object. The safeParseAsync method is available as an instance method on schema objects (e.g., schema.safeParseAsync(data)), identical to Zod v3 and continuing in the full Zod package.[1][2][3][4] However, in the new zod/v4/core sub-package (the foundational core API shared between full Zod and Zod Mini), parsing functions including safeParseAsync are provided as top-level functions that take a schema instance as the first argument: z.safeParseAsync(schema, data).[5][4][6] This design accommodates the core's schema classes, which lack instance methods, requiring top-level functions for parsing.[4] In the full Zod and Zod Mini packages, schema instances retain instance methods like safeParseAsync for convenience and backward compatibility.[7][8][9] Example from core: import * as z from 'zod/v4/core'; const schema = new z.$ZodString({ type: 'string' }); await z.safeParseAsync(schema, 'hello');[4] Example from full Zod: import { z } from 'zod'; const schema = z.string; await schema.safeParseAsync('hello');[1][3]

Citations:


🏁 Script executed:

# Let's check if there are any usage examples or tests that show actual v4 core schema usage
rg "v4\.\$ZodType|v4\.safeParseAsync" tests/ -A 3 -B 3

Repository: orval-labs/orval

Length of output: 42


🏁 Script executed:

# Check if there are any conditions or runtime checks that handle v4 differently
rg "safeParseAsync" tests/__snapshots__/hono/petstore-tags-with-handlers/ -B 5 -A 5

Repository: orval-labs/orval

Length of output: 1147


Use the Zod v4 core parse helper for v4 schemas.

This validator accepts v4.$ZodType, but lines 157-159 unconditionally use the v3 instance API. Zod v4 core schemas lack instance methods and require the top-level v4.safeParseAsync(schema, value) function. A v4 core schema will type-check but fail at runtime when validating responses.

Suggested fix
-    const { success, data, error } = (await (
-      schema as v3.ZodType
-    ).safeParseAsync(value)) as ZodSafeParseResult<InferredValue, Out, T>;
+    const parseResult =
+      'safeParseAsync' in schema
+        ? await schema.safeParseAsync(value)
+        : await v4.safeParseAsync(schema, value);
+
+    const { success, data, error } =
+      parseResult as ZodSafeParseResult<InferredValue, Out, T>;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { success, data, error } = (await (
schema as v3.ZodType
).safeParseAsync(value)) as ZodSafeParseResult<InferredValue, Out, T>;
const parseResult =
'safeParseAsync' in schema
? await schema.safeParseAsync(value)
: await v4.safeParseAsync(schema, value);
const { success, data, error } =
parseResult as ZodSafeParseResult<InferredValue, Out, T>;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/__snapshots__/hono/petstore-tags-with-handlers/endpoints.validator.ts`
around lines 157 - 159, The validator currently always calls the v3 instance
method (schema as v3.ZodType).safeParseAsync(value), which breaks for v4 core
schemas that require the top-level v4.safeParseAsync(schema, value); update the
code to detect whether the provided schema is a v4 core schema (or otherwise
lacks the instance safeParseAsync) and, if so, call v4.safeParseAsync(schema,
value), otherwise retain the existing (schema as
v3.ZodType).safeParseAsync(value) call and keep the returned shape as
ZodSafeParseResult<InferredValue, Out, T>.

Comment on lines +8 to +15
import { zValidator } from '../../endpoints.validator';
import { ListPetsContext } from '../../pets.context';
import { ListPetsQueryParams } from '../../pets.zod';

const factory = createFactory();
export const listPetsHandlers = factory.createHandlers(
zValidator('query', ListPetsQueryParams),
async (c: ListPetsContext) => {},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

find tests/__snapshots__/hono/petstore-tags-with-handlers -type f -name "*.ts" | head -20

Repository: orval-labs/orval

Length of output: 1028


🏁 Script executed:

find tests/__snapshots__/hono/petstore-tags-with-handlers -type f -name "pets.*" | head -20

Repository: orval-labs/orval

Length of output: 255


🏁 Script executed:

cat tests/__snapshots__/hono/petstore-tags-with-handlers/pets.zod.ts

Repository: orval-labs/orval

Length of output: 4369


🏁 Script executed:

cat tests/__snapshots__/hono/petstore-tags-with-handlers/src/handlers/listPets.ts

Repository: orval-labs/orval

Length of output: 559


🏁 Script executed:

cat tests/__snapshots__/hono/petstore-tags-with-handlers/pets.context.ts

Repository: orval-labs/orval

Length of output: 1366


🏁 Script executed:

cat tests/__snapshots__/hono/petstore-tags-with-handlers/src/handlers/createPets.ts

Repository: orval-labs/orval

Length of output: 696


🏁 Script executed:

cat tests/__snapshots__/hono/petstore-tags-with-handlers/endpoints.schemas.ts | head -100

Repository: orval-labs/orval

Length of output: 2009


Wire the generated header schema into this handler preamble.

pets.zod.ts exports ListPetsHeader, but this handler only validates the query string. That leaves the documented X-EXAMPLE header unenforced at runtime and unmapped in the context type, even though Hono supports header-target validators in the same middleware chain.

Suggested fix
 import { createFactory } from 'hono/factory';
 import { zValidator } from '../../endpoints.validator';
 import { ListPetsContext } from '../../pets.context';
-import { ListPetsQueryParams } from '../../pets.zod';
+import { ListPetsHeader, ListPetsQueryParams } from '../../pets.zod';
 
 const factory = createFactory();
 export const listPetsHandlers = factory.createHandlers(
+  zValidator('header', ListPetsHeader),
   zValidator('query', ListPetsQueryParams),
   async (c: ListPetsContext) => {},
 );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@tests/__snapshots__/hono/petstore-tags-with-handlers/src/handlers/listPets.ts`
around lines 8 - 15, The handler only validates query params but misses the
generated header schema; update the createHandlers call for listPetsHandlers to
include the header validator by passing zValidator('header', ListPetsHeader)
(alongside the existing zValidator('query', ListPetsQueryParams)) so the
X-EXAMPLE header is validated and mapped into the request context type used by
ListPetsContext; ensure the validators are passed before the async handler in
the factory.createHandlers invocation.

…y extraction (orval-labs#2989)

Address two preservation edge cases flagged by CodeRabbit on extractExistingHandlerBodies:

- isRegexContext now matches the preceding token against the keyword set that
  permits a regex literal (return, throw, yield, await, case, new, typeof,
  void, delete, in, of, instanceof) and the spread `...`. Previously a body
  like `return /[)]/.test(x)` was treated as division, so the bracket inside
  the regex character class corrupted depth tracking in findMatchingClose and
  the preserved body was truncated.
- extractAsyncArrowBody walks the top-level argument list with the existing
  lex-aware scanner instead of `lastIndexOf('=>')`, so an inner arrow
  callback (e.g. `pets.map((p) => p.id)`) is no longer mistaken for the
  handler arrow and bodies containing nested arrows survive regeneration.

Adds regression tests for both shapes.
@zeriong
zeriong force-pushed the fix/hono-handlers-imports-and-overwrite-2989 branch from cc59b57 to 1e50a78 Compare May 6, 2026 13:11
@melloware
melloware merged commit 1c07741 into orval-labs:master May 6, 2026
4 checks passed
@xandris

xandris commented May 18, 2026

Copy link
Copy Markdown
Contributor

uh how do i prevent orval from eating the imports my handler implementations need to function

@melloware

Copy link
Copy Markdown
Collaborator

@xandris might have to do some analysis and submit a PR?

@anymaniax

Copy link
Copy Markdown
Collaborator

@melloware @xandris gonna work on this because this pr make for me hono unusable I cannot add import or custom middleware I will do add a feature with multiple mode like full handled like now, skip completly when file already exist and a smart mode that try to fix the stuff that the generator need

@melloware

Copy link
Copy Markdown
Collaborator

@anymaniax sounds good!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

hono Hono related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hono handlers do not overwrite/update existing files

5 participants