Skip to content

Give relative imports the extension nodenext requires - #205

Draft
bmesuere wants to merge 1 commit into
mainfrom
fix/nodenext-types
Draft

bmesuere wants to merge 1 commit into
mainfrom
fix/nodenext-types

Conversation

@bmesuere

@bmesuere bmesuere commented Aug 19, 2026

Copy link
Copy Markdown
Member

Fixes the pre-existing issue reported in #201. A consumer on "moduleResolution": "nodenext" or "node16" got no types at all from this package:

error TS2305: Module '"unipept-visualizations"' has no exported member 'Treeview'.
error TS2305: Module '"unipept-visualizations"' has no exported member 'StringUtils'.

Worth restating because the name misleads: node16 is not about Node 16 the runtime. It is TypeScript's mode for Node's real ESM resolution, and nodenext is the setting currently recommended for Node-targeted projects. The mode that used to cover this package is node/node10, which TypeScript 7 removed and which this repository itself stopped using in #196. So the set of consumers this affects grows rather than shrinks.

The fix

Those modes require a file extension on relative specifiers, and TypeScript writes declaration specifiers exactly as the source spells them. So the fix has to be in the source, not in the emit:

-export * from "./visualizations";
+export * from "./visualizations/index.js";

141 specifiers across 50 files, all mechanical: .js appended, and directory imports naming their index explicitly. The .js is correct even though the file on disk is .ts, because the specifier describes the emitted file. Nothing else changed, and the bundle is identical in size.

Why not bundle the declarations instead

The tidier-looking option is to have vite-plugin-dts emit one self-contained index.d.ts, which would leave no relative specifiers to resolve. I tried it and it is not available to us:

  • the option is bundleTypes, not rollupTypes as I guessed in Declare the package entry point with an exports map #201; it was renamed when vite-plugin-dts v5 moved onto unplugin-dts
  • it requires @microsoft/api-extractor, which carries its own bundled TypeScript 5.9.3 and warns that the project's 6.0.2 is newer than it supports
  • it then fails the build outright: Internal Error: Unable to follow symbol for "HTMLElement"

So it is blocked on api-extractor catching up with TypeScript 6. If that happens, this change could be reverted in favour of the one-line option, but there is no need to: the extensions are correct on their own terms and cost nothing.

Manual testing

Nothing user-visible changed. examples/ still work as before.

What I verified against a real install

Packed the branch with npm pack and installed the tarball into a scratch project:

moduleResolution before after
nodenext 2 errors types resolve
node16 2 errors types resolve
bundler types resolve types resolve

import("unipept-visualizations") still resolves at runtime with all 22 exports. This was tested on a branch off main, so without the exports map from #201: the two changes are independent and neither depends on the other.

In this repository: lint, typecheck, build and 62/62 tests pass. Two files in the heatmap use CRLF line endings; the codemod preserves them, so the diff is 142 lines rather than a whole-file rewrite.

No specifier without an extension is left in src.

@bmesuere bmesuere added the bug label Aug 19, 2026
@bmesuere bmesuere self-assigned this Aug 19, 2026
@bmesuere

Copy link
Copy Markdown
Member Author

Leaving this one on hold to wait for the simpler fix to become possible.

A consumer on moduleResolution "nodenext" or "node16" got no types from
this package at all: every export resolved to nothing, because the
emitted declarations re-exported each other with extensionless relative
specifiers and those modes require the extension. Only "bundler" worked,
and the legacy "node10" that used to cover this was removed in
TypeScript 7.

TypeScript writes declaration specifiers exactly as the source spells
them, so the fix is in the source: 141 relative specifiers across 50
files gain the .js extension, and directory imports name their index
explicitly. The convention is that the specifier describes the emitted
file, so .js is correct even though the file on disk is .ts.

Nothing about the bundle changes; it is byte identical in size and the
tests are untouched.

The alternative was vite-plugin-dts bundling the declarations into a
single file, which needs api-extractor. That is not usable here: it
carries its own TypeScript 5.9 and fails outright against this project
on 6.0.

Co-Authored-By: Claude Opus 5 <[email protected]>
@bmesuere

Copy link
Copy Markdown
Member Author

Parking this rather than merging: the 50-file codemod is a lot of churn for a fix that becomes a one-liner once the tooling catches up.

What unblocks the simpler fix

vite-plugin-dts can bundle the declarations into a single self-contained index.d.ts, which leaves no relative specifiers to resolve and makes this whole PR unnecessary:

dts({ insertTypesEntry: true, bundleTypes: true })

It needs @microsoft/api-extractor, which today carries its own bundled TypeScript 5.9.3 and fails against this project on 6.0.2:

Analysis will use the bundled TypeScript version 5.9.3
*** The target project appears to use TypeScript 6.0.2 which is newer than the bundled compiler engine
Error: Internal Error: Unable to follow symbol for "HTMLElement"

How to check whether it has cleared

npm i -D @microsoft/api-extractor
# add bundleTypes: true to the dts() options in vite.config.ts
rm -rf dist && npm run build
find dist -name "*.d.ts" | wc -l   # 1 means it worked; 53 means the option was ignored

Then confirm the actual symptom is gone by packing the result and type-checking a consumer under moduleResolution: nodenext.

What stays broken until then: consumers on nodenext or node16 get no types from this package. bundler, which Vite and most modern setups use, is unaffected.

Note the option is bundleTypes, not rollupTypes — it was renamed when vite-plugin-dts v5 moved onto unplugin-dts.

@bmesuere
bmesuere force-pushed the fix/nodenext-types branch from 7675545 to c132701 Compare August 19, 2026 07:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant