Description
Bug Report
In the NodeJS resolve spec is mentioned that the main field and all other alias fields like browser modules
get shadowed by exports so there is a fallback if a field is missing it looks up the original fields Typescript 4.6 does not do so and so I am forced to split dependencies for consumption into the once that support package.json exports: field including all sub dependencies.
see: #46452 (comment)
for me that is not a big issue as i tend to create dev bundels any way but most people will not have a good time without that fallback
see: #46452
🔎 Search Terms
node12 nodenext moduleResolve
🕗 Version & Regression Information
4.5 + new resolveModes
🙁 Actual behavior
I Started Consuming libs with node12 and nodenext resolve mode and got a lot of errors then I looked into the packages to identify what they did and found out there is no fallback in typescript which helped me to identify packages fast that got no exports field or export fields with subPath patterns as they are also not supported.
🙂 Expected behavior
always remember the rule that is most specific is the one that gets in effect! Like with css when a selector is more specific it gets used.
lookup patterns nodenext :
- package.json => exports: types:,
- package.json => types:,
- package.json => exports: node?.import|import|default,
- package.json => main:
same for node12
Short
moduleResolve modes node12 and nodenext should be backward compatible and should inhire resolve mode node which also includes Classic
Related Partial (Classic)
- The ESM resolver does not respect package
main
s for packages imported via relative imports nodejs/node#41940 - Missing node12 + nodenext => node (which includes classic which is resolved maybe) - TypeScript module "NodeNext" does not resolve
index.d.ts
from CJS modules #47848 - Implements node12 + nodenext => Classic (as far as i understood) - ESM mode nonrelative imports should assume index.js entrypoints even … #47854 - Implements node12 + nodenext => Classic (as far as i understood)
Activity
weswigham commentedon Mar 14, 2022
You seem to be under this mistaken impression that only one condition is active at a time and that we could execute multiple lookups inside the export map. Unfortunately, this is wrong, because compound conditions exist. You must resolve all export map conditions in a single pass to respect its export-blocking behavior, and that includes our
types
condition. The lack of atypes
condition does not imply a fallback to legacy main or types for lookup - on the contrary, it means that either the types are either alongside the provided export map entries or that there are no types at all; to do anything else is to usurp the intended blocking behavior of an export map.Every package author was well-informed that adding an export map is a breaking change - one of those breaks is to TS, unless you provide appropriate
types
entrypoints.weswigham commentedon Mar 14, 2022
In short: if you encounter any package with a
types
package json entry but nottypes
in a provided export map - report it as an issue on that package; they've (likely unintentionally) broken TS support.frank-dspeed commentedon Mar 15, 2022
@weswigham i guess your missing a little word because my english is so bad the exports map does shadow shadow means it takes the value of the existing fild if it exists and is not in the exports map also that is not a export map this does not fully implement the importMaps Proposal it is simply a temp addition of that feature so the algo should be designed like
then do the lookup the single one
that is meaned by design when we write the filds get shadowed its like with the Shadow DOM in the browser it is a overlay a onion type.
frank-dspeed commentedon Mar 15, 2022
@guybedford can you please verify my assumption or enlighten me? It would help me a lot. And if i am correct maybe point to something that defines that in better english i did not find the related documentation.
guybedford commentedon Mar 15, 2022
The implementation @weswigham describes is correct in that
"exports"
is authoritative and there is no fallback. There is one fallback exception though and that is that internal browser mappings do chain with the exports field:This was first implemented by Webpack then picked up by other bundlers so has become a convention now and I personally went along with it as well.
In all other cases, the exports field doesn't fall back at all to checking other fields.
That said, since TypeScript
"types"
usually correspond 1-1 with JS files, perhaps @weswigham there is a way to fall back to checking the expected"import"
/"module"
condition with a.d.ts
extension in place of the.mjs
or.js
extension. It seems like this would fix the usability issue in the example described without needing a complete verbose type listing for every export.frank-dspeed commentedon Mar 15, 2022
@guybedford @weswigham what do you think about that i droped that idea as i tought that would be a more aggressiv way but your current suggestion sounds a bit like that and it is a valid way to solve compat issues. #48239
weswigham commentedon Mar 15, 2022
Already works that way (.d.mts corresponds to .mjs, however). :)
frank-dspeed commentedon Mar 16, 2022
ok lets close that i will simply finish the package test utils and then we can see the results and then issue pull requests via the nodejs package maintainance group we have all moving parts in place i guess. So simply lets see what happens. Thx to all for making sure that the ecosystem evolves more fast.
Fix nodenext module support from TypeScript 4.7
17 remaining items