custom (ESM) loader + extension-less "bin" files #41711
Replies: 8 comments 62 replies
-
Another way to phrase this might be that the ESMLoader is the correct loader in all cases, but it arrives at a different / "wrong" conclusion compared to the CJSLoader. Both loaders should come to the same conclusion regarding the format of the entrypoint. Teaching the ESMLoader today to draw the same conclusion as the CJSLoader for entrypoints does not prevent node from, in the future, updating both loaders to arrive at a different conclusion. But leaving node's two loader behaviors inconsistent as it is today makes this a problem. |
Beta Was this translation helpful? Give feedback.
-
Proposal: Limited extension-less “bin” support in
|
Beta Was this translation helpful? Give feedback.
-
Proposal: Remove flag checks from
|
Beta Was this translation helpful? Give feedback.
-
Proposal: ESM custom loader opt-in via export flagAllow ESM custom loaders to opt into affecting entry-points. Ex // a-loader.mjs
export const useForEntryPoint = true;
export async function resolve() {}
export async function load() {} This may introduce a cart-before-the-horse scenario (the custom loader might not have been itself loaded, so reading that export flag may currently not be possible). |
Beta Was this translation helpful? Give feedback.
-
Proposal: Create a “higher” class of custom loader (eg ambient-loader)This class of custom loader would always be applied to entry-points (and may do other things too). |
Beta Was this translation helpful? Give feedback.
-
Proposal: Treat extension-less file with "magic phrase" as JSIf and only if the file starts with |
Beta Was this translation helpful? Give feedback.
-
Definining "extensionless"In relation to #41711 (comment), another topic worth bringing up here is what is the definition of an extensionless file? There are a few that might apply:
Under a specific definition of extensionless in the ESM loader it could then be possible for this definition to exhibit both backwards and forwards compatibility. The backwards compatibility risk would remain that extensions in the reserved list would work in the CJS loader but not the ESM loader. The forwards compatibility risk would remain that new extensions in future not in the reserved list would be breaking changes to support. Although perhaps this isn't such a huge risk since surely this is what MIME registries are designed to avoid in the first place. |
Beta Was this translation helpful? Give feedback.
-
Coming back to this, it looks like the best options are either:
|
Beta Was this translation helpful? Give feedback.
-
Currently, when a custom ESM loader is used in combination with an extension-less commonjs file, it erroneously fails. For example
There have been several disparate discussions about this issue. I’ve tried to capture the main points here from the ones I’ve found so far. If I’ve missed any, please comment and I’ll update.
Related: #33226, #41275, #41465, #41552, mochajs/mocha#4645
Problems
1A: The presence of the
--loader
flag causes Node.js to use the "wrong" loader (ESMLoader instead of CJSLoader), regardless of the entry-point’s actual type.1B:
ESMLoader::defaultGetFormat()
ignores any file without a.cjs
,.mjs
,.js
,.json
, or.node
file extension. This means, for a commonjs file without a file extension (which is allowed), Node.js will eventually throw when it should not.Considerations
WASM
ESMLoader intentionally does not handle extension-less files due to the possibility of extension-less wasm files in future.
Loader for entry-point
There is not an authoritative way to dictate which Loader (CJS vs ESM) should be used: A user specifying
--loader
my intend for that to be used when loading the entry-point (and thus manipulate how it’s handled). However, the logic inshouldUseESMLoader()
does not account for CJS’s equivalent flag,--require
(and friends). So if both are supplied together:What does that mean? One might reasonably expect the ESM loader to merely be used when the parent is ESM and the CJS require to merely be used when the parent is CJS (there is currently no other way for the ESM loader) and neither to affect which Loader is used for the entry-point.
Previously proposed solutions
I've created threads below with each I found.
Workarounds
Add a snippet to the custom loader
I use this in my own projects (my actual uses a map of extensions to load “hooklets” à la
{ '': loadBin, jsx: loadJSX, … }
)sample
@nodejs/loaders
Beta Was this translation helpful? Give feedback.
All reactions