Skip to content
This repository was archived by the owner on Jul 12, 2026. It is now read-only.
This repository was archived by the owner on Jul 12, 2026. It is now read-only.

v19 type declarations incompatible with moduleResolution: "NodeNext" #369

Description

@michealparks

v19 added an exports field to package.json. This activates strict ESM module resolution in TypeScript's NodeNext mode, but the package's .d.ts files still use extensionless relative imports — which NodeNext requires to be explicit (e.g. "./exports.js", "./geometry/index.js").

I'm a member of Threlte and recently ran into this issue when upgrading our Rapier package.

We cannot do a patch workaround since this'll affect our end users.

Root cause

rapier.d.ts is the entry point resolved via the exports field:

import * as RAPIER from "./exports";  // ← no extension                                                                      
export * from "./exports";            // ← no extension                                                                      
export default RAPIER;

With "type": "module" + an exports field, TypeScript treats the package as strict ESM. In NodeNext strict ESM, "./exports" cannot be resolved — TypeScript requires "./exports.js". The export * from "./exports" silently exports nothing, breaking all named imports.

This same pattern repeats throughout every .d.ts file in the package (exports.d.ts, geometry/index.d.ts, dynamics/index.d.ts, etc.).

This worked in v18 because v18 had no exports field, so TypeScript used legacy entry-point resolution which is lenient about extensionless imports inside declaration files.

Reproduction

Any TypeScript project with:

{
  "compilerOptions": {                                                                                                       
    "module": "NodeNext",
    "moduleResolution": "NodeNext"
  }                               
}  
   
import { World, Collider, RigidBody } from '@dimforge/rapier3d-compat'
//       ^^^^^  ^^^^^^^^  ^^^^^^^^^                                                                                          
// Error: Module has no exported member. Did you mean to use 'import X from ...' instead?                                    

Possible fix

Add .js extensions to all relative imports in .d.ts files, and use /index.js for directory imports. For example:

-import * as RAPIER from "./exports";                                                                                        
-export * from "./exports";                                                                                                  
+import * as RAPIER from "./exports.js";
+export * from "./exports.js";                                                                                               
                  
-export * from "./geometry";
+export * from "./geometry/index.js";  

This was the fix that we arrived at: threlte/threlte#1471

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions