You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Webpack not prioritizing the pkg.module for dependencies #5673
Open browser and navigate to webpack-dev-server URL
Observe console logs, should look something like the following:
What is the expected behavior?
Webpack should prioritize the pkg.module field over pkg.browser. Browserify also relies on the pkg.browser but does not have support for ES Modules. Seeing as webpack is able to more intelligently parse the ES Module bundles, it should prioritize that bundle if available.
NOTE: I am aware that you can change the resolve.mainFields to modify this however you'd like. I'm suggesting that the default behavior should be changed
If this is a feature request, what is motivation or use case for changing the behavior?
n/a
Please mention other relevant information such as the browser version, Node.js version, webpack version and Operating System.
The order of these fields is nowhere spec'ed. We chose this order. browser seem to be more specific for in-browser usage, while module is more generic for all targets.
@sokra thanks for the response! Would you be willing to explain your rationale behind:
browser seem to be more specific for in-browser usage, while module is more generic for all targets.
Based on the current state of ES modules in Node (which I understand is still subject to change), I would disagree with that statement. Here's what I'm thinking (tell me if you think I'm crazy):
As a library author I have a bunch of environments that I'd want to target. For the context of this issue, the ones I'm thinking of are:
Browser - minified standalone JS executable
Browser - via CJS
Browser - via ESM
Node - via CJS
Node - via ESM
Now we don't have enough specced fields to cover all of these entrypoints (the only specced one is main), so the community has come up with some non-standard ones (i.e. browser, module, jsnext:main, etc). As I'm sure you're already aware, different bundlers support these non-standard fields to different degrees, but the main, browser, and module fields seem to be agreed on by the big players (i.e. Browserify, Webpack, Rollup, am I missing more that I should be aware of?).
Library author: if my goal is to provide entrypoints for all of these environments, I can do so by doing the following:
Browser(minified standalone JS executable) - This file would typically be thrown on a CDN and served alone. It is contained in the package, but not explicitly listed as an entrypoint in the package.json. Users can npm install our package and directly reference the node_modules dir via script tag. They can also access this binary via a CDN service like https://unpkg.com/
Browser(CJS) - This file would be attached to the browser field of the package.json. Webpack and Browserify support this out of the box, and rollup supports it via plugin.
Browser(ESM) - This file would be attached to the 'module' field of the package.json. Webpack and Rollup support this and are able to do static optimizations to the resulting bundles they generate. AFAICT, Browserify is still waiting for Node support to be resolved before they act (see: Add support for ES6 import syntax browserify/browserify#1186)
Node(CJS) - This file would typically be attached to the main field of the package.json. Optionally, this can be omitted in favor of an index.js at the root of the package.
Node(ESM) - Per this section of the ESM draft, you could accomplish this by doing the following:
By providing an entrypoint of the same name as your CJS node entrypoint but with the .mjs extension and changing the package.json main field as demonstrated below:
By providing an index.mjs, alongside a index.js, at the root of the package and omitting the main field entirely from the package.json.
Consumer of a library: I would think one would always want to get the ES Modules version of a library where available, and fallback to the other versions. We currently have a clean way to delineate between the two* and prioritizing the ES Module build benefits Webpack users by ultimately reducing bundle size, and allowing for auto chunking of dynamic imports (further reducing blocking bundle size).
*: pkg.browser/pkg.module for browser envs and main or index.js/index.mjs for Node
The hard part as a library author is, though Webpack supports the reprioritization of the resolve.mainFields, it is easier to expect all of my users to update Webpack, than it is to ask them all to add and maintain additional code in their Webpack config.
Thoughts?
EDIT: P.S. I'm a tad long-winded, thanks in advance for your understanding :)
In the above case, the node versions can be handled by "./node/index.{js,mjs}" (When node supports modules).
The module version of the browser code is covered off by "module" and would be beneficial for build tools like Rollup and WebPack to use as it gives the developer control over treeshaking, transpilation, minification etc.
The built browser code is used by tools like browserify who don't support modules and need to just pull in a chunk of code.
For users of WebPack, it makes sense to use module over browser to give developers control over how the javascript is passed etc.
Possible Problems:
module is actually node code and can't be used in a browser context (Where as the "browser" field can).
module requires certain plugins to be parsed and work.
My gut says most node modules exports a module field are doing so with the intention of a bundler working on it to produce a browser bundle, but it's a guess at best.
Does WebPack get used for node code?
Is there a general feel for how module is used in packages at the moment, i.e. is it node or browser environments?
Is it better / easier for WebPack to handle browser vs es2015 code in terms of parsing, transpiling, etc?
cc @addyosmani who probably has a gauge on some of this (and is wiser at WebPack than myself)
In my opinion it's wrong to publish browser-only code at the module field. The module field doesn't say anything about the target enviroment. It's like main but with the only difference that ESM is used.
The browser is a field which can be used to provide a browser-only replacement for the package.
If module has higher priority than browser people would publish browser-only code to module, but this will break non-browser builds with ESM support (like webpack compiling for node.js, electron, ...).
It's possible to publish different code for all targets with this package.json:
Last Q and then I'll 🤐 would it make sense for browser to follow the same pattern as main where I could define "browser": "lib/index.browser" and the file extension is tested for .mjs and .js?
would it make sense for browser to follow the same pattern as main where I could define "browser": "lib/index.browser" and the file extension is tested for .mjs and .js?
This will work when all tools support .mjs. webpack will start to support it with version 4.
So I looked into the browser as a map, as has been suggested, and I'm fairly certain that it doesn't work. Webpack seems unable to resolve the module with pkg.browser set to a map.
Activity
fregante commentedon Sep 14, 2017
That appears to be correct. You can use the
browserfield as a map instead:{ "name": "test-module", "version": "1.0.0", "description": "A test module exporting a `pkg.module`, `pkg.browser`, and `pkg.main`", "author": "Josh Crowther <jshcrowthe@gmail.com>", "license": "MIT", "main": "index.js", - "browser": "index.browser.js", + "browser": { + "index.js": "index.browser.js" + }, "module": "index.esm.js", "jsnext:main": "dist/index.esm.js" }sokra commentedon Sep 14, 2017
The order of these fields is nowhere spec'ed. We chose this order.
browserseem to be more specific for in-browser usage, whilemoduleis more generic for all targets.jshcrowthe commentedon Sep 14, 2017
@sokra thanks for the response! Would you be willing to explain your rationale behind:
Based on the current state of ES modules in Node (which I understand is still subject to change), I would disagree with that statement. Here's what I'm thinking (tell me if you think I'm crazy):
As a library author I have a bunch of environments that I'd want to target. For the context of this issue, the ones I'm thinking of are:
Now we don't have enough specced fields to cover all of these entrypoints (the only specced one is
main), so the community has come up with some non-standard ones (i.e.browser,module,jsnext:main, etc). As I'm sure you're already aware, different bundlers support these non-standard fields to different degrees, but themain,browser, andmodulefields seem to be agreed on by the big players (i.e. Browserify, Webpack, Rollup, am I missing more that I should be aware of?).Library author: if my goal is to provide entrypoints for all of these environments, I can do so by doing the following:
Browser(minified standalone JS executable) - This file would typically be thrown on a CDN and served alone. It is contained in the package, but not explicitly listed as an entrypoint in the
package.json. Users cannpm installour package and directly reference thenode_modulesdir via script tag. They can also access this binary via a CDN service like https://unpkg.com/Browser(CJS) - This file would be attached to the
browserfield of the package.json. Webpack and Browserify support this out of the box, and rollup supports it via plugin.Browser(ESM) - This file would be attached to the 'module' field of the package.json. Webpack and Rollup support this and are able to do static optimizations to the resulting bundles they generate. AFAICT, Browserify is still waiting for Node support to be resolved before they act (see: Add support for ES6 import syntax browserify/browserify#1186)
Node(CJS) - This file would typically be attached to the
mainfield of the package.json. Optionally, this can be omitted in favor of anindex.jsat the root of the package.Node(ESM) - Per this section of the ESM draft, you could accomplish this by doing the following:
.mjsextension and changing thepackage.jsonmain field as demonstrated below:index.mjs, alongside aindex.js, at the root of the package and omitting themainfield entirely from the package.json.Consumer of a library: I would think one would always want to get the ES Modules version of a library where available, and fallback to the other versions. We currently have a clean way to delineate between the two* and prioritizing the ES Module build benefits Webpack users by ultimately reducing bundle size, and allowing for auto chunking of dynamic imports (further reducing blocking bundle size).
*:
pkg.browser/pkg.modulefor browser envs andmainorindex.js/index.mjsfor NodeThe hard part as a library author is, though Webpack supports the reprioritization of the
resolve.mainFields, it is easier to expect all of my users to update Webpack, than it is to ask them all to add and maintain additional code in their Webpack config.Thoughts?
EDIT: P.S. I'm a tad long-winded, thanks in advance for your understanding :)
gauntface commentedon Sep 14, 2017
If I'm understanding this correctly, the issue is this:
In the above case, the
nodeversions can be handled by "./node/index.{js,mjs}" (When node supports modules).The
moduleversion of the browser code is covered off by "module" and would be beneficial for build tools like Rollup and WebPack to use as it gives the developer control over treeshaking, transpilation, minification etc.The built
browsercode is used by tools like browserify who don't support modules and need to just pull in a chunk of code.For users of WebPack, it makes sense to use
moduleoverbrowserto give developers control over how the javascript is passed etc.Possible Problems:
moduleis actually node code and can't be used in a browser context (Where as the "browser" field can).modulerequires certain plugins to be parsed and work.My gut says most node modules exports a module field are doing so with the intention of a bundler working on it to produce a browser bundle, but it's a guess at best.
moduleis used in packages at the moment, i.e. is it node or browser environments?cc @addyosmani who probably has a gauge on some of this (and is wiser at WebPack than myself)
sokra commentedon Sep 15, 2017
yes.
In my opinion it's wrong to publish browser-only code at the
modulefield. Themodulefield doesn't say anything about the target enviroment. It's likemainbut with the only difference that ESM is used.The
browseris a field which can be used to provide a browser-only replacement for the package.If
modulehas higher priority thanbrowserpeople would publish browser-only code tomodule, but this will break non-browser builds with ESM support (like webpack compiling for node.js, electron, ...).It's possible to publish different code for all targets with this package.json:
{ "main": "lib/index", "module": "lib/index.mjs", "browser": { "./lib/index.js": "./lib/index.browser.js", "./lib/index.mjs": "./lib/index.browser.mjs" } }lib/index.browser.jslib/index.browser.mjslib/index.jslib/index.mjsNote: It's probably better if you put all environment-specific code in a module and only replace this one with the
browserfield.gauntface commentedon Sep 15, 2017
@sokra that looks like a good option to me.
Last Q and then I'll 🤐 would it make sense for browser to follow the same pattern as
mainwhere I could define"browser": "lib/index.browser"and the file extension is tested for.mjsand.js?Thanks for the info all :)
sokra commentedon Sep 16, 2017
This will work when all tools support
.mjs. webpack will start to support it with version 4.jshcrowthe commentedon Sep 17, 2017
So I looked into the
browseras a map, as has been suggested, and I'm fairly certain that it doesn't work. Webpack seems unable to resolve the module withpkg.browserset to a map.I updated my gist here: https://gist.github.com/jshcrowthe/acea97f8b18e81c0a797137b8a38f1e4
Thoughts?
fregante commentedon Sep 17, 2017
@jshcrowthe I may have used the wrong syntax, perhaps you need to start the paths with
./as shown in thebrowserfield spechttps://github.com/defunctzombie/package-browser-field-spec/blob/master/README.md
jshcrowthe commentedon Sep 17, 2017
Ahhh that did it. Thanks @bfred-it