Skip to content

Instantly share code, notes, and snippets.

@frank-dspeed
Last active April 24, 2022 06:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save frank-dspeed/245c22e525985632d01d75c377ded41d to your computer and use it in GitHub Desktop.
Save frank-dspeed/245c22e525985632d01d75c377ded41d to your computer and use it in GitHub Desktop.
Package Authoring Manual for NodeJS made by Frank Lemanschik member of the @NodeJS/PackageMaintainance Group

Package Authoring Guide for Nodejs

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

Why CJS first it is old?

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.

What is ESM?

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.

smallest denominator between CJS and ESm

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.

Why Engines like Deno and GraalJS are diffrent?

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.

Can NodeJS Behave like Deno or GraalJS?

Sure it can it is written in Nativ C but there is not much API Around that

Handling dependencies

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment