Reputation: 395
i have NPM package that build for ESM and CJS formats in dist folder of package root
also in package.json there is export field like:
{
"exports": {
"./foo": {
"types": "./dist/types/foo.d.ts",
"require": "./dist/cjs/foo.js",
"import": "./dist/esm/foo.js",
"default": "./dist/esm/foo.js"
},
"./bar": {
"types": "./dist/types/bar/index.d.ts",
"require": "./dist/cjs/bar/index.js",
"import": "./dist/esm/bar/index.js",
"default": "./dist/esm/bar/index.js"
},
"./hello/world": {
"types": "./dist/types/hello/world/index.d.ts",
"require": "./dist/cjs/hello/world/index.js",
"import": "./dist/esm/hello/world/index.js",
"default": "./dist/esm/hello/world/index.js"
}
}
}
as you can see foo is a alias for foo.js BUT bar is alias for bar/index.js
and wneh i use this package in other project with TypeScript, tsc dont see bar and hello/world
it see only foo
import foo from 'my-pkg/foo' // WORKS FINE
import bar from 'my-pkg/bar'; // NOT WORKING (cannot find declarations for...)
import helloWorld from 'my-pkg/hello/world'; // NOT WORKING (cannot find declarations for...)
Why does it work like that?
In Node.js without TypeScript it works in ESM and CJS fine without errors.
Only TypeScript can not find type definitions
Upvotes: 3
Views: 3608
Reputation: 4615
In the importing workspace tsconfig.json file, set moduleResolution to "Bundler".
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"jsx": "preserve",
"jsxImportSource": "solid-js",
"types": ["vite/client"],
"noEmit": true,
"isolatedModules": true,
"skipLibCheck": true,
"noUncheckedIndexedAccess": true
}
}
Upvotes: 0
Reputation: 21984
This may not answer your question but is important for what you're trying to accomplish and is too long for a comment.
This isn't going to work the way you've implemented it, at least not at the time I'm writing this. This is unreasonably hard to do, so don't feel bad. And now I show you how deep the rabbit hole goes...
When you don't have an exports
map node will respect your main
and module
entries in your package.json and will treat .js
files the correct way even based on whether they were import
ed or require
d as long as you don't set the type
to "module".
But once you add an exports
map it takes precedence, and all .js files will be treated as either CJS or ESM depending on what you set type
to in package.json, regardless of whether it's the import
or require
path in the exports
map. So that gives you three options:
exports
map, set type
to "commonjs" (or omit it) and use the module
property to point to your ESM entry point.The problem with options #2 and #3 is that the Typescript compiler will not add it for you, and they have repeatedly refused to implement this.
Worth noting that even though the spec says that the extension for ESM imports should be '.js' in practice pretty much every up-to-date tool (e.g. Typescript, Rollup, Webpack, Vite) and up-to-date engine (e.g. V8, Webkit) will accept '.mjs'.
You can hack your way through it with shell scripts to replace the file extension with find
/mv
and sed
to append the file extension on your import statements, you can do it less hackily by using a real parser to achieve the same outcome, or you can use something like this package that does it for you.
If you want to see all this in action here's an example I wrote on GitHub.
And here are some more resources, shout out to Bergi in the comments on the question:
https://nodejs.org/api/esm.html#modules-ecmascript-modules
https://nodejs.org/api/packages.html#dual-commonjses-module-packages
Good luck!
Upvotes: 5