krutoo
krutoo

Reputation: 395

Why TypeScript dont see exports of package? (with module: CommonJS and moduleResolution: Node)

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

Answers (2)

Mikael Lirbank
Mikael Lirbank

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

Jared Smith
Jared Smith

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 imported or required 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:

  1. Don't use an exports map, set type to "commonjs" (or omit it) and use the module property to point to your ESM entry point.
  2. Change all your esm files to .mjs (and add .mjs to all your imports)
  3. Change all your cjs files to .cjs (and add .cjs to all your requires)

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

Related Questions