-
Notifications
You must be signed in to change notification settings - Fork 4
Description
Summary
In strict ESM environments (Next.js / Node 20 with TS "moduleResolution": "NodeNext"), @creit.tech/sorobandomains-sdk fails to load because the published ESM contains extensionless relative imports (e.g., ./types instead of ./types.js). Node’s ESM resolver does not infer file extensions unless subpaths are covered by a package.json exports map.
Package / Repo
- npm:
@creit.tech/sorobandomains-sdk - version: <please fill, e.g. 0.x.y>
- repo:
Creit-Tech/sorobandomains-sdk-js
Environment
- OS: Windows 11
- Node: 20.x
- Package manager: npm
- Framework: Next.js (App Router, ESM)
- TypeScript config highlights:
"module": "ESNext""moduleResolution": "NodeNext""verbatimModuleSyntax": true
- Also reproducible when running scripts with
ts-node -P tsconfig.scripts.json
Steps to Reproduce
- Create a Next.js or Node ESM project using TypeScript with
"moduleResolution": "NodeNext". - Install
@creit.tech/sorobandomains-sdk. - Import the package:
import { SorobanDomainsSDK } from "@creit.tech/sorobandomains-sdk";
- Start the app or run the script.
Actual Behavior
Node throws ERR_MODULE_NOT_FOUND for an internal file without a .js extension, for example:
Error [ERR_MODULE_NOT_FOUND]: Cannot find module ".../node_modules/@creit.tech/sorobandomains-sdk/dist/types"
imported from ".../node_modules/@creit.tech/sorobandomains-sdk/dist/index.js"
Expected Behavior
The package should load in strict ESM environments without custom resolvers, bundler-specific shims, or transpiler hacks.
Why This Happens
- Node’s native ESM loader requires explicit file extensions for relative imports (e.g.,
./x.jsinstead of./x). - Alternatively, a robust
package.jsonexportsmap can present stable entry points so consumers aren’t relying on internal relative paths.
Proposed Fix (either approach works)
- Add
.jsextensions to all relative ESM imports in the published files (and in TS sources when using"moduleResolution":"NodeNext"; TypeScript will emit the correct.jsindist):
- import { Foo } from "./types";
+ import { Foo } from "./types.js";- Provide a robust
exportsmap inpackage.jsonso consumers resolve only through defined entry points:
{
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs",
"default": "./dist/index.js"
}
}
}Adjust paths/filenames to match the actual build output; add subpath exports if you intend to support them.
Workarounds Tried
- Custom resolvers/transpilation: possible but undesirable for consumers.
- Forcing CommonJS: defeats the purpose of ESM in frameworks like Next.js.
I am here for you team!
Happy to open a PR adding either the .js extensions or an exports map—whichever you prefer. Thanks for considering!