Sometimes it can be useful to divide the exports of a package into groups. This can help to avoid namespace conflicts and make it clearer what is being exported.
In this article, I'll demonstrate how to achieve imports like this for your library:
import { bau } from 'mylib/helpers';
import { useWow } from 'mylib/hooks';
It may look like an import from a subdirectory of the library, but it is not.
👋 Hello Subpath Exports!
This can be done with so called subpath exports. They were introduced in Node.js v12.7.0. and configuring them is fairly easy. Here's a two-step guide to implement this in your library:
1. Create the entry files
In your library directory, create separate files for each subpath you want to expose. For example:
// lib/helpers.ts
export const bau = ":-)";
// lib/hooks.ts
export const useWow = () => "🤯";
2. Update package.json
Next, update your package.json
to include the exports field. This provides a modern alternative to the traditional "main" field, allowing multiple entry points to be defined.
{
"exports": {
".": {
"types": "./dist/main.d.ts",
"default": "./dist/main.js"
},
"./helpers": {
"types": "./dist/helpers.d.ts",
"default": "./dist/helpers.js"
},
"./hooks": {
"types": "./dist/hooks.d.ts",
"default": "./dist/hooks.js"
}
}
}
⚠️ When the "exports" field is defined, all subpaths of the package are encapsulated and no longer available to importers. For example, require('pkg/subpath.js') throws an ERR_PACKAGE_PATH_NOT_EXPORTED error.
That's it, build your library and enjoy your organized imports 🍻
TypeScript support
If you consume a package with subpath exports in TypeScript you have to make sure to set moduleResolution
to to node16
, nodenext
, or bundler
. If you use a bundler like vite you likely want to set it to bundler
the most recent version of create vite does set this value. Otherwise you will get an error like this:
Cannot find module '@your/package' or its corresponding type declarations. There are types at '/Users/yourname/Sites/your-app/node_modules/@your/package/dist/main.d.ts', but this result could not be resolved under your current 'moduleResolution' setting. Consider updating to 'node16', 'nodenext', or 'bundler'
https://stackoverflow.com/questions/58990498/package-json-exports-field-not-working-with-typescript
Conclusion
The "exports" field in the package.json
provides a clean and modern approach to define the public interface of your package. It not only allows for organized imports but can also encapsulate subpaths, preventing unwanted access.
For further details, you can refer to the Node.js documentation
I hope this quick guide has helped you. Feel free to reach out if you have any more questions. Also I appreciate it if you like this article, as it really helps me to stay motivated. Thank you!
Happy coding! 🚀
Top comments (0)