From c2b25d220465dac34dd2da41a2a44cb30c6f42e4 Mon Sep 17 00:00:00 2001 From: Rob Palmer Date: Fri, 5 Sep 2025 18:49:14 +0100 Subject: [PATCH] Modules Theory: require(ESM) is in Node 20 (#3440) --- packages/documentation/copy/en/modules-reference/Theory.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/documentation/copy/en/modules-reference/Theory.md b/packages/documentation/copy/en/modules-reference/Theory.md index 3c54f7f9b4ac..5e64354c5bb7 100644 --- a/packages/documentation/copy/en/modules-reference/Theory.md +++ b/packages/documentation/copy/en/modules-reference/Theory.md @@ -180,11 +180,11 @@ Can an ES module `import` a CommonJS module? If so, does a default import link t 1. **ESM-only.** Some runtimes, like browser engines, only support what’s actually a part of the language: ECMAScript Modules. 2. **Bundler-like.** Before any major JavaScript engine could run ES modules, Babel allowed developers to write them by transpiling them to CommonJS. The way these ESM-transpiled-to-CJS files interacted with hand-written-CJS files implied a set of permissive interoperability rules that have become the de facto standard for bundlers and transpilers. -3. **Node.js.** Until Node.js v22.12.0, CommonJS modules could not load ES modules synchronously (with `require`); they could only load them asynchronously with dynamic `import()` calls. ES modules can default-import CJS modules, which always binds to `exports`. (This means that a default import of a Babel-like CJS output with `__esModule` behaves differently between Node.js and some bundlers.) +3. **Node.js.** Until Node.js v20.19.0, CommonJS modules could not load ES modules synchronously (with `require`); they could only load them asynchronously with dynamic `import()` calls. ES modules can default-import CJS modules, which always binds to `exports`. (This means that a default import of a Babel-like CJS output with `__esModule` behaves differently between Node.js and some bundlers.) TypeScript needs to know which of these rule sets to assume in order to provide correct types on (particularly `default`) imports and to error on imports that will crash at runtime. When the `module` compiler option is set to `node16`, `node18`, or `nodenext`, Node.js’s version-specific rules are enforced.[^1] All other `module` settings, combined with the [`esModuleInterop`](/docs/handbook/modules/reference.html#esModuleInterop) option, result in bundler-like interop in TypeScript. (While using `--module esnext` does prevent you from _writing_ CommonJS modules, it does not prevent you from _importing_ them as dependencies. There’s currently no TypeScript setting that can guard against an ES module importing a CommonJS module, as would be appropriate for direct-to-browser code.) -[^1]: In Node.js v22.12.0 and later, a `require` of an ES module is allowed, but only if the resolved module and its top-level imports don’t use top-level `await`. TypeScript does not try to enforce this rule, as it lacks the ability to tell from a declaration file whether the corresponding JavaScript file contains top-level `await`. +[^1]: In Node.js v20.19.0 and later, a `require` of an ES module is allowed, but only if the resolved module and its top-level imports don’t use top-level `await`. TypeScript does not try to enforce this rule, as it lacks the ability to tell from a declaration file whether the corresponding JavaScript file contains top-level `await`. ### Module specifiers are not transformed by default @@ -341,7 +341,7 @@ import { add } from "./math.ts"; This restriction applies since TypeScript [won’t rewrite the extension](#module-specifiers-are-not-transformed) to `.js`, and if `"./math.ts"` appears in an output JS file, that import won’t resolve to another JS file at runtime. TypeScript really wants to prevent you from generating an unsafe output JS file. But what if there _is_ no output JS file? What if you’re in one of these situations: - You’re bundling this code, the bundler is configured to transpile TypeScript files in-memory, and it will eventually consume and erase all the imports you’ve written to produce a bundle. -- You’re running this code directly in a TypeScript runtime like Deno or Bun. +- You’re running this code directly in a TypeScript runtime like Node, Deno or Bun. - You’re using `ts-node`, `tsx`, or another transpiling loader for Node. In these cases, you can turn on `noEmit` (or `emitDeclarationOnly`) and `allowImportingTsExtensions` to disable emitting unsafe JavaScript files and silence the error on `.ts`-extensioned imports.