-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: for platforms supporting conditional exports, when importing tiny-invariant from an ESM module, serve an ESM build #145
Conversation
Thanks heaps! I will take a look at this first thing next week |
package.json
Outdated
@@ -54,8 +60,10 @@ | |||
"build:clean": "rimraf dist", | |||
"build:flow": "cp src/tiny-invariant.js.flow dist/tiny-invariant.cjs.js.flow", | |||
"build:typescript": "tsc ./src/tiny-invariant.ts --emitDeclarationOnly --declaration --outDir ./dist", | |||
"build:typescript:node16": "tsc ./src/tiny-invariant.ts --emitDeclarationOnly --declaration --outDir ./dist/node16", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be called "node16" or "esm"? Soon we will have Node 18 as standard, and having node16 in the path might be confusing for folks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
true, I renamed "node16" to "esm" :)
I'll release this change as a |
@@ -0,0 +1,3 @@ | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmmm.. can you please explain what this file is for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The NPM script build:copy-esm-packagejson
puts this file into the folder ./dist/esm
.
It's sole purpose is that Node.js interprets ./dist/esm/tiny-invariant.js
as an ESM module.
Dr. Axel Rauschmayer wrote a blog post about "Hybrid npm packages", and Option 1 is the one we want to enable ("ESM and CommonJS are both bare imports"): https://2ality.com/2019/10/hybrid-npm-packages.html#option-1-(experimental%2C-needs-conditional-exports)%3A-esm-and-commonjs-are-both-bare-imports.
Note that this blog post was last updated in 2019 and the things mentioned as experimental are not experimental anymore.
I am leaning towards merging this to get people unstuck. I am also thinking of doing a |
What is your concern about merging this? I would probably stick with a CJS build because I think with the conditional export ( |
Hey sorry, been busy today. I plan on merging and releasing this soon. My hesitation has been due to my lack of familiarity with the details of this problem domain. |
Hey, no problem, for me it's not urgent. |
...I just did a big update of both the title of this PR and the description. @alexreardon please read it through once again, I discovered one risk (outlined in the description). I am still in favor of merging this PR. I expect very little to no problems with existing codebases; it fixes #144; and an ESM build is then available to consumers of this package. The alternative would be to not merge this PR and instead fixing #144 by modifying the types of this package. I actually tried that at the beginning but could not get it to work, the compilation problem would just stay. |
I am now back at work so I can help see this through. Thank you for your thoroughness @pkerschbaum. I think reaching into So right now I am thinking:
An alternative courseI have been thinking of moving all of my packages to esm only. Moving to esm only should:
What do you think @pkerschbaum ? |
👍 👍 👍 👍
|
I have seen that ESM Readme of sindresorhus many times and I really don't like the "convert your own codebase to ESM or just stick with the old version" attitude. However, in the case of In case of the ESM-only approach, we could close this PR and I could create one which just outputs ESM using You decide which direction we go 😄
That's true but I think @alexreardon just wants to ensure that no platform/bundler out there breaks because of this change (and because of them not having a proper lockfile...).
This does not work (I just tried it to confirm that). Package entry points (the "exports" property of package.json) is taken into consideration when Node.js (or other platforms/bundlers) resolves a "bare module specifier". But never after that. See https://2ality.com/2022/01/esm-specifiers.html#resolving-module-specifiers-in-node.js and https://nodejs.org/api/packages.html#package-entry-points.
I agree with that, but I would stick with a default export of |
Okay, I'll aim to merge and release tomorrow |
@pkerschbaum @theKashey I have released this change in https://github.com/alexreardon/tiny-invariant/releases/tag/v1.3.0 |
Fixes #144
Current Situation
When
tiny-invariant
is imported from an ESM module in Node.js, like this:...the CJS build of
tiny-invariant
is served.Here's why 👇
tiny-invariant
innode_modules
and look intopackage.json
.exports
, but since this is not present in the currently published version oftiny-invariant
, it looks at the propertymain
. This property refers to"dist/tiny-invariant.cjs.js"
. From this point on it is clear that this file will get executed..js
is ambiguous, it can contain either CJS or ESM code. To know in which module mode Node.js should run this file, it will search for the "closest"package.json
file and look at thetype
property. There is nopackage.json
in the folderdist
, so it looks one level up, findingpackage.json
of tiny-invariant itself.type
is not set, so Node.js interprets the file as a CJS file.Goal of this Pull Request
This pull requests adds an ESM build which is used by Node.js and any other platform respecting "conditional exports" when importing
tiny-invariant
from an ESM module.This fixes #144.
It uses the technique outlined by 2ality.com/2019/10/hybrid-npm-packages.html#variant%3A-avoiding-.mjs, with the only difference that
tiny-invariant
is still CJS by default and ESM by opt-in.The same technique is used by
safe-stable-stringify
.Risks
This should be almost backwards-compatible, because:
"exports"
field will still work as before,"exports"
field will use the new file only if an ESMimport
is used to consume the package. And then, it will get the same JS as it would have before when it consumeddist/tiny-invariant.esm.js
.There's one catch though I am aware of: Reaching into `dist` might not be possible anymore 👇
exports
is now defined as:Once the property
exports
is present inpackage.json
, platforms respecting that property will refuse to import anything not specified inexports
.Thus if some codebase reaches into
dist
like this (and the platform respectsexports
):...this will not work anymore.
We could adapt
exports
such that everything fromdist
is exported, but I wonder if we should do that.This potential problem can affect only modern codebases respecting
exports
, and I would expect that almost everyone is importingtiny-invariant
with the "bare module specifier":Additional Notes
module
ofpackage.json
is already set, pointing to a JS file containing ESM code. While this looks like an ESM build is already configured fortiny-invariant
, this property is not used by Node.js, see https://stackoverflow.com/a/42817320/1700319.