JavaScript modules and packaging

Published on
4 min read
Related tags

Compiling everything I've learned about modules and packaging into a simple cheat sheet.

As I've started dipping my toes into the world of publishing packages on npm, I've often been overwhelmed with all the different terminology and options. This is my attempt at gathering as much information in one place for a quick reference.

Definitions

"ECMAScript Modules" aka "ES modules" aka "ESM"

"CommonJS" aka "CJS"

"Universal Module Definition" aka "UMD"

"dual packaging"

File Extensions

.mjs

.mts

.cjs

Packaging (package.json fields)

main

module

type

exports

Subpath exports

some-package/package.json
Copy

_10
{
_10
"exports": {
_10
".": "./foo.js",
_10
"./bar.js": "./src/bar.js"
_10
}
_10
}

Only defined subpaths can then be imported by a consumer, other imports will error:


_10
import bar from 'some-package/bar'; // okay!
_10
import private from 'some-package/something-private.js'; // nope!

In the absence of multiple subpaths, "." has no purpose!

package.json
Copy

_10
"exports": {
_10
".": {
_10
"require": "./dist/index.cjs",
_10
"default": "./dist/index.modern.js"
_10
}
_10
}

...is equivalent to:

package.json
Copy

_10
"exports": {
_10
"require": "./dist/index.cjs",
_10
"default": "./dist/index.modern.js"
_10
}

Conditional exports

package.json
Copy

_10
{
_10
"exports": {
_10
"import": "./foo.js", // Entry-point for `import "foo"` in ESM
_10
"require": "./foo.cjs" // Entry-point for `require("foo")` in CJS
_10
}
_10
}

exports.types

package.json
Copy

_10
{
_10
"exports": {
_10
"types": "./foo.d.ts", // TypeScript typings for NodeNext modules
_10
"import": "./foo.js", // Entry-point for `import "foo"` in ESM
_10
"require": "./foo.cjs" // Entry-point for `require("foo")` in CJS
_10
}
_10
}

types

Thanks ❤️


Last Updated