Reputation: 28380
I want to take advantage of the new-ish "exports" feature of Node.js/package.json
so that I can do the following:
"exports": {
".": "./dist/index.js",
"./foo": "./dist/path/to/foo.js"
}
and users can do the following:
import { foo } from 'my-package/foo';
TypeScript 4.5 should support the "exports" field, yet it does not seem to work. I am building a simple package using TS 4.5.2, and I am consuming that package in a project using TS 4.5.2. I have looked at other SO questions and this GitHub thread and this bug report but can't seem to find a consensus on the issue and whether it should work today.
I am still able to import using the more verbose syntax:
import { foo } from 'my-package/dist/path/to/foo.js';
I have also tried the object notation for exports, to no avail:
"exports": {
".": { "require": "./dist/index.js", "import": "./dist/index.js" },
"./foo": { "require": "./dist/path/to/foo.js", "import": "./dist/path/to/foo.js" }
}
Is this feature ready to be used with TypeScript projects today? If yes, what am I missing? Specifics about tsconfig would be useful for both the source project and consuming project. The TS compiler complains about node12
/nodenext
being used for either the module
or moduleResolution
fields (I am definitely using TS 4.5.2).
Upvotes: 56
Views: 52120
Reputation: 28380
Typescript 4.7 FTW! Here's the breakdown:
Node.js has supported exports
since v12.7.0 (Jul. 2019)
When I asked this question (Dec. 2021), NodeJS had supported the exports
field for nearly 2.5 years. It seemed reasonable to assume that Typescript supported it.
The latest version of TypeScript at that time (4.5) did not support the exports
field.
This was particularly confusing because the TS 4.5 beta announcement said that it would supportpackage.json
exports.
Typescript 4.7 (Jun. 2022) finally supported package.json
exports
Using typesVersions
in package.json
is not the solution
Several people suggested using typesVersions
- but that's a completely separate feature which is specific to TypeScript only (read more about it here). The exports
field in package.json
is a feature of node and should work with any npm module.
So, if you have a Typescript project and you want to be able to import a package which uses package.json "exports", you will need to do the following:
tsconfig
should be using moduleResolution
of node16
or nodenext
.
moduleResultion
if you are using module
with a value of CommonJS
, ES2015
, ES6
, ES2020
, or ESNEXT
Upvotes: 38
Reputation: 8620
TypeScript only respects export maps in package.json
if you use "moduleResolution": "NodeNext"
(or "Node16"
) instead of the widespread "moduleResolution": "Node"
. (I guess "moduleResolution"
defaults to the same value as "module"
, but it's hard to find documentation of this?
Update: in TypeScript 5 there is also a "moduleResolution": "bundler"
option that also respects export maps. The differences between the two are:
"bundler"
never requires file extensions on relative paths in imports"NodeNext"
will match the "node"
export condition in export maps, but "bundler"
won't (not documented yet in the TSConfig reference...haven't managed to find the issue thread where I confirmed this with the TypeScript team)See: https://www.typescriptlang.org/docs/handbook/esm-node.html and https://www.typescriptlang.org/tsconfig#moduleResolution
A number of TS libraries out there (including many of Microsoft's own) have errors with "moduleResolution": "NodeNext"
right now because of things like relative imports without explicit file extensions.
Upvotes: 11
Reputation: 107
I've been looking to use nested folders for a design system package we created at Pipefy, and after deep research, I found how to do it.
The package exports React components, and we use to import them like this import { Button } from '@mypackage/design-system;
Later on, we've added tokens to our design system library, like Colors
, Spacing
, and Fonts
, but we don't want to import everything from the index, it isn't productive.
After some exhaustive research, I found how to export nested folders using TypeScript. My purpose is to use tokens like this import { Colors } from '@mypackage/design-system/tokens;
To use your TypeScript lib like this you should use the typesVersions
inside the package.json
file.
Here I use it like this
"typesVersions": {
"*": {
"index": [
"lib/components/index.d.ts"
],
"tokens": [
"lib/tokens/index.d.ts"
]
}
},
It worked like a charm for me, it would work for you too!
Upvotes: 6
Reputation: 47614
Without knowing what error you are getting, or in what other way TypeScript doesn't seem to be working for you (not sure why you would not want to share such crucial information), I can tell that your exports
section appears to be missing types information. Typically, if your .d.ts files were located next to their respective .js files, your exports section would look like this:
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./foo": {
"types": "./dist/path/to/foo.d.ts",
"default": "./dist/path/to/foo.js"
}
}
Upvotes: 14