TanStack / TanStack/config

vite-config: dts extension rewrite mangles directory-barrel imports ('../utils' → '../utils.js' instead of '../utils/index.js')

Open
#401 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
393
Forks
42
PR merge metrics
No merged PRs in 30d

Description

Describe the bug

tanstackViteConfig's declaration post-processing (ensureImportFileExtension in packages/vite-config/src/index.ts) appends .js / .cjs to every extensionless relative specifier in emitted .d.ts files, without checking whether the specifier targets a directory barrel.

Given a source file that imports from a directory with an index.ts:

// src/adapters/image.ts
import type { GeminiClientConfig } from '../utils' // src/utils/index.ts

the emitted declaration becomes:

// dist/esm/adapters/image.d.ts
import { GeminiClientConfig } from '../utils.js';

but the build only emits dist/esm/utils/index.d.ts — there is no dist/esm/utils.d.ts — so the specifier should have been '../utils/index.js'. Any consumer compiling with skipLibCheck: false fails with:

error TS2307: Cannot find module '../utils.js' or its corresponding type declarations.

The emitted JS is unaffected (rollup/rolldown resolves the barrel itself, pointing at the concrete module, e.g. '../utils/client.js'), so this is invisible at runtime and only breaks the type layer.

Both the ESM (.js) and CJS (.cjs) branches of ensureImportFileExtension have the same problem. Verified present in 0.4.1 and still present in the 0.5.2 source.

Why it goes unnoticed
  • The rewrite happens in beforeWriteFile, after type-checking — afterDiagnostic validates the source compile, and the rewritten output text is never re-checked.
  • Consumers overwhelmingly use skipLibCheck: true, which suppresses TS2307 inside .d.ts files; the affected types silently degrade instead of erroring.
  • publint doesn't catch it (it checks the export map, not deep declaration resolution). @arethetypeswrong/cli does flag it, as InternalResolutionError.
Real-world occurrence

Published packages built with this config ship the broken specifiers today, e.g. @tanstack/ai-gemini@0.19.1:

  • dist/esm/adapters/image.d.tsimport { GeminiClientConfig } from '../utils.js' (only dist/esm/utils/index.d.ts exists)

and @tanstack/ai (dist/esm/activities/generate*/index.d.ts'../middleware.js', where middleware is a directory barrel).

Steps to reproduce
  1. Scaffold a lib using tanstackViteConfig with srcDir: './src', entry ./src/index.ts.
  2. Add src/utils/index.ts exporting anything, and import it from a sibling as from '../utils'.
  3. vite build, then inspect the emitted .d.ts — the specifier is '../utils.js'.
  4. Compile a file importing the package with skipLibCheck: false → TS2307.
Suggested fix

Make the rewrite resolution-aware instead of purely textual. beforeWriteFile receives the output filePath, and the dist tree mirrors entryRoot/srcDir, so for each relative specifier the plugin can check whether the target resolves to a directory (or equivalently, whether <target>/index.d.ts rather than <target>.d.ts is being emitted) and append /index.js in that case:

'../utils'  → '../utils/index.js'  // when utils/ is a directory barrel
'../client' → '../client.js'      // unchanged for plain files

Happy to send a PR if the approach sounds right.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in packages/vite-config/src/index.ts at ensureImportFileExtension and its beforeWriteFile handling. Reproduce the issue with a directory barrel and inspect emitted .d.ts files for both ESM and CJS branches. Done means directory imports become ../utils/index.js or ../utils/index.cjs while plain-file imports remain unchanged, and the reported type-resolution failure is addressed.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript, vite
Domain
build-system
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.