- Nathan Bierema
- Mateusz Burzynski
- Mark Erikson
- Mateusz: what do you want besides "ship widely compat code?" What preferences?
- Mark: don't care about
.mjs
as an output, really. Mostly just don't want to have to rewrite all imports to be"createSlice.js"
- Mateusz: Could do some changes like rewriting imports as a post-build script
- Mateusz: TS team kinda caved and will allow
.ts
extensions if you add a flag. Not sure what the output will be. Original reason for.js
extensions was not wanting to transform source paths - "write what will be executed". So, in 5.0 they'll allow you to use.ts
, question is whether they're going to rewrite that or not. - Mateusz: tried switching XState repo over, Jest had trouble with the
.js
extension due to mappings. Had to write a resolver plugin, and still had issues with.jsx/tsx
too. So, not all tools might understand this depending on what the tool is looking for.
- Mark: don't care about
- Mateusz: "what does it mean to ship ESM today?"
- Baseline: Node should be able to load files with
require()
andimport
, but you can evenimport
CJS files (as long as it has named exports). Point is, you technically don't have to ship ESM for node, it can get loaded, and by shipping both formats you can run into the "dual-package hazard". (Possible to isolate shared code, but it has to be CJS. But, not every project suffers from that concern. Other issue is that files/deps can get double-imported, like two copies of Lodash.) So, shipping dual packages does make it more hazardous. - Mateusz: Probably do want to ship ESM files for bundlers, and that's where "export conditions" comes in.
exports.module
mostly understood by bundlers. ESM technically "async", but irelevant with bundlers because they're in control of the module graph and flattening things anyway. Some tools might even deduplicaterequire/module
conditions? Not sure. Webpack 5 tries to be strict and follow Node semantics. So, by usingexports.module
, I as package author am being more clear about what I want tools to use. - Mateusz: So, technically, don't have to ship ESM just to get Node to work. So, to a certain extent, shipping ESM is about "purity". For now, Emotion is sticking with
require
, and bundlers will load ESM files.
- Baseline: Node should be able to load files with
- Mark: RTK ships pre-bundled versions in various formats (CJS/ESM/UMD). 1.9.x uses ESBuild+TSC (to compile to ES5), v2.0 sticks with
esnext
(although may transpilepackage.module
to ES2017 for Webpack 4 parsing support). Redux core uses Rollup, other packages use Babel and output separate files.- Mateusz: TS doesn't even try to interpret
require()
as far as types. Lot of complexity around.cdts
files, etc. - Mark: I'M LOST! :)
- Mateusz: if you mark the whole package as
type: "module"
, it gets a lot harder to have types for CJS consumers - Mark: I'm fine removing
type: "module"
- Mateusz: yeah, most important thing is the
exports
conditions. - Mark: so the order of fields in
exports
matters? - Mateusz: yes, matched from top to bottom. So, put
types
first (mentioned in the TS docs) - Mateusz: Andrew Branch (TS team) prefers to use "sibling files", but that's hard because TSC and other tools might be outputting in different ames/locations.
- Mateusz: you're using
typesVersions
over in Reselect, right? TS will ignoretypesVersions
if you supplyexports
, but supports an equivalent version selection inside ofexports
. - Mark: we do stupid build shenanigans in Reselect with a renamed
package.json
to select between a nested type in TS 4.6 vs 4.7 - Mateusz: this might not work with TS reading
exports
, TS might ignore it. - Mateusz: a lot of packages are misconfigured - they've started to use
exports
, but don't happen to change TS'smoduleResolution
field, so they don't see the issues
- Mateusz: TS doesn't even try to interpret
- Mark: shipped
redux-thunk@3.0.0-alpha
to switch from default export to only named; Reselect is only named. Would prefer to avoid shipping a React-Redux major just to alter packaging.- Mateusz: we did ship
exports
in an Emotion minor - Mark: everyone's warned me that shipping
exports
is a "breaking change" - Mateusz: we've decided that internal file structure is not a "public API". Yeah, some people can import from
dist
orsrc
, but that's on them.
- Mateusz: we did ship
- Mark: been trying to set up a battery of RTK example apps (CRA, Next, Vite, Parcel, RN, etc), and build+run them in CI. Maybe they should live in a separate repo so that we can run the jobs in all the Redux repo CIs? (Would be really nice if this was generalized into a SAAS somehow...)
- Mateusz: yeah, hard to do because of all the different flags and options, and also if external dependencies come into play.
- Mark: we've got 3 entry points,
@reduxjs/toolkit
,@reduxjs/toolkit/query
, and@reduxjs/toolkit/query/react
.- Mateusz: Yeah, Emotion has a couple as well. Nested
package.json
files is probably right. Forexports
, have multiple entries for"."
and each sub-entry.
- Mateusz: Yeah, Emotion has a couple as well. Nested
- Mark: What actions should we take?
- Mateusz: I think it's easiest to only ship ESM for bundlers, which avoids the dual-package issue.
- Mateusz: I provide ESM files for bundlers (see Emotion files):
exports.default
-> CJSexports.module
-> ESM- Can combine exports conditions via nesting
- Mark: What about TS
"node16"
?- Mateusz: Put
"types"
condition before"module"
- Mateusz: Put
- Mark: any advantage to rolling up TS typedefs into one file?
- Mateusz: not really. Save a couple FS reads, but no benefit there.
- Mark: is it worth switching to a premade tool like
preconstruct
ortsup
?- Mateusz:
preconstruct
isn't being marketed. Smart wrapper around Rollup, doesn't allow customization. Can do autofixes. Ran into trouble when Node shipped ESM support, hard to support dual packages out of the box.
- Mateusz:
FYI (and shamless plug), Parcel supports bundling
.d.ts
along with the usual main/module builds. See here. If you just runparcel build
in a package structured like that it'll output everything for you.That's how all of the packages in React Aria are built. For example here is what one of our package.jsons looks like. And on npm here is the generated dist directory. To build all of the packages in the monorepo, we just do
parcel build 'packages/*/*'
. The additionalimport.mjs
files referenced in exports are just copies ofmodule.js
created by a post build script.