In General you should author your extensions to work with CJS and offer a ESM Wrapper for the Exports if they are not already getting interpreted by the CJS-MODULE-Lexer
Because the Engine by Default is Sync and the new introduced Module System is Async and can produce easy single file code that is sync and static analyzeable. So if you publish something as ESM only it can not contain nativ code that is bound to the engine. As rule of thumb if you produce something where you need to interact with Nativ Code in Sync better produce CJS without the ESM loader envolved that makes your Module Compatible to NodeJS (Nativ) Electron (Nativ) and other Runtimes that use native code.
When you target mainly browsers and you offer a function that is always async you can go ESM Only.
it is static defined NamedExports for CJS Code aka ES2022 without the Module System. it applys some rules to the exports thats it.
The ESM Module System Implements the rule that every Module should export a Object with namedExports
Before ESM you could for example export a function module.exports = () => console.log('From function')
with ESM you get module.exports = const default = () => console.log('From function')
There is a module marker __esModule = true it often says it is there to declare that a CJS Module got created from a ESM module
and that it has no default export. That is missleading as it does exact that it replaces module.exports = myModule.default with module.exports = myModule
The keyword that is often referenced here is syntacticDefaultImports. it sayes that we always import default that is defined by the syntax. it is a so called esModule InterOp Setting as it defines how we interact with transpiled esModules that do normaly contain ESM Syntax.
it is the namedExports pattern if you follow it there can nothing go wrong as long as you export always a Object with string propertys your fine it can get transpiled back and forth and keeps its api as long as the module System is Compatible CJS and ESM as also SYSTEMJS for example are 100% Compatible and interchange able if you follow that pattern
the only mistery is the default export if the coder is not aware of the pattern.
Deno as also GraalVM (GraalJS) are total diffrent engines that do interpret code totaly diffrent for example GraalJS is able to Converty back and forth between ESM and CJS at runtime because it is Simply Feeding the Code into the Engine via Java and can this way transpil it before. Or Rust can use ESM Directly as the Engine is Written in Rust it supports Async code out of the Box even for the native Code Parts.
Sure it can it is written in Nativ C but there is not much API Around that
- https://nodejs.org/api/worker_threads.html#worker-threads
- https://nodejs.org/api/async_context.html#using-asyncresource-for-a-worker-thread-pool
- Also the part about resource binding is importent to bind it to the right context!
package.json npm v6+ Installing peerDependencies if not marked optional
{
"name": "tea-latte",
"version": "1.3.5",
"peerDependencies": {
"tea": "2.x",
"soy-milk": "1.2"
},
"peerDependenciesMeta": {
"soy-milk": {
"optional": true
}
}
}
this concept is key to produce packages that adapt to the environment and should not get abused to produce libs most of the time while there are some interristing usecases.