-
-
Notifications
You must be signed in to change notification settings - Fork 17
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
Add exports field to package.json #208
Conversation
package.json
Outdated
"require": "./dist/cjs/index.js", | ||
"types": "./dist/types/index.d.ts" | ||
}, | ||
"./dist/cjs/*": { |
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.
Just so I understand this correctly from your comment here, because Browserify ignores exports
, the paths on the left-hand side of exports
(aside from ".") need to be written so that they resolve to real files in the filesystem. We do this so that if the extension uses a library that contains a subpath import e.g., @metamask/key-tree/dist/cjs/some/path
, will continue to work.
However, this seems to imply two things: 1) the responsibility for resolving an import path shifts from the consumer to the library itself, and 2) we cannot create fully ESM-compatible libraries.
Say we publish a library that contains a subpath import of another library. Ideally we could use a shortcut for that import (e.g. @metamask/key-tree/some/path
) and it would resolve to either the CJS or ESM version depending on what the consumer is using (a bundler or straight Node). But we can't do that, so we have to be explicit and use the CJS version (@metamask/key-tree/dist/cjs/some/path
).
But if we do that, wouldn't this mean that in the compiled version of our library, both the CJS and ESM builds will contain this import? So even if in our ESM build, we'd potentially have a mixture of ESM and CJS?
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.
Could you give an example of "a subpath import of another library"? I'm not sure what that means, or why we'd want to support it.
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.
But if we do that, wouldn't this mean that in the compiled version of our library, both the CJS and ESM builds will contain this import? So even if in our ESM build, we'd potentially have a mixture of ESM and CJS?
Yeah, that's the downside of doing this. I thought about doing something like this:
{
"./dist/cjs/*": {
"import": "./dist/esm/*.js",
"require": "./dist/cjs/*.js"
}
}
So even if you import ./dist/cjs/foo
, and your bundler supports exports
, it will resolve to ./dist/esm/foo
. But this feels very hacky.
I wonder if there's a way to support exports
in Browserify through a plugin, but I couldn't find anything about this. That would solve all problems I think.
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.
Could you give an example of "a subpath import of another library"? I'm not sure what that means, or why we'd want to support it.
For example:
utils
importsabi-utils/dist/cjs/foo
snaps-utils
importsutils
.snaps-utils
is now indirectly usingabi-utils/dist/cjs/foo
, even though the bundler may support ESM.
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.
Thanks, my brain somehow read "subpath import" as "subpath export". That makes more sense.
Perhaps we could omit the |
I'm not sure how much work would be involved in this, but if it's not too much, that seems like a good option too. A regex search in the Snaps repo ( |
"require": "./dist/cjs/index.js", | ||
"types": "./dist/types/index.d.ts" | ||
}, | ||
"./package.json": "./package.json" |
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.
This is useful for resolving the root of the package. I'm not sure if it should be added to the module template, but it doesn't hurt I suppose?
We use this in the Snaps repo several times, which doesn't work without this line:
path.dirname(require.resolve('package/package.json'));
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.
Interesting. I guess it doesn't hurt 🤷
@@ -24,7 +32,8 @@ | |||
"build:cjs": "swc src --out-dir dist/cjs --config-file .swcrc.build.json --config module.type=commonjs", | |||
"build:clean": "rimraf dist && yarn build", | |||
"build:docs": "typedoc", | |||
"build:esm": "swc src --out-dir dist/esm --config-file .swcrc.build.json --config module.type=es6", | |||
"build:esm": "swc src --out-dir dist/esm --config-file .swcrc.build.json --config module.type=es6 && yarn build:esm:package", | |||
"build:esm:package": "echo >dist/esm/package.json \"{\\\"type\\\":\\\"module\\\"}\"", |
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.
Apparently Node.js doesn't allow using .js
for both the CJS and ESM version in the same project (??), but you can work around it by adding a package.json
to the ESM folder.
I like this. Maybe it shouldn't be a blocker for this PR? But I do think it's a good idea. |
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.
Looks good to me.
Following discussion in #203, I removed the
exports
field from thepackage.json
because it seemed unnecessary. Unfortunately, not having it breaks when importing a package when Node.js is running in ESM mode, because Node.js doesn't look at themodule
field at all. This causes issues like in MetaMask/key-tree#144.This adds a two exports fields:
.
: The main export, which is used when importing a package from the root. This means that we cannot reliably import from subpaths anymore../package.json
: To allow resolving thepackage.json
path (and importing it, if desired), we also export this.Since Browserify does not support the exports field, we can't use
./*
exports (i.e.,package/foo
resolves topackage/dist/esm/foo
orpackage/dist/cjs/foo
depending on the environment), as it would break in the extension.