-
Notifications
You must be signed in to change notification settings - Fork 20.6k
WIP (example) Core: Define the exports
& module
fields in package.json
#5122
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
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 |
"main": "dist/jquery.js", | ||
"module": "dist/jquery.mjs", |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it’s still the case that many servers/CDNs don’t serve the proper MIME type for
.mjs
files
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
&default
for 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
module
is 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