Skip to content

fix(core): emit NodeNext .js suffixes in workspace barrel and mutator imports - #3615

Merged
melloware merged 1 commit into
orval-labs:masterfrom
aqeelat:fix/nodenext-barrel-mutator-import-extensions
Jun 16, 2026
Merged

fix(core): emit NodeNext .js suffixes in workspace barrel and mutator imports#3615
melloware merged 1 commit into
orval-labs:masterfrom
aqeelat:fix/nodenext-barrel-mutator-import-extensions

Conversation

@aqeelat

@aqeelat aqeelat commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #3603. Two emission sites still dropped the required .js import suffix under module: "nodenext", reported in #3596.

Changes

Workspace barrel (packages/orval/src/write-specs.ts)

mode: "tags-split" + output.workspace generated an index.ts barrel whose re-exports were built from pathWithoutExtension with no extension re-applied, producing export * from './pets/pets'. The barrel now derives each specifier from the full path, strips output.fileExtension once, and appends getImportExtension(...). Stripping the full extension (not just trailing .ts) also avoids doubling multi-part extensions like .generated.ts.

Mutator import (packages/core/src/generators/mutator.ts)

getImport defaulted to mutator.extension ?? '', so a relative override.mutator under NodeNext lost its suffix unless the user manually pinned extension: '.js'. It now threads tsconfig through and, when mutator.extension is unset, derives the suffix from the mutator file's real extension (path.extname) via getImportExtension.ts.js, .mts.mjs, etc.

Intentionally not changed

Directory re-exports (addOperationSchemasReExport, and the output.schemas / output.operationSchemas directory branches in the workspace barrel) target directories, so a bare .js suffix would be wrong — the correct NodeNext form is /index.js, a separate transformation. These are left as-is with an explanatory NOTE on addOperationSchemasReExport.

Tests

Three new axios configs + focused assertions:

  • petstoreTagsSplitNodeNextWorkspace — workspace barrel under NodeNext
  • petstoreTagsSplitNodeNextMutator — relative mutator under NodeNext (no pinned extension)
  • petstoreTagsSplitWorkspaceGeneratedExt — workspace barrel with .generated.ts (guards against the multi-part doubling regression caught in review)

Full suite green: 5009 snapshot tests, 165 core/orval unit tests, typecheck, lint, and typecheck-generated (16/16 clients). No existing snapshots changed.

Refs #3596.

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling of import file extensions for NodeNext and Node16 module resolution in generated code to ensure compatibility with workspace configurations.
    • Fixed issue where workspace barrel-export generation was not applying correct .js suffixes for module specifiers.
  • Tests

    • Added comprehensive test coverage for NodeNext extension handling in workspace mode with tag-split generation.

… imports

orval-labs#3603 fixed NodeNext schema imports inside the split/tags writers but two
emission sites still dropped the required .js suffix under module: nodenext:

- The workspace index.ts barrel (write-specs.ts) built each re-export from
  pathWithoutExtension and never re-applied getImportExtension, so
  mode: 'tags-split' + output.workspace produced extensionless
  `export * from './pets/pets'`.
- The mutator import (mutator.ts) defaulted to mutator.extension ?? '',
  forcing users to manually pin extension: '.js'.

The barrel now derives the specifier from the full path and strips
output.fileExtension once before appending the import extension, which
also avoids doubling multi-part extensions (.generated.ts). The mutator
now derives the suffix from the mutator file's real extension via
path.extname + getImportExtension when mutator.extension is unset.

Directory re-exports (operationSchemas barrel, output.schemas dir in the
workspace barrel) are intentionally left as-is: they need an /index.js
suffix under NodeNext, a separate transformation tracked elsewhere.

Refs orval-labs#3596.
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fb5f63de-cb8d-4144-a7ed-29d3af87ddad

📥 Commits

Reviewing files that changed from the base of the PR and between 5b239b2 and e7012bd.

⛔ Files ignored due to path filters (11)
  • tests/__snapshots__/axios/petstore-tags-split-nodenext-mutator/endpoints.schemas.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/axios/petstore-tags-split-nodenext-mutator/health/health.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/axios/petstore-tags-split-nodenext-mutator/pets/pets.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/axios/petstore-tags-split-nodenext-workspace/endpoints.schemas.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/axios/petstore-tags-split-nodenext-workspace/health/health.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/axios/petstore-tags-split-nodenext-workspace/index.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/axios/petstore-tags-split-nodenext-workspace/pets/pets.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/axios/petstore-tags-split-workspace-generated-ext/endpoints.schemas.generated.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/axios/petstore-tags-split-workspace-generated-ext/health/health.generated.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/axios/petstore-tags-split-workspace-generated-ext/index.ts is excluded by !**/__snapshots__/**
  • tests/__snapshots__/axios/petstore-tags-split-workspace-generated-ext/pets/pets.generated.ts is excluded by !**/__snapshots__/**
📒 Files selected for processing (4)
  • packages/core/src/generators/mutator.ts
  • packages/orval/src/write-specs.ts
  • tests/api-generation.spec.ts
  • tests/configs/axios.config.ts

📝 Walkthrough

Walkthrough

getImport in mutator.ts now accepts an optional tsconfig parameter and resolves the import extension via getImportExtension when mutator.extension is unset. The workspace barrel re-export path builder in write-specs.ts similarly derives extensions via getImportExtension and correctly strips multi-part fileExtension values. Three new test configs and Vitest tests cover the workspace, mutator, and .generated.ts scenarios.

Changes

NodeNext .js Extension Fix

Layer / File(s) Summary
tsconfig-aware extension in getImport and generateMutator
packages/core/src/generators/mutator.ts
getImport gains an optional tsconfig parameter and resolves the import extension via getImportExtension(path.extname(mutator.path), tsconfig) when mutator.extension is not pinned; generateMutator forwards tsconfig to the helper.
Workspace barrel re-export extension and multi-part fileExtension stripping
packages/orval/src/write-specs.ts
Workspace index re-export construction now calls getImportExtension(output.fileExtension, output.tsconfig) and strips the full configured output.fileExtension before appending the result; JSDoc on addOperationSchemasReExport documents the NodeNext /index.js directory requirement.
Test configs and assertions
tests/configs/axios.config.ts, tests/api-generation.spec.ts
Adds petstoreTagsSplitNodeNextWorkspace, petstoreTagsSplitNodeNextMutator, and petstoreTagsSplitWorkspaceGeneratedExt config entries and three Vitest tests asserting .js-suffixed barrel exports, automatic mutator import suffixes, and correct multi-part fileExtension handling without duplication.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • orval-labs/orval#3361: Introduces and threads getImportExtension through import/schema writers for the same NodeNext/Node16 .js-extension problem this PR extends to mutator and workspace barrel paths.

Suggested labels

bug

Suggested reviewers

  • melloware

🐇 A hop through the imports, extension in paw,
.js at the end — no longer a flaw!
NodeNext said "add it!", the barrel agreed,
Multi-part extensions stripped just as we need. 🌿
The mutator and workspace now dance in the light,
Every re-export resolved exactly right!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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 directly and specifically describes the main fix: emitting NodeNext .js suffixes in workspace barrel and mutator imports, which aligns with the primary objectives of addressing #3603.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 and usage tips.

@melloware melloware added this to the 8.18.0 milestone Jun 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants