Conversation
`module` is needed for Parcel which still doesn't support `exports`: parcel-bundler/parcel#4155
exports & module fields in package.jsonexports & module fields in package.json
|
An example script showing how it works in Node.js: 'use strict';
// Get a `window` implementation from jsdom and assign it to the global.
import jsdom from 'jsdom';
const { JSDOM } = jsdom;
const { window } = new JSDOM(`<!DOCTYPE html><p>Hello world</p>`);
globalThis.window = window;
// Log the jQuery slim version
const jQuerySlim = (await import('jquery/slim')).default;
console.log('jQuery slim version:', jQuerySlim?.fn?.jquery);
// Log the contents of the internal cssExpand array.
const cssExpand = (await import('jquery/src/css/var/cssExpand.js')).default;
console.log('jQuery cssExpand array:', cssExpand);To run this example, save it as |
| "./amd/*.js": "./amd/*.js" | ||
| }, | ||
| "main": "dist/jquery.js", | ||
| "module": "dist/jquery.mjs", |
There was a problem hiding this comment.
Parcel doesn't support package.json exports so this field is still needed:
parcel-bundler/parcel#4155
| "./src/*.js": "./src/*.js", | ||
| "./amd/*.js": "./amd/*.js" |
There was a problem hiding this comment.
These entries are tricky. Technically, we don't guarantee anything about files in those directories, they can come and go even in patch releases. On the other hand, we've been telling people that for advanced cases they can use our modules, taking into account these restrictions.
src contains jQuery source written in ES modules, amd those modules transpiled to AMD as jQuery 3.x & older used AMD as the source format and we wanted to still make it available to people.
The modules in src are compatible with direct usage in a browser without any bundler as well:
<script type="module">
import jQuery from "./src/jquery.js";
/* do someting with jQuery */
</script>| ".": { | ||
| "node": { | ||
| "module": "./dist/jquery.mjs", | ||
| "import": "./dist/jquery-node-wrapper.mjs", | ||
| "default": "./dist/jquery.js" | ||
| }, | ||
| "browser": "./dist/jquery.mjs" | ||
| }, | ||
| "./slim": { | ||
| "node": { | ||
| "module": "./dist/jquery.slim.mjs", | ||
| "import": "./dist/jquery-node-wrapper.slim.mjs", | ||
| "default": "./dist/jquery.slim.js" | ||
| }, | ||
| "browser": "./dist/jquery.slim.mjs" | ||
| }, |
There was a problem hiding this comment.
This part mostly corresponds with a recommendation from Webpack from:
https://webpack.js.org/guides/package-exports/#providing-commonjs-and-esm-version-stateful
|
@guybedford @jkrems @MylesBorins could you have a look? |
| "import": "./dist/jquery-node-wrapper.mjs", | ||
| "default": "./dist/jquery.js" | ||
| }, | ||
| "browser": "./dist/jquery.mjs" |
There was a problem hiding this comment.
You probably want to add the require and default in case, that should cover most cases. I'm not sure if "browser" is needed if you already have default, but also can't hurt.
| "browser": "./dist/jquery.mjs" | |
| "require": "./dist/jquery.js", | |
| "browser": "./dist/jquery.mjs", | |
| "default": "./dist/jquery.mjs" |
There was a problem hiding this comment.
I would also recommend flipping around the file extensions. Use .cjs for the CommonJS version and .js for all other versions, and add "type": "module" to the package.json. I think it’s still the case that many servers/CDNs don’t serve the proper MIME type for .mjs files, so the browser version of jQuery (even the browser ESM version) should absolutely have a .js file extension for maximum compatibility.
There was a problem hiding this comment.
I think it’s still the case that many servers/CDNs don’t serve the proper MIME type for
.mjsfiles
It's been years since I've since this issue, not sure if it's worth it. (One could argue that we should be using .mjs to maximize compatibility because Node.js v10.x never got support for "type": "module"). Note that the .js files are not CJS but AMD FWIW.
There was a problem hiding this comment.
It’s been years since I’ve since this issue, not sure if it’s worth it.
I think jQuery’s enormous scope justifies being careful. There’s no benefit to .mjs/.js over .cjs/.js. Node 10 doesn’t support "exports" at all; if you want to explicitly provide a Node 10-and-below ESM build, an .mjs file somewhere just for those consumers could be included. I would still save all the others as .js files as .js works everywhere.
There was a problem hiding this comment.
The only reason we test on Node 10 is our Jenkins instance cannot handle anything newer. I'd like to get us away from it, though, so that shouldn't hopefully be a blocker.
Our goal is to only support Node.js versions that are supported by upstream, i.e. now 14+.
There was a problem hiding this comment.
@GeoffreyBooth I'd like to understand where every entry comes from. Why do we need require, browser & default for non-Node.js cases?
There was a problem hiding this comment.
I’d like to understand where every entry comes from
They’re documented here: https://nodejs.org/api/packages.html#conditional-exports
Why do we need
require,browser&defaultfor non-Node.js cases?
Others should chime in, especially @guybedford, but my understanding is that while Node reads the conditions as appropriate at runtime, the other conditions (such as "browser") are intended for bundlers. So when a build tool is creating a browser build, it’ll use the jQuery file listed under the "browser" field.
Build tools can also build for Node, of course, in which case they’ll read the other conditions too. When preparing a Node build, if jQuery is referenced via a require() call, the "require" condition will be chosen, and so on. When nothing more specific matches, "default" is chosen (which is why it must always be last).
There was a problem hiding this comment.
Why do we need
require[…] for non-Node.js cases?
E.g. if someone uses require() to write browser focused code. If you set browser to point to the ESM file, depending on the builder it may or may not work (i.e. it would work only if the bundler supports requiring ES modules).
| "import": "./dist/jquery-node-wrapper.slim.mjs", | ||
| "default": "./dist/jquery.slim.js" | ||
| }, | ||
| "browser": "./dist/jquery.slim.mjs" |
There was a problem hiding this comment.
Same here.
| "browser": "./dist/jquery.slim.mjs" | |
| "require": "./dist/jquery.slim.js", | |
| "browser": "./dist/jquery.slim.mjs", | |
| "default": "./dist/jquery.slim.mjs" |
|
Shouldn't this be put in the 4.0.0 milestone? |
|
@ollie-iterators We have #4592 for tracking purposes; this PR is a POC of what the compiled bundles could look like; the final code will not include any built files and will need code to actually generate them so it will be a different PR. |
|
I created a PR adding One important change from this POC is that I ended up just defining a single entry point for Node.js. This is because our compiled ESM version still only has a default export with the value of |
Summary
moduleis needed for Parcel which still doesn't supportexports: parcel-bundler/parcel#4155I prepared this after reading https://nodejs.org/api/packages.html and https://webpack.js.org/guides/package-exports/. Important information is:
This draft contains files as they'd exist in a final package. Of course, the final PR would be different, files would be generated during the build and only included in published packages but I included them in the PR so that it can be experimented with.
Ref gh-4592
Checklist