Skip to content

Add --module node20 #61805

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open

Conversation

andrewbranch
Copy link
Member

@andrewbranch andrewbranch commented Jun 2, 2025

Following up on:

Also fixes:

This PR adds --module node20 as an alias for nodenext, the only difference being node20 implies --target es2023 while nodenext implies --target esnext. It also adds to node20/nodenext support for nodejs/node#54563:

// esm-lib:
export default class Foo {}
export { Foo as "module.exports" };

// CommonJS consumer:
const Foo = require("esm-lib"); // class Foo {}

Before this PR, TypeScript thought the type of const Foo was { default: Foo, "module.exports": Foo }.

This PR also makes node20 and nodenext imply resolveJsonModule. This change will be backported to node16 and node18 in TypeScript 6.0.

The new Node.js module option table looks like:

target moduleResolution resolveJsonModule import assertions import attributes JSON imports require(esm)
node16 es2022 node16 false no restrictions
node18 es2022 node16 false needs type "json"
node20 es2023 node16 true needs type "json"
nodenext esnext nodenext true needs type "json"

As a reminder, the semantics of --module node20 will only change if Node.js backports new module behavior into Node.js v20 (seems unlikely at this point), whereas nodenext is a moving target that will change as soon as Node.js introduces any new module behavior into the latest version of Node.js.

New baselines were validated against node18 and nodenext with

for f in tests/baselines/reference/*module=node20*; do
  [[ -e ${f/module=node20/module=node18} ]] && echo "$f" && diff "$f" "${f/module=node20/module=node18}";
done

and

for f in tests/baselines/reference/*module=node20*; do
  [[ -e ${f/module=node20/module=nodenext} ]] && echo "$f" && diff "$f" "${f/module=node20/module=nodenext}";
done

The latter produces only diffs related to the changing target setting or options diagnostics that quote the value of the module option verbatim.

@Copilot Copilot AI review requested due to automatic review settings June 2, 2025 16:28
@github-project-automation github-project-automation bot moved this to Not started in PR Backlog Jun 2, 2025
@typescript-bot typescript-bot added Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Jun 2, 2025
@typescript-bot
Copy link
Collaborator

Thanks for the PR! It looks like you've changed the TSServer protocol in some way. Please ensure that any changes here don't break consumers of the current TSServer API. For some extra review, we'll ping @sheetalkamat, @mjbvz, @zkat, and @joj for you. Feel free to loop in other consumers/maintainers if necessary.

Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces a new module alias “--module node20,” which behaves similarly to “nodenext” but implies a target of ES2023 rather than ESNext. The changes include updates to diagnostics, compiler option computations, module resolution and transformation, as well as adjustments throughout the test baselines to support the new Node20 module option.

  • Updated test baselines to include “node20” in error messages.
  • Extended import fixes, module option computations, and type checker logic to recognize and handle Node20.
  • Updated enums, diagnostic messages, and command line parsing accordingly.

Reviewed Changes

Copilot reviewed 523 out of 523 changed files in this pull request and generated no comments.

File Description
tests/baselines/reference/* Revised error baseline files to include “node20” in allowed module option lists.
src/services/codefixes/importFixes.ts, fixModuleAndTargetOptions.ts Added support for Node20 in import resolution and option fixers.
src/server/protocol.ts Added Node20 to the ModuleKind enum.
src/compiler/**/* Updated utilities, types, transformers, checkers, and diagnostic messages to support the Node20 module option.

@typescript-bot
Copy link
Collaborator

Looks like you're introducing a change to the public API surface area. If this includes breaking changes, please document them on our wiki's API Breaking Changes page.

Also, please make sure @DanielRosenwasser and @RyanCavanaugh are aware of the changes, just as a heads up.

Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces the --module node20 alias as an alternative to nodenext with a different target (ES2023 instead of ESNext) and updates all related diagnostics, error messages, and code paths accordingly.

  • Updates diagnostic messages, tests baselines, and compiler code to include node20 in the valid module list.
  • Adjusts module resolution, import fixes, and targeted emit options to account for node20.

Reviewed Changes

Copilot reviewed 523 out of 523 changed files in this pull request and generated 2 comments.

File Description
tests/baselines/reference/* Updates expected error messages to list node20 where applicable.
src/services/codefixes/* Adjusts code fixes so that node20 is treated alongside node16/node18/nodeNext.
src/compiler/* Updates various compiler routines and diagnostic messages to support node20.
src/server/protocol.ts Adds node20 into the ModuleKind enum for protocol communication.

@Mateusnasciment
Copy link

Does --module node20 fully replace nodenext for projects targeting Node.js 20, or are there still scenarios where nodenext would be preferred?

@andrewbranch
Copy link
Member Author

They’re the same right now except for implied target, but my usually recommendation is that library authors should use the pinned module setting for the lowest version of Node.js they’re trying to support, while app developers keeping their app’s runtime environment more or less up to date with new Node.js releases can just use nodenext.

@@ -4,7 +4,7 @@
"======== Resolving module '../lib' from '/app/test.ts'. ========",
"Explicitly specified module resolution kind: 'NodeNext'.",
"Resolving in CJS mode with conditions 'require', 'types', 'node'.",
"Loading module as file / folder, candidate module location '/lib', target file types: TypeScript, JavaScript, Declaration.",
"Loading module as file / folder, candidate module location '/lib', target file types: TypeScript, JavaScript, Declaration, JSON.",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to ask "what happens if lib.json exists", but I can see from the trace below that lib.json is still not being checked, which is good. Phew.

@github-project-automation github-project-automation bot moved this from Not started to Needs merge in PR Backlog Jun 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug
Projects
Status: Needs merge
Development

Successfully merging this pull request may close these issues.

4 participants