Skip to content

Add "exports" map to package.json (proper ESM support) #4013

@brawaru

Description

@brawaru

Which package?

Almost all of them, @formatjs/intl in this case specifically.

Describe the bug

I've stumbled upon a problem caused by the lack of exports map and improper ESM packaging by FormatJS packages.

I use import.meta.resolve (import-meta-resolve, actually, but it closely follows the Node.js sources) to find the path to an isolated @formatjs/intl dependency (in pnpm repository without shameful hoisting, I assume this is also a thing in regular npm now too).

Due to lack of exports map in any of the @​formatjs/ (and intl-messageformat) packages are always imported in their CJS form by Node.js, only bundlers pick up the non-standard module property (and that also depends on the configuration).

So what I did in Nuxt is add auto-import from <whatever import.meta.resolve resolves @formatjs/intl to> for defineMessages, which caused any use of defineMessages to auto-import it from node_modules/.../@formatjs/intl/index.js (CJS module). It's should've probably been fine*, since Nuxt has some transpiling for require calls, but then it all crashes because ESM build of tslib (since Nuxt is ESM-first framework, it tries to maximise use of ESM) does not have default export which CJS require would expect.

* I say probably fine, but it probably wouldn't really be fine: I haven't checked, but I might assume that tree-shaking is completely broken, and if you import @formatjs anywhere in your .vue files, then it will most probably bring ESM build of @formatjs/intl, because unlike import.meta.resolve, an import by package name will be resolved by the bundler, which will take the non-standard module property into account during the build.

To Reproduce

Codesandbox URL

Unfortunately Codesandbox does not work well for me, so I created two StackBlitz sandboxes instead:

Reproducible Steps/Repo

Steps to reproduce the behavior:

  1. Do import * as formatJSIntl from "@formatjs/intl"

Expected behavior

package.json would declare proper conditional exports, allowing Node.js to properly determine where the ESM files are located, and import the ESM build. import.meta.resolve would resolve to an ESM module.

Actual behaviour

Due to lack of conditional exports, Node.js resolves to importing the CJS module specified in main field.

Screenshots

× Not applicable.

Environment

Node.js v18.14.0.

Additional context

Relevant issues:

Workaround

For now you can import and resolve @formatjs/intl/lib/index.js instead.


Personal and irrelevant message of appreciation

Thank you for all the hard work on maintaining these packages! ❤️ I have been working on my package vintl for one of the projects I really appreciate. VIntl tries to bring first-class support for Intl in Vue with all the cool jazz like locale changing and such, and it is fully powered by your packages! So big probs for making it all possible. I am currently making a Nuxt module for it, and that's where the issue is coming from actually :D Hopefully I'll be able to finish it all one day

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions