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
What should the new moduleResolution setting be called? #51714
I’m looking for community feedback on the naming for #51669. For the full background on that PR, read #50152.
The short version is that it’s mostly designed for bundlers, but is also appropriate for runtimes like Bun, especially those that understand TypeScript natively. I initially wanted to avoid a very specific name like bundler that could prove to be a bad name in a few years if, say, a new wave of bundlers takes a very different approach to module resolution, or a new wave of runtimes are well-suited for the mode, or a huge number of projects use all-in-one frameworks like Next.js that are also well-suited for the mode but make it difficult to recognize what bundlers and runtimes and resolution features are actually in use.
The name I first proposed in #50152 was conventional, because it attempts to find the common ground shared by all the popular non-Node, non-Deno resolvers I surveyed. There was some concern that this could be read as a snub to Node, Deno, and other resolvers that implement strict resolution rules for ESM, so I revived hybrid from #29353. Both names are intentionally vague, but I’m rethinking whether that’s a good idea.
One potential mitigation for the name becoming a poor fit due to unpredictable future events is simply tacking the year onto the end: bundler2022. I don’t love this because if it turns out to be long-lived with the help of small, backward-compatible changes, it will falsely appear to grow increasingly outdated.
Reacted by Andrew Branch, Bruce Pascoe, Max Bernstein, Nico Jansen, Jordan Harband, Daniel Rosenwasser, Andreas Pfurtscheller, Brad Zacher, Gabriele Tomberli, Toni Villena and 10 more
Babel transpiles to CJS, so it allows ESM to be authored omitting extensions, and "index", and it ends up working the same.
If it's all bundlers, then I think "bundler" is probably the right key - the only way that term would stop being accurate would be if the majority of bundlers all switched to something new - and there'd probably be a new term we all associated with that new thing anyways that would help you name the new mode.
If we’re splitting hairs, what Babel does is orthogonal to moduleResolution. The moduleResolution setting you should choose to check your code that you’ll later compile with Babel depends entirely on where you intend to run that CJS code. moduleResolution is only concerned with the things in your toolchain/runtime that actually consume module specifiers and perform resolution to take some action on the target module. The caveat is that if, as in Node, resolution happens differently depending on import/require syntax, file extension, and package.json scope, then TypeScript may need to understand that a transformation of import syntax is going to happen. So in the example of running code transpiled with Babel in Node 12+, you would want to choose moduleResolution: "nodenext" while ensuring that your package.json "type" is unset or set to "commonjs", so that TypeScript will understand that the import syntax will ultimately become a require. This all works out only because Babel will produce roughly the same JS that tsc would under these settings—TypeScript doesn’t know you’re using Babel or what Babel will do, but it’s sufficiently close to what tsc will do that the checking works out. But it would be wrong to use moduleResolution: "node" in this case, because that mode lacks support for package.json exports. So if you write an import of some package that Babel turns into a require of some package and then run that require in Node, Node will consult that package’s exports if it exists. If that lookup resolves to a different result than the main would have pointed to, then likewise you would get an incorrect resolution of types for that import under moduleResolution: "node". (To help clear this confusion up, I plan to rename node to node10 after #51669.)
Reacted by Jordan Harband, Ryan Cavanaugh and Bruce Pascoe
To be honest, hybrid doesn't feel like a great name as it doesn't convey any particular meaning. Is it for the browser? Don't know. Is it not for the browser? Maybe?
How about runtime? I think the idea is that this setting covers multiple runtimes including node, deno, bun etc. And bundlers are invoked by a runtime (typically node but that may change).
In my mind the advantage of runtime is that it conveys some information of where it might be used
And if you wanted to hedge your bets: runtimenext
And if you want internet points: runtimeyMcRuntimeFace
If you're targeting a bundler, moduleResolution: "runtime" is inaccurate because where your code is ultimately run doesn't matter - the bundler does all module resolution, which in turn depends on the particular bundler, not the runtime it happens to run on. That's what this setting is about - it's not about where your code is being run, but instead which part of your toolchain resolves your module specifiers.
I think the shared heritage for the new mode is that it captures descendents/specializations of a Node-like approach. It represents some kind of lowest common denominator for mainstream use today, which also means it omits some of the features of latest Node. Here are some suggestions:
node-based (dual use of "based")
node-compatible
node-minimal
node-like
node-ish
nodey (I genuinely like this)
All of these escape the value judgements of conventional and vagueness of hybrid. It's no more or less of a snub to Node or non-Node resolvers than today's situation in which they are forced to use node.
Reacted by Jordan Harband, Daniel Rosenwasser and John Reilly
I do think node being in the name makes sense. To me, node-flavored is reminiscent of GitHub Flavored Markdown (though I'm sorry in advance to the Brits who will accidentally write node-flavoured).
The problem with permissive, to my mind, is, "permissive" with respect to what? If it's more permissive with respect to bundlers then that just means it's resolving things more closely to how bundlers do and isn't actually any more permissive than any other mode relative to the ecosystem; the goalposts are just as far away as they were before, they're just in a different spot now. For TS to actually be "more permissive" in any meaningful general sense it would have to (try to) support every module resolution strategy at once, which would almost certainly involve rewriting module specifiers.
Reacted by Jake Bailey, Jordan Harband and John Reilly
I’m up for node-based for hybrid as long as I can also rename node to node-cringe
I agree that the features the new mode shares with Node resolution are its most distinctive traits—if you give a directory named node_modules special powers, you must share something significant with Node. Something that mentions node is probably the least likely to become a misnomer in the future. However, I worry that putting node in the name could easily be mistaken for a signal that the mode is suitable for people actually compiling for Node, when the only correct choices for them are node16 and nodenext. Pending the renaming of node to node10, I was excited about creating a situation where a Node user can look at our resolution options and pick the right one by name, without having to read a three-volume history on module systems and TypeScript options.
"chapter 10: node and ES modules (not a short chapter)" 😅
I do think node being in the name makes sense. To me, node-flavored is reminiscent of GitHub Flavored Markdown (though I'm sorry in advance to the Brits who will accidentally write node-flavoured).
It's okay - we've had to get used to color instead of colour, not to mention all the zs that ought to be ss - we will survive another spelling mishap. 😉
In therapy news I've nearly made peace with the spelling of HTTP referer
Jokes aside, I think node-flavored actually carries intent quite well, and meaningful names are something to optimise for. (Or "optimize" for - YMMV)
I agree with the node-style of naming this one. But I also like "conventional", because it communicates that it could be used in multiple use cases as long as they support the convention. Using node-convention clearly communicates that the convention is based on "node". If you want to know more, you can dig into the docs.
I think it would help to clarify how the existing module resolutions mode are working. Maybe I missed whole sections in the documentation but as far as I know only classic and node are documented [1]
At the moment it sounds like this new resolution mode is the behaviour most TypeScript developers would expect when messing around with type=module? No need to import directories with index.js, ability to use no file extension or instead the .ts? If so, the question is, how would a typical developer call such a mode 🤔
Here my two cents but why not just moduleResolution: typescript?
With moduleResolution: node, TS follows Node module resolution, it can be seen as an external constraint that TS must implement to match with Node specifics.
Whereas, the new module emerged directly from TS usage itself and represents what the TS developer community has been doing to circumvent Node resolution specific constraints in a more or less hacky way (paths alias, typesVersions etc.) as Andrew Branch (@andrewbranch) already described thoroughly.
Plus, it makes .ts extension resolution explicit in its name.
Except that there's no such thing as "TypeScript module resolution" (unless TS rewrites import paths, which is a non-starter) because there's no "TypeScript runtime"--the closest thing to that would be Deno--and you're meant to always write the paths that work at runtime. The idea is that TS doesn't impose itself on you, but instead you configure TS to align with your target environment. TypeScript is a compiler, so not a target environment. Even if people are using paths, etc. then they also need to find a way to get their runtime and/or bundler to understand them, otherwise the code will fail at runtime. Whether TS can resolve the path or not is immaterial at that point.
Thanks for all the suggestions. I don’t think there’s a perfect option, but I’m currently leaning toward bundler. I had two concerns with this name:
What if bundlers all decide to do something super different in the future? I’m encouraged by our progress on Flag Deprecation Plan #51000 and think that going forward, we’ll be able to change defaults with a process and time scale that neither leaves decade-old misnomers around nor breaks a huge number of consumers without warning. If bundler-based resolution evolves significantly, the process will probably go something like (1) new options added that can be enabled alongside --moduleResolution bundler, (2) a new mode named something like --moduleResolution bundler2022 aliased to bundler, (3) deprecation-style warnings that the settings implied by bundler will change, (4) updated defaults in bundler, old defaults in bundler2022. I also think it’s unlikely that the changes we’ll see will be so disruptive and backward-incompatible that such a heavy process will be necessary. Planning for the unknown is great, but I am pretty satisfied to cross this bridge if we ever get to it.
The mode is good for more than just bundlers. At the moment, the only example I know of is Bun, and I don’t think a single example is enough to justify a more generic name right now. While I do think it’s quite possible we’ll see more JS runtimes emerge in the coming years, I have no basis for predicting whether this module resolution mode will be suitable for them. (Also, Bun has already changed part of its module resolution algorithm once since being released.) While there is nothing inherently bundler-specific about this mode, I would rather have a name now that makes it obvious that the mode is a good choice for bundler users and a bad choice for Node users, which together I think must be the vast majority of users today. I am concerned that an attempt to future-proof this will make it a present-hazard.
Activity
MartinJohns commentedon Dec 1, 2022
I vote for
BundlyMcBundlerface.ljharb commentedon Dec 1, 2022
From reading the recommended issue, it's still not clear to me what the target is, besides bun/esbuild.
I think that it's reasonable to have resolution modes that match platforms:
but also to have modes that match what users expect:
Are there more tools/bundlers that would use the proposed new mode that I missed?
andrewbranch commentedon Dec 1, 2022
Webpack, Parcel, Browserify, maybe Rollup (they call into us for resolution so it’s debatable), Vite, Next.js I think, which uses swc.
Babel does module resolution? I thought it only needs to care about one file at a time, specified via glob or something?
ljharb commentedon Dec 1, 2022
Babel transpiles to CJS, so it allows ESM to be authored omitting extensions, and "index", and it ends up working the same.
If it's all bundlers, then I think "bundler" is probably the right key - the only way that term would stop being accurate would be if the majority of bundlers all switched to something new - and there'd probably be a new term we all associated with that new thing anyways that would help you name the new mode.
fatcerberus commentedon Dec 1, 2022
In that case, "what babel does" reduces to
moduleResolution: "node"(i.e. pre-ESM Node resolution behavior).andrewbranch commentedon Dec 1, 2022
If we’re splitting hairs, what Babel does is orthogonal to
moduleResolution. ThemoduleResolutionsetting you should choose to check your code that you’ll later compile with Babel depends entirely on where you intend to run that CJS code.moduleResolutionis only concerned with the things in your toolchain/runtime that actually consume module specifiers and perform resolution to take some action on the target module. The caveat is that if, as in Node, resolution happens differently depending on import/require syntax, file extension, and package.json scope, then TypeScript may need to understand that a transformation of import syntax is going to happen. So in the example of running code transpiled with Babel in Node 12+, you would want to choosemoduleResolution: "nodenext"while ensuring that your package.json"type"is unset or set to"commonjs", so that TypeScript will understand that theimportsyntax will ultimately become arequire. This all works out only because Babel will produce roughly the same JS thattscwould under these settings—TypeScript doesn’t know you’re using Babel or what Babel will do, but it’s sufficiently close to whattscwill do that the checking works out. But it would be wrong to usemoduleResolution: "node"in this case, because that mode lacks support for package.jsonexports. So if you write animportof some package that Babel turns into arequireof some package and then run thatrequirein Node, Node will consult that package’sexportsif it exists. If that lookup resolves to a different result than themainwould have pointed to, then likewise you would get an incorrect resolution of types for that import undermoduleResolution: "node". (To help clear this confusion up, I plan to renamenodetonode10after #51669.)Faithfinder commentedon Dec 1, 2022
If this targets bun/rebuild you could name it both as an alias, and tack on a year anyway.
johnnyreilly commentedon Dec 2, 2022
Have you considered
conventionalnext😅To be honest,
hybriddoesn't feel like a great name as it doesn't convey any particular meaning. Is it for the browser? Don't know. Is it not for the browser? Maybe?How about
runtime? I think the idea is that this setting covers multiple runtimes including node, deno, bun etc. And bundlers are invoked by a runtime (typically node but that may change).In my mind the advantage of
runtimeis that it conveys some information of where it might be usedAnd if you wanted to hedge your bets:
runtimenextAnd if you want internet points:
runtimeyMcRuntimeFacefatcerberus commentedon Dec 2, 2022
If you're targeting a bundler,
moduleResolution: "runtime"is inaccurate because where your code is ultimately run doesn't matter - the bundler does all module resolution, which in turn depends on the particular bundler, not the runtime it happens to run on. That's what this setting is about - it's not about where your code is being run, but instead which part of your toolchain resolves your module specifiers.robpalme commentedon Dec 2, 2022
I think the shared heritage for the new mode is that it captures descendents/specializations of a Node-like approach. It represents some kind of lowest common denominator for mainstream use today, which also means it omits some of the features of latest Node. Here are some suggestions:
node-based(dual use of "based")node-compatiblenode-minimalnode-likenode-ishnodey(I genuinely like this)All of these escape the value judgements of
conventionaland vagueness ofhybrid. It's no more or less of a snub to Node or non-Node resolvers than today's situation in which they are forced to usenode.DanielRosenwasser commentedon Dec 2, 2022
What do people think about
nodey-mcnodeface?fatcerberus commentedon Dec 2, 2022
Okay I’ll bite - is this a recent meme I’m not aware of?
7 remaining items
DanielRosenwasser commentedon Dec 5, 2022
I do think
nodebeing in the name makes sense. To me,node-flavoredis reminiscent of GitHub Flavored Markdown (though I'm sorry in advance to the Brits who will accidentally writenode-flavoured).fatcerberus commentedon Dec 5, 2022
The problem with
permissive, to my mind, is, "permissive" with respect to what? If it's more permissive with respect to bundlers then that just means it's resolving things more closely to how bundlers do and isn't actually any more permissive than any other mode relative to the ecosystem; the goalposts are just as far away as they were before, they're just in a different spot now. For TS to actually be "more permissive" in any meaningful general sense it would have to (try to) support every module resolution strategy at once, which would almost certainly involve rewriting module specifiers.andrewbranch commentedon Dec 6, 2022
I’m up for
node-basedforhybridas long as I can also renamenodetonode-cringeI agree that the features the new mode shares with Node resolution are its most distinctive traits—if you give a directory named
node_modulesspecial powers, you must share something significant with Node. Something that mentionsnodeis probably the least likely to become a misnomer in the future. However, I worry that puttingnodein the name could easily be mistaken for a signal that the mode is suitable for people actually compiling for Node, when the only correct choices for them arenode16andnodenext. Pending the renaming ofnodetonode10, I was excited about creating a situation where a Node user can look at our resolution options and pick the right one by name, without having to read a three-volume history on module systems and TypeScript options.johnnyreilly commentedon Dec 6, 2022
"chapter 10: node and ES modules (not a short chapter)" 😅
It's okay - we've had to get used to
colorinstead ofcolour, not to mention all thezs that ought to bess - we will survive another spelling mishap. 😉In therapy news I've nearly made peace with the spelling of HTTP referer
Jokes aside, I think
node-flavoredactually carries intent quite well, and meaningful names are something to optimise for. (Or "optimize" for - YMMV)fatcerberus commentedon Dec 6, 2022
How did I never notice that
refererwas misspelled? And now I can’t unsee it… thanks I hate itnicojs commentedon Dec 7, 2022
How about
node-worthy? (couldn't help myself 🤡).So how about:
node-convention?I agree with the
node-style of naming this one. But I also like "conventional", because it communicates that it could be used in multiple use cases as long as they support the convention. Usingnode-conventionclearly communicates that the convention is based on "node". If you want to know more, you can dig into the docs.robpalme commentedon Dec 7, 2022
Andrew Branch (@andrewbranch) said
A stronger distinction would be to swap the order.
approximating-node(a formalizednodey)like-nodeakin-to-nodetantamount-to-node(may imply node is a crime)weyert commentedon Dec 7, 2022
I think it would help to clarify how the existing module resolutions mode are working. Maybe I missed whole sections in the documentation but as far as I know only
classicandnodeare documented [1]At the moment it sounds like this new resolution mode is the behaviour most TypeScript developers would expect when messing around with
type=module? No need to import directories withindex.js, ability to use no file extension or instead the.ts? If so, the question is, how would a typical developer call such a mode 🤔[1] https://www.typescriptlang.org/docs/handbook/module-resolution.html#module-resolution-strategies
hubertkuoch commentedon Dec 7, 2022
Here my two cents but why not just
moduleResolution: typescript?With
moduleResolution: node, TS follows Node module resolution, it can be seen as an external constraint that TS must implement to match with Node specifics.Whereas, the new module emerged directly from TS usage itself and represents what the TS developer community has been doing to circumvent Node resolution specific constraints in a more or less hacky way (paths alias, typesVersions etc.) as Andrew Branch (@andrewbranch) already described thoroughly.
Plus, it makes
.tsextension resolution explicit in its name.fatcerberus commentedon Dec 10, 2022
Except that there's no such thing as "TypeScript module resolution" (unless TS rewrites import paths, which is a non-starter) because there's no "TypeScript runtime"--the closest thing to that would be Deno--and you're meant to always write the paths that work at runtime. The idea is that TS doesn't impose itself on you, but instead you configure TS to align with your target environment. TypeScript is a compiler, so not a target environment. Even if people are using
paths, etc. then they also need to find a way to get their runtime and/or bundler to understand them, otherwise the code will fail at runtime. Whether TS can resolve the path or not is immaterial at that point.andrewbranch commentedon Dec 13, 2022
Thanks for all the suggestions. I don’t think there’s a perfect option, but I’m currently leaning toward
bundler. I had two concerns with this name:--moduleResolution bundler, (2) a new mode named something like--moduleResolution bundler2022aliased tobundler, (3) deprecation-style warnings that the settings implied bybundlerwill change, (4) updated defaults inbundler, old defaults inbundler2022. I also think it’s unlikely that the changes we’ll see will be so disruptive and backward-incompatible that such a heavy process will be necessary. Planning for the unknown is great, but I am pretty satisfied to cross this bridge if we ever get to it.johnnyreilly commentedon Dec 13, 2022
No nodey-mcnodeface!
Conaclos commentedon Dec 13, 2022
What about
node16-extended?