`browser` vs `module` fields in `package.json`
alex35mil opened this issue ยท 24 comments
Do you want to request a feature or report a bug?
I don't think it's a bug, but confusing and inefficient behavior.
What is the current behavior?
When module's package.json
contains browser
, module
& main
fields, webpack is bundling browser
build by default.
What is the expected behavior?
The key point of the module
build is to optimize compiled bundle. And even if a package has hq ES build, it's not being used by default in favor of legacy UMD/IIFE builds. So expected behavior is to use module
build by default.
We may need a field module-browser
...
Another way to solve it nodejs/node-eps#60. Not advocating this approach, just want to let you know.
UPD: there is also esnext prposal. Started to collect all those undocumented package.json fields in small repository
@alexfedoseev found solution, package should be in this form to work properly with wepback
{
"name": "main-module-browser",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"browser": {
"./dist/index.js": "./dist/index.browser.js",
"./dist/index.esm.js": "./dist/index.browser.esm.js"
}
}
Full example https://github.com/stereobooster/main-module-browser-test
Just have similiar problem and happen to found this issue.
@stereobooster What is the difference between index.js
and index.browser.js
? Is it umd
and cjs
or something else?
@DrSensor here is full spec https://github.com/defunctzombie/package-browser-field-spec
it doesn't specify types of the packages. One thing that matters is if code uses browser-specific (example: Dom) or server-specific (example: filesystem) features.
Does this mean that currently webpack's order of importing is browser
> module
> main
?
I would also prefer module > browser > main.
I would also prefer module > browser > main.
That would break things. What happens when you need to load WebCrypto for the browser and node's crypto for node?
Or similar naunced APIs - atob
, Buffer
, etc.
@solderjs I only define module
and main
fields on all my package.json files.
module:
It works perfect, because webpack will always use module
, which I point towards files bundled with esm (ES Module) format (eg. export default something
).
main:
My main
fields point to commonjs bundled files, which are automatically chosen when someone uses my package in a nodeJS environment I believe.
browser:
I'm not sure why the browser
field exists in the first place... ๐ง
conclusion:
I was always under the impression that I was doing to right... But now I don't know what's right and what's less right, and how I should best do it anymore. ๐
@mesqueeb for code that can not work in a browser (using node-only APIs, for example), the browser field lets bundlers automatically select an alternative implementation (using browser-only APIs, for example). When writing universal code, you never need the browser field.
Note that node itself will not have any "module" field, so anyone currently using that field is already incurring technical debt.
@ljharb thanks for the insight.
I do however feel that it's important to use the "module" field. Because, exactly as you point out:
Node itself doesn't (use) the "module" field
since Node uses the "main" field, we should export our libraries with a commonJS export method in the file we point to in the "main" field. Because Node has difficulties reading ES Module type exports (export default
).
So the reason they added the "module" field in package.json is for us devs to be able to also use the ES6 import export method directly without having to rely on commonJS, while still keeping backwards compatibility support to Node by setting a commonJS exported index file on "main".
I hope you understand what I mean. ๐
I've a question: If I've a node package that has module & main fields and I've an app using this package while bundling to target node.
-
Does using "require" from the package in my app goes to module or main?
-
Does using "import" from the package in my app goes to module always?
Or it's always module if exist and then main? Because I know that webpack can "require" an ESM module (or maybe I'm wrong).
Always module
Thanks @sokra! ๐๐ป
What is the guidance with node now providing an exports
field? Do we still continue to use module
or browser
as previous or do we use them as a properties of exports
?
AFAICT webpack@5
will use exports
if it is specified and fall back to module
and browser
otherwise, webpack@4
will continue to use module
and browser
, and for the time being there are no plans in rollup
to support exports
.
So I think for quite a while package maintainers will have to support both if they do want to make use of exports
but don't want to break a considerable amount of tooling.
@ctavan Thanks for the answer. I have to say though, this esm transition situation really really sucks! Emotions are flaring up everywhere (not just with this issue, like on the other thread you link) and no end to the confusion for developers in sight!!!!!!
Hi everyone, what about prioritizing the "browser"
field on webpack in case the filename is .mjs
?
Please give it a read to this thread - this seems to be a generalized problem across all modern ESM bundlers: vitejs/vite#1154. Maybe this is a good time to get a consensus to define the best approach for this problem?
Here is a repository I've prepared demonstrating the problem: https://github.com/endel/esm-browser-node
Not all browsers that people support, support ESM.
The "browser" field speaks to a content swap. It has nothing to do with format specification. If it did, then all browser
entries would most likely be CommonJS (re browserify) but UMD files emerged as a convention.
There should definitely be some detection as to what the browser
entry is pointing at. The .mjs
extension should be enough evidence โ or exports[key].browser
if webpack is parsing that already.
But again, browser is about content... either through polyfills or implementation changes, the "browser" entry is the browser/non-Node.js variant of XYZ module behavior.
I recognize a lot of folks just want ES modules to work in their builds (at this point there's few in the JS ecosystem who don't feel the ES modules transition pain), but using module
over browser
is an incomplete solution. Really what you want is the ability to specify ES and CommonJS builds for both Node.js and Browser environments.
It is really important that Webpack retain the ability to use a different set of built files (within the imported package) as compared to server side (Node.js, aka main
and module
).
Personally I think the best option is to introduce browser-module
. Preference order would be:
browser-module
browser
module
main
If one of the options doesn't exist, fall to the next.
They can be combined, which is actually parts of the exports
design intention, and I think webpack supports this already:
{
"exports": {
"browser": {
"import": "./browser.mjs",
"require": "./browser.js"
},
// ...
}
Phew- for any unfamiliar: https://webpack.js.org/guides/package-exports/#providing-different-versions-depending-on-target-environment.
Thanks @lukeed
I think we can close it in favor exports
, also good answer #4674 (comment) for legacy projects, feel free to feedback
And docs https://webpack.js.org/guides/package-exports/#providing-different-versions-depending-on-target-environment, anyway feel free to improve it
I think we can close it in favor
exports
, also good answer #4674 (comment) for legacy projects, feel free to feedbackAnd docs https://webpack.js.org/guides/package-exports/#providing-different-versions-depending-on-target-environment, anyway feel free to improve it
Hi ! I don't get your snippet, how does it work ? It choose randomly ?