Skip to content

fix(angular): honor schemas.importPath and splitByTags in resource files - #3813

Open
the-ult wants to merge 7 commits into
orval-labs:masterfrom
the-ult:fix/angular-resource-schemas-import-path
Open

fix(angular): honor schemas.importPath and splitByTags in resource files#3813
the-ult wants to merge 7 commits into
orval-labs:masterfrom
the-ult:fix/angular-resource-schemas-import-path

Conversation

@the-ult

@the-ult the-ult commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

What

Angular's retrievalClient: 'both' emits a sibling *.resource.ts next to each service. That file resolved schema imports with its own copy of core's rule, and the copy had drifted. With schemas: { path, importPath: '@acme/models' } the two files disagreed:

// pets.service.ts
import { Pet } from '@acme/models';

// pets.resource.ts
import { Pet } from '../../../../../../models/src/lib/generated/schemas';

That relative path crosses a package boundary, so module-boundary lint rules (Nx's @nx/enforce-module-boundaries, for example) reject it. retrievalClient: 'both' was unusable from a monorepo library.

Why this fix

Extra files are rendered in getApiBuilder, before any mode writer runs. A resource file cannot see the writers' import decisions, so it derives them from output — and two derivations of one rule drift.

The rule now lives once, in resolveSchemaImportDependencies (core/src/utils/schema-import-path.ts). Both generateImportsForBuilder and Angular's resource builder call it.

The schema→tag map moves to getApiBuilder and rides on WriteSpecBuilder, so writeSchemasTagsSplit no longer rebuilds it. It is built over the merged schema list, because component schemas are appended after getApiBuilder returns.

User-visible changes

  • schemas.importPath is honored in *.resource.ts — the reported bug. The specifier is emitted verbatim, with no file extension under indexFiles: false.
  • schemas.splitByTags is honored in *.resource.ts. The tag subdirectory was missing under indexFiles: false.
  • Zod schema imports use output.schemaFileExtension, so they point at the file that is written. Before, a custom fileExtension wrote model/pet.gen.ts but imported ./model/pet.zod.gen.
  • Imports dedupe on name + alias + values + default rather than name alone, so two aliases of one schema both survive.

Tests

  • core/src/utils/schema-import-path.test.ts — the helper across indexFiles, splitByTags, zod/TypeScript, importPath set or unset, and a custom fileExtension.
  • angular/src/http-resource.test.ts — the same options against rendered *.resource.ts.
  • orval/src/generate-spec.test.ts — end to end: the service file and the resource file must import the same symbol from the same module. A unit test on the resource builder alone cannot catch a disagreement, because it sees only one side.

Known, not fixed

TypeScript schemas with a custom fileExtension and schemas.importPath and indexFiles: false still import @acme/models/pet while the file is pet.gen.ts. Same family, outside this change.

Verification

build:release, typecheck, lint, format:check, test, test:snapshots — all green. No committed fixture or sample output changed.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved generated schema imports for Angular resources, including custom filenames, package paths, relative paths, and tag-split output.
    • Prevented duplicate schema imports while preserving required type and runtime imports.
    • Ensured tag-specific generated files consistently reference the correct schema modules.
  • Tests

    • Added coverage for schema import resolution, deduplication, custom extensions, package subpaths, and tag-based schema splitting.

the-ult and others added 3 commits August 5, 2026 13:20
`generateImportsForBuilder` owned the rule that maps a schema import to the
module it is imported from: package specifier vs relative path, file extension
vs none, tag subdirectory, zod suffix and basename, dedup key. Client
generators that emit extra sibling files cannot call it — it is not exported
from `@orval/core` — so Angular's `*.resource.ts` builder carried a copy that
had drifted.

Move the rule to `utils/schema-import-path.ts` as
`resolveSchemaImportDependencies`, exported through the existing `./utils`
barrel that `getSchemasImportPath` already uses for the same reason.
`generateImportsForBuilder` now composes it with its schema-factory and
external-import handling.

The seam is the dependency list rather than a path string, so `indexFiles`
branch selection cannot drift either. Export shaping stays caller-owned: it
legitimately differs between callers.

No behaviour change — core's own logic, relocated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`writeSpecs` built the schema→tag map for `schemas.splitByTags`. Extra files
(`*.resource.ts` and friends) are rendered earlier, inside `getApiBuilder`, so
generators that emit them had no way to route schema imports into the same tag
subdirectories the writers would later use.

Build the map in `getApiBuilder` instead, carry it on `WriteSpecBuilder`, and
pass it to `ClientExtraFilesBuilder` as an optional fourth argument. `writeSpecs`
now consumes it rather than recomputing it, so every consumer routes through one
map instead of two derivations that can drift.

The map is a required field, not optional: a construction site that omits it
should fail to compile rather than silently degrade to a flat layout.

`getApiBuilder` takes `componentSchemas` because the map must be built over the
merged schema list. Component schemas are appended after it returns, so building
from the operation-derived schemas alone yields a map missing nearly every
schema — which collapses tag routing to flat without any error.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`buildSchemaImportDependencies` re-derived core's schema-import rule instead of
sharing it, and had drifted from it on four points. Resource files are rendered
before any mode writer runs, so they cannot observe the writers' decisions —
re-deriving the rule is what let the two disagree.

Delegate to core's `resolveSchemaImportDependencies`. Four fixes follow:

- `schemas.importPath` is now honored. It was ignored, so a config setting
  `schemas: { path, importPath: '@acme/models' }` emitted:

    pets.service.ts   import { Pet } from '@acme/models';
    pets.resource.ts  import { Pet } from '../../../../../../models/.../schemas';

  The deep relative path crosses a package boundary, which module-boundary lint
  rules reject (e.g. Nx's `@nx/enforce-module-boundaries`), so
  `retrievalClient: 'both'` could not be used from a monorepo library. Package
  specifiers are emitted verbatim, and carry no file extension under
  `indexFiles: false` — `@acme/models/pet.js` would not resolve.

- `schemas.splitByTags` is now honored. With `indexFiles: false` the tag
  subdirectory was omitted, so the resource file imported `<schemas>/pet` while
  the schema is written to `<schemas>/pets/pet`.

- Zod schema filenames now derive from the TS identifier, matching how
  `writeZodSchemas` names the files. Deriving from `schemaName` pointed at a
  file that is never emitted whenever the two differ.

- Imports dedupe on name, alias, values and default rather than name alone, so
  the same schema imported under two aliases no longer loses one.

Tested as one matrix asserted twice — against the shared helper in core, and
against rendered `*.resource.ts` output here — plus an end-to-end test that
generates both files and asserts they import the same symbol from the same
module.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 5, 2026 11:28
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@the-ult, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 13 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: dc9c0ee0-d7b9-4311-9916-ae15bf14c9ef

📥 Commits

Reviewing files that changed from the base of the PR and between aa4382e and 9e8eb31.

📒 Files selected for processing (15)
  • packages/angular/src/http-resource.test.ts
  • packages/angular/src/http-resource.ts
  • packages/core/src/types.ts
  • packages/core/src/utils/index.ts
  • packages/core/src/utils/schema-import-path.test.ts
  • packages/core/src/utils/schema-import-path.ts
  • packages/core/src/writers/generate-imports-for-builder.test.ts
  • packages/core/src/writers/generate-imports-for-builder.ts
  • packages/core/src/writers/schemas-tags-split.test.ts
  • packages/core/src/writers/schemas-tags-split.ts
  • packages/orval/src/api.ts
  • packages/orval/src/client.ts
  • packages/orval/src/generate-spec.test.ts
  • packages/orval/src/import-open-api.ts
  • packages/orval/src/write-specs.ts

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: fa47aad8-9518-49b0-b075-12cef9812afe

📥 Commits

Reviewing files that changed from the base of the PR and between 75036da and 04ad651.

📒 Files selected for processing (12)
  • packages/angular/src/http-resource.test.ts
  • packages/angular/src/http-resource.ts
  • packages/core/src/types.ts
  • packages/core/src/utils/schema-import-path.test.ts
  • packages/core/src/utils/schema-import-path.ts
  • packages/core/src/writers/generate-imports-for-builder.test.ts
  • packages/core/src/writers/generate-imports-for-builder.ts
  • packages/core/src/writers/schemas-tags-split.test.ts
  • packages/core/src/writers/schemas-tags-split.ts
  • packages/orval/src/api.ts
  • packages/orval/src/generate-spec.test.ts
  • packages/orval/src/write-specs.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/orval/src/write-specs.ts
  • packages/core/src/writers/generate-imports-for-builder.ts
  • packages/angular/src/http-resource.ts
  • packages/orval/src/api.ts

📝 Walkthrough

Walkthrough

Schema import resolution moves into a shared core utility. Schema tag mappings now flow from API construction through writers and Angular resource generation. Tests cover package, relative, tag-split, extension, Zod, and deduplication cases.

Changes

Schema import routing

Layer / File(s) Summary
Core schema import resolution
packages/core/src/utils/schema-import-path.ts, packages/core/src/utils/schema-import-path.test.ts, packages/core/src/writers/generate-imports-for-builder.ts, packages/core/src/writers/generate-imports-for-builder.test.ts
Core utilities resolve schema dependency paths for barrel and per-file outputs, apply naming and extension rules, route tag directories, and deduplicate imports.
Schema map propagation
packages/core/src/types.ts, packages/core/src/writers/schemas-tags-split.ts, packages/core/src/writers/schemas-tags-split.test.ts, packages/orval/src/api.ts, packages/orval/src/client.ts, packages/orval/src/import-open-api.ts, packages/orval/src/write-specs.ts
API and writer contracts carry a shared schemaTagMap from component schemas and operations into schema and extra-file generation.
Angular resource integration
packages/angular/src/http-resource.ts, packages/angular/src/http-resource.test.ts, packages/orval/src/generate-spec.test.ts
Angular resource imports use configured schema paths, shared resolution, tag mappings, Zod value imports, and duplicate-import removal. Regression tests cover tag-split and custom Zod filenames.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: ⚪ Minimal · up to 04ad6

This PR aligns Angular resource-file schema imports and tag splitting with the configured output, preventing invalid monorepo import paths. No actionable merge-blocking risk remains beyond normal checks and review.

Possibly related PRs

Suggested labels: bug

Suggested reviewers: aqeelat, melloware

Sequence Diagram(s)

sequenceDiagram
  participant importOpenApi
  participant getApiBuilder
  participant generateExtraFiles
  participant AngularResource
  participant resolveSchemaImportDependencies

  importOpenApi->>getApiBuilder: pass componentSchemas
  getApiBuilder->>generateExtraFiles: pass schemaTagMap
  generateExtraFiles->>AngularResource: generate extra resource files
  AngularResource->>resolveSchemaImportDependencies: resolve schema dependencies
  resolveSchemaImportDependencies-->>AngularResource: return grouped imports
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the Angular resource-file fix for schemas.importPath and splitByTags, which matches the primary changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI 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.

Pull request overview

This PR fixes Angular retrievalClient: 'both' resource-file generation so *.resource.ts resolves schema imports using the same logic as core writers, ensuring consistent imports (including honoring schemas.importPath and schemas.splitByTags) and avoiding cross-package deep relative paths in monorepos.

Changes:

  • Extracts schema-import dependency resolution into a shared core utility (resolveSchemaImportDependencies) and reuses it from both core writers and Angular *.resource.ts generation.
  • Computes and propagates a single schema→tag map during API building so extra files and mode writers route schema imports identically.
  • Adds targeted unit + integration tests (core util matrix, Angular resource output matrix, and an end-to-end service/resource agreement test).

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated no comments.

Show a summary per file
File Description
packages/orval/src/write-specs.ts Stops recomputing the schema→tag map in writeSpecs, instead using the one carried on the builder.
packages/orval/src/import-open-api.ts Passes component-derived schemas into getApiBuilder so the schema→tag map is built over the merged schema list.
packages/orval/src/generate-spec.test.ts Adds E2E coverage ensuring Angular service/resource files import the same schema from the same module.
packages/orval/src/client.ts Threads schemaTagMap through the extra-files generation call chain.
packages/orval/src/api.ts Builds schemaTagMap during API building (before extra files render) and passes it to extra-file generators.
packages/core/src/writers/generate-imports-for-builder.ts Replaces duplicated schema-import path logic with the shared resolver util.
packages/core/src/utils/schema-import-path.ts Introduces resolveSchemaImportDependencies (+ dedupeSchemaImports) as the shared single source of truth for schema import dependency resolution.
packages/core/src/utils/schema-import-path.test.ts Adds a matrix test suite for schema-import dependency resolution behavior.
packages/core/src/utils/index.ts Exports the new schema import resolver utility via the utils barrel.
packages/core/src/types.ts Extends extra-file builder signature to accept schemaTagMap and adds schemaTagMap to builder types for propagation.
packages/core/src/test-utils/split-modes.ts Updates test builder factory to include the new schemaTagMap field.
packages/angular/src/http-resource.ts Routes resource-file schema imports through the shared core resolver; honors schemas.importPath and tag subdirectories.
packages/angular/src/http-resource.test.ts Adds a mirrored matrix asserting *.resource.ts schema import resolution matches core behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@pkg-pr-new

pkg-pr-new Bot commented Aug 5, 2026

Copy link
Copy Markdown

Open in StackBlitz

@orval/angular

bun add https://pkg.pr.new/@orval/angular@9e8eb31

@orval/axios

bun add https://pkg.pr.new/@orval/axios@9e8eb31

@orval/core

bun add https://pkg.pr.new/@orval/core@9e8eb31

@orval/effect

bun add https://pkg.pr.new/@orval/effect@9e8eb31

@orval/fetch

bun add https://pkg.pr.new/@orval/fetch@9e8eb31

@orval/hono

bun add https://pkg.pr.new/@orval/hono@9e8eb31

@orval/mcp

bun add https://pkg.pr.new/@orval/mcp@9e8eb31

@orval/mock

bun add https://pkg.pr.new/@orval/mock@9e8eb31

orval

bun add https://pkg.pr.new/orval@9e8eb31

@orval/query

bun add https://pkg.pr.new/@orval/query@9e8eb31

@orval/solid-start

bun add https://pkg.pr.new/@orval/solid-start@9e8eb31

@orval/swr

bun add https://pkg.pr.new/@orval/swr@9e8eb31

@orval/zod

bun add https://pkg.pr.new/@orval/zod@9e8eb31

commit: 9e8eb31

Copilot AI 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.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.

Comment thread packages/angular/src/http-resource.ts
@the-ult

the-ult commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

Context: supersedes the schemas.importPath portion of #3707, which is being retired — current master already covers the rest of what it proposed. This PR is self-contained and has no dependency on it or on #3814 / #3815.

@the-ult
the-ult marked this pull request as draft August 5, 2026 13:26

Copilot AI 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.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.

Zod schema imports were built with a hardcoded `.zod` suffix and an
extension taken from `output.fileExtension`. With a custom
`fileExtension` the writers emitted `model/pet.gen.ts` while the
imports pointed at `./model/pet.zod.gen`. The import tail now comes
from `output.schemaFileExtension`, which is the option the writers
use to name the files.

Also addresses review feedback on the shared helper:

- Route the remaining "is package import" derivation in
  `generateImportsForBuilder` through `getSchemasImportPath`.
- Pass the prebuilt schema tag map to `writeSchemasTagsSplit` instead
  of rebuilding it there, so the map really is computed once.
- Rename `getHttpResourceRelativeSchemasPath` to
  `getHttpResourceSchemasModule`, because it can return a package
  specifier.
- Align the parameter order of `resolveSchemaImportDependencies` with
  the existing convention.
- Make `schemaTagMap` optional at every declaration site.
- Trim the JSDoc and comments to the essentials.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@melloware melloware added the angular Related to Angular generation issues label Aug 8, 2026
buildSchemaImportDependencies deduped schema imports before forcing
every export to `{ values: true }` for Zod output. dedupeSchemaImports
keys on the `values` field, so a schema imported once as a value
(e.g. the auto-detected parse schema) and once as a type (e.g. a
named path-params type) survived the pre-force dedupe as two distinct
entries, which the force-to-values step then collapsed into identical
entries without deduping again.

Core's generateDependency already uniques named-import specifiers at
render time, so this never produced invalid duplicate specifiers in
generated files, but the intermediate export list buildSchemaImportDependencies
returns should be self-consistent regardless of that downstream
safety net.

Adds a regression test constructing the type-only + value duplicate
case.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@the-ult
the-ult marked this pull request as ready for review August 14, 2026 22:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

angular Related to Angular generation issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants