Module Era
ESM + npm2015 – 2020Specified in 2015, shipped in runtimes years later. The language finally got a static module format — and the ecosystem got a decade-long CommonJS interop problem.
The specifier `"lodash"` means nothing to the language. Every runtime layers its own resolution algorithm on top — the single biggest source of ecosystem divergence.
// The same import, resolved four different ways:
import { readFile } from "node:fs/promises";
import chalk from "chalk";
import local from "./util.js";| V8 | Provides a resolution *hook* only. The embedder answers 'what does this specifier mean'. |
|---|---|
| Node.js | node_modules walk + `exports`/`imports` maps in package.json. File extensions are mandatory in ESM; `node:` prefix for builtins. |
| Deno | URL-first: remote https imports, import maps, and JSR. npm packages via the `npm:` specifier, cached rather than installed into a folder. |
| Bun | Node-compatible resolution with extension guessing, plus native TypeScript and JSX loading with no build step. |