Skip to content

release: v12.0.0 major release (approx. Q3 2026)#3280

Draft
kamilmysliwiec wants to merge 153 commits intomasterfrom
v12.0.0
Draft

release: v12.0.0 major release (approx. Q3 2026)#3280
kamilmysliwiec wants to merge 153 commits intomasterfrom
v12.0.0

Conversation

@kamilmysliwiec
Copy link
Copy Markdown
Member

@kamilmysliwiec kamilmysliwiec commented Mar 9, 2026

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Other... Please describe: v12.0.0 release

Description

Approximate release window: early Q3 2026

A brief list of changes:

  • nest new will now prompt users asking whether they want to use ESM or CJS for their project (ESM is the new default with vitest instead of jest)
  • the entire source code has been migrated to ESM
  • jest -> vitest for tests
  • e2e tests added for all commands
  • command classes have been refactored, and they now take context objects as inputs instead of non-types input and options arrays
  • webpack builder has been deprecated
  • rspack is the new default for monorepos
  • eslint to oxlint
  • bun added as a supported package manager

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

@kamilmysliwiec
Copy link
Copy Markdown
Member Author

A current version of this PR has been published under the next tag - @nestjs/cli@12.0.0-alpha.5

Copy link
Copy Markdown

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 prepares the Nest CLI v12 major release by migrating the codebase to ESM/NodeNext, switching the test runner from Jest to Vitest, and introducing rspack support (with webpack deprecated) alongside expanded e2e coverage.

Changes:

  • Add Vitest configuration (unit + e2e) and migrate existing unit tests from Jest to Vitest.
  • Migrate CLI/runtime code to ESM ("type": "module", .js import specifiers, NodeNext TS config) and refactor commands/actions to use typed context objects.
  • Add rspack compiler + defaults and add e2e tests across core CLI commands.

Reviewed changes

Copilot reviewed 128 out of 131 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
vitest.e2e.config.ts Adds Vitest config for e2e suite (timeouts, includes).
vitest.config.ts Adds Vitest config for unit tests and excludes e2e.
tsconfig.json Switches TS compilation to NodeNext/ES2022 and removes Jest types.
tools/postinstall.cjs Post-install helper for test fixtures + vitest timeout patching.
tools/clean.js Replaces gulp-based clean with a Node script to remove build artifacts.
test/lib/utils/get-default-tsconfig-path.spec.ts Migrates test from Jest to Vitest + ESM import specifiers.
test/lib/schematics/schematic.option.spec.ts Updates tests for boolean flag serialization changes + Vitest migration.
test/lib/schematics/nest.collection.spec.ts Migrates to Vitest and updates ESM import paths.
test/lib/schematics/custom.collection.spec.ts Migrates to Vitest and adds an ESM-safe mock for schematics tools.
test/lib/runners/schematic.runner.spec.ts Updates module-path resolution expectations for ESM.
test/lib/readers/file-system.reader.spec.ts Migrates to Vitest and updates reader imports.
test/lib/questions/questions.spec.ts Migrates to Vitest and updates ESM import paths/types usage.
test/lib/package-managers/yarn.package-manager.spec.ts Migrates to Vitest and updates ESM import paths/mocks.
test/lib/package-managers/pnpm.package-manager.spec.ts Migrates to Vitest and updates ESM import paths/mocks.
test/lib/package-managers/package-manager.factory.spec.ts Migrates to Vitest and updates typings for mock casting.
test/lib/package-managers/npm.package-manager.spec.ts Migrates to Vitest and updates ESM import paths/mocks.
test/lib/configuration/nest-configuration.loader.spec.ts Migrates to Vitest and updates ESM import paths.
test/lib/compiler/webpack/webpack-compiler.spec.ts Adds coverage for webpack compiler deprecation + ESM rejection behavior.
test/lib/compiler/swc/swc-compiler.spec.ts Updates SWC compiler unit tests for ESM module layout and Vitest mocks.
test/lib/compiler/rspack/rspack-defaults.spec.ts Adds tests for new rspack defaults factory (incl. ESM mode).
test/lib/compiler/rspack/rspack-compiler.spec.ts Adds unit tests for new rspack compiler behavior (watch, config merging).
test/lib/compiler/hooks/tsconfig-paths.hook.spec.ts Reworks hook tests to assert both CJS and ESM output (drops snapshots).
test/lib/compiler/hooks/fixtures/unused-imports/src/main.ts Updates fixture imports to include .js extension for ESM.
test/lib/compiler/hooks/fixtures/type-imports/src/main.ts Updates fixture relative imports to include .js extension for ESM.
test/lib/compiler/hooks/snapshots/tsconfig-paths.hook.spec.ts.snap Removes Jest snapshots now that assertions are explicit.
test/lib/compiler/helpers/get-value-or-default.spec.ts Migrates helper test to Vitest and ESM import specifiers.
test/lib/compiler/helpers/get-rspack-config-path.spec.ts Adds tests for new rspack config-path helper.
test/jest-config.json Removes Jest test configuration.
test/e2e/new.command.e2e-spec.ts Adds e2e coverage for nest new behaviors (language, strict, flags).
test/e2e/jest-e2e.json Adds fixture Jest config used by generated projects in e2e scenarios.
test/e2e/info.command.e2e-spec.ts Adds e2e coverage for nest info with/without deps installed.
test/e2e/helpers.ts Adds shared e2e utilities for running/spawning CLI and scaffolding projects.
test/e2e/generate.command.e2e-spec.ts Adds e2e coverage for nest generate across many schematic types/flags.
test/e2e/build.command.e2e-spec.ts Adds e2e coverage for nest build (tsc/swc + monorepo webpack cases).
test/e2e/add.command.e2e-spec.ts Adds e2e coverage for nest add (install + dry-run/skip-install).
test/actions/info.action.spec.ts Migrates action tests to Vitest and adapts to private method access.
test/actions/build.action.spec.ts Adds unit tests for builder dispatch behavior (webpack vs rspack).
package.json Marks package as ESM, switches to Vitest scripts, updates deps/peers, replaces clean step.
lib/utils/tree-kill.ts Renames internals to clearer “children” naming.
lib/utils/remaining-flags.ts Updates commander integration to use Command + getOptionValue().
lib/utils/project-utils.ts Removes hasValidOptionFlag and updates imports for ESM layout.
lib/utils/local-binaries.ts Converts local command loader to async ESM import of local CLI commands.
lib/utils/load-configuration.ts Updates imports for ESM layout.
lib/utils/is-module-available.ts Uses createRequire for ESM-safe module resolution.
lib/utils/is-esm-project.ts Adds helper to detect ESM projects via package.json "type": "module".
lib/ui/messages.ts Updates ESM import specifier for emojis module.
lib/ui/index.ts Updates barrel exports to explicit .js specifiers.
lib/schematics/schematic.option.ts Changes boolean option rendering to --flag=false for false.
lib/schematics/nest.collection.ts Updates imports for ESM layout.
lib/schematics/index.ts Updates barrel exports to explicit .js specifiers.
lib/schematics/custom.collection.ts Updates NodeWorkflow import to explicit /index.js for ESM.
lib/schematics/collection.factory.ts Updates imports for ESM layout.
lib/schematics/abstract.collection.ts Updates imports for ESM layout.
lib/runners/yarn.runner.ts Updates import specifier for ESM layout.
lib/runners/schematic.runner.ts Reworks module-path resolution to be ESM-safe via createRequire.
lib/runners/runner.factory.ts Changes unsupported runner behavior from warn+continue to throwing an Error.
lib/runners/pnpm.runner.ts Updates import specifier for ESM layout.
lib/runners/npm.runner.ts Updates import specifier for ESM layout.
lib/runners/index.ts Updates barrel exports to explicit .js specifiers.
lib/runners/git.runner.ts Updates import specifier for ESM layout.
lib/runners/abstract.runner.ts Updates UI import for ESM layout and aligns variable naming with lint config.
lib/readers/index.ts Updates barrel exports to explicit .js specifiers.
lib/readers/file-system.reader.ts Updates reader import specifier for ESM layout.
lib/questions/questions.ts Removes any return annotation from generateInput factory.
lib/package-managers/yarn.package-manager.ts Updates imports for ESM layout.
lib/package-managers/pnpm.package-manager.ts Updates imports for ESM layout.
lib/package-managers/package-manager.factory.ts Updates imports for ESM layout and simplifies catch handling.
lib/package-managers/npm.package-manager.ts Updates imports for ESM layout.
lib/package-managers/index.ts Updates barrel exports to explicit .js specifiers.
lib/package-managers/abstract.package-manager.ts Migrates to fs/promises, ESM ora import, and tightens typing for package.json reads.
lib/configuration/nest-configuration.loader.ts Updates imports, fixes a typo in comment, and simplifies defaulting logic.
lib/configuration/index.ts Updates barrel exports to explicit .js specifiers.
lib/configuration/defaults.ts Updates imports and adds default rspack config filename constant.
lib/configuration/configuration.ts Extends builder types to include rspack and tightens several type definitions.
lib/configuration/configuration.loader.ts Updates import specifier for ESM layout.
lib/compiler/webpack-compiler.ts Adds webpack deprecation messaging, ESM project guard, and lazy-loads webpack deps.
lib/compiler/watch-compiler.ts Updates imports for ESM layout.
lib/compiler/typescript-loader.ts Reworks TS binary resolution for ESM (no module.paths reliance).
lib/compiler/swc/type-checker-host.ts Updates imports and logs type-check errors to stderr.
lib/compiler/swc/swc-compiler.ts ESM migration (createRequire/dirname), better error handling, and safer deep-merge own-property checks.
lib/compiler/swc/forked-type-checker.ts Updates imports and sends errors to stderr.
lib/compiler/swc/constants.ts Removes unused constant.
lib/compiler/rspack-compiler.ts Introduces new Rspack compiler implementation with watch/build flows.
lib/compiler/plugins/plugins-loader.ts ESM-safe plugin resolution via createRequire, fixes PluginAndOptions typing, improves error causes.
lib/compiler/plugins/plugin-metadata-printer.ts Updates import specifier for ESM layout.
lib/compiler/plugins/plugin-metadata-generator.ts Updates imports for ESM layout.
lib/compiler/hooks/tsconfig-paths.hook.ts ESM-safe resolution updates and removes os.platform() dependency.
lib/compiler/helpers/tsconfig-provider.ts Updates imports for ESM layout.
lib/compiler/helpers/get-webpack-config-path.ts Updates signature to accept option records instead of Input[] arrays.
lib/compiler/helpers/get-value-or-default.ts Changes option handling from Input[] to Record-based access for context objects.
lib/compiler/helpers/get-tsc-config.path.ts Updates signature to accept option records instead of Input[] arrays.
lib/compiler/helpers/get-rspack-config-path.ts Adds helper for rspack config path resolution from builder config.
lib/compiler/helpers/get-builder.ts Updates signature to accept option records instead of Input[] arrays.
lib/compiler/helpers/delete-out-dir.ts Updates imports for ESM layout.
lib/compiler/defaults/webpack-defaults.ts Lazy-loads webpack-related deps and improves missing-dep errors for optional installation.
lib/compiler/defaults/swc-defaults.ts Updates imports for ESM layout.
lib/compiler/defaults/rspack-defaults.ts Adds rspack defaults factory with ESM/CJS support and optional plugin integration.
lib/compiler/compiler.ts Updates imports and tightens extra arg typing.
lib/compiler/base-compiler.ts Refines abstract method return types and restricts some methods to protected.
lib/compiler/assets-manager.ts Updates imports and improves error message typing with cause.
gulpfile.js Removes legacy gulp entrypoint for cleaning/build tooling.
eslint.config.js Adds new flat-config ESLint setup for TS/ESM repo layout.
commands/start.command.ts Migrates to context-based action invocation and updates commander types.
commands/new.command.ts Migrates to context-based action invocation and updates language normalization logic.
commands/info.command.ts Updates commander types and ESM imports.
commands/index.ts Updates exports and adds context exports.
commands/generate.command.ts Migrates to context-based invocation and updates --spec option default semantics.
commands/context/start.context.ts Adds typed context interface for start command.
commands/context/new.context.ts Adds typed context interface for new command.
commands/context/index.ts Adds context barrel exports.
commands/context/generate.context.ts Adds typed context interface for generate command.
commands/context/build.context.ts Adds typed context interface for build command.
commands/context/add.context.ts Adds typed context interface for add command.
commands/command.loader.ts Adds ESM compatibility guard for global/local CLI mismatches and improves invalid command handling.
commands/build.command.ts Migrates to context-based action invocation and adds rspack to builder help text.
commands/add.command.ts Migrates to context-based action invocation and passes through remaining flags.
commands/abstract.command.ts Updates commander type usage for ESM migration.
bin/nest.ts Switches to new Command() for ESM, marks program as ESM-compatible, awaits local loader import.
actions/start.action.ts Refactors to accept StartCommandContext and adapts runBuild/start lifecycle to ESM.
actions/new.action.ts Refactors to accept NewCommandContext and updates prompting + schematic option mapping.
actions/info.action.ts Refactors internal methods to private, uses padEnd, and updates ESM imports.
actions/index.ts Updates exports to explicit .js specifiers.
actions/generate.action.ts Refactors to accept GenerateCommandContext and updates schematic-option mapping.
actions/build.action.ts Refactors to context-based inputs, adds rspack builder support, and updates ESM imports/dynamic imports.
actions/add.action.ts Refactors to AddCommandContext and removes reliance on Input[] option scanning.
actions/abstract.action.ts Simplifies AbstractAction API to a single context parameter.
.eslintrc.js Removes legacy ESLint RC config in favor of flat config.
.eslintignore Removes legacy ignore file (handled in flat config).
.circleci/config.yml Updates CI to run Vitest and adds separate e2e job.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/package-managers/abstract.package-manager.ts
Comment thread lib/compiler/helpers/get-rspack-config-path.ts
Comment thread tools/clean.js Outdated
Comment thread tools/postinstall.cjs Outdated
Comment thread tools/postinstall.cjs
Comment thread test/lib/readers/file-system.reader.spec.ts
Comment thread test/e2e/build.command.e2e-spec.ts
Comment thread test/e2e/build.command.e2e-spec.ts
Copy link
Copy Markdown

Copilot AI commented Mar 11, 2026

@kamilmysliwiec I've opened a new pull request, #3282, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown

Copilot AI commented Mar 11, 2026

@kamilmysliwiec I've opened a new pull request, #3283, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown

Copilot AI commented Mar 11, 2026

@kamilmysliwiec I've opened a new pull request, #3284, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Copy Markdown

Copilot AI commented Mar 11, 2026

@kamilmysliwiec I've opened a new pull request, #3285, to work on those changes. Once the pull request is ready, I'll request review from you.

kamilmysliwiec and others added 30 commits April 17, 2026 10:30
fix(new): prefer process.stdout.columns over tput for terminal width
feat(cli): add rspack config path option to build and start commands
…aries

feat(assets): add option to include library assets in app builds
feat(package-managers): add bun support
…odenext resolution (#3364)

Under `moduleResolution: 'node16'` or `'nodenext'`, ESM-style imports
require explicit file extensions. The PluginMetadataGenerator previously
emitted dynamic `import("./hello.dto")` calls without an extension,
causing TypeScript diagnostics in the generated metadata file and
`ERR_MODULE_NOT_FOUND` at runtime when the metadata is executed.

Detect the consuming project's `moduleResolution` from the supplied
`ts.Program` and, when it is Node16 or NodeNext, walk both the
collected metadata AST and the visitor `typeImports` map to append
`.js` to relative specifiers that lack an extension. Bare specifiers,
absolute paths, and already-extended specifiers (`.js`, `.mjs`, `.cjs`,
`.json`, ...) are left untouched, so projects using classic / node10 /
bundler resolution are unaffected.

Closes #3364
v12.0.0 runs vitest without globals enabled, so the new spec needs to
import `describe`, `expect`, `it` explicitly. CI was failing with
`ReferenceError: describe is not defined` at file load time, which
caused the whole spec to be skipped.

After this change, all 18 cases in the spec pass alongside the existing
suite.
…Dir (#3387)

When `--path` (or any tsconfig override) results in an emit whose
`rootDir` differs from the configured `sourceRoot`, AssetsManager
currently strips asset paths against `sourceRoot`, leaving copied
assets one level above where the emitted JavaScript ended up.

Computes the effective rootDir the same way the TypeScript compiler
does — explicit `compilerOptions.rootDir` when set, otherwise the
longest common parent directory of the input file list — and uses it
as the strip root for application assets. Library assets keep the
per-library `_sourceRoot` introduced for `includeLibraryAssets` so
their layout is unchanged.

This is a re-implementation of #3403 on top of v12.0.0 (vitest tests,
library-assets-aware AssetsManager, debounced watch onSuccess).
…754525b

fix(plugin-metadata): emit .js extensions for dynamic imports under nodenext (#3364)
fix(build): align asset path stripping with effective TypeScript rootDir (#3387)
`nest info` walks the project `package.json` and lists every `@nestjs/*`
dependency it finds. The dependency list flows through `format()`, which
sorted the entries and then read `sorted[0].name.length` to compute a
column width — without first checking that the list was non-empty.

When `package.json` was readable but contained zero `@nestjs/*` entries
(running `nest info` from a non-Nest project, or from a fresh checkout
where dependencies have not been installed yet), `format([])` threw
`Cannot read properties of undefined (reading 'name')`. That exception
bubbled up to the outer `try/catch` in `displayNestInformationFromPackage`
and was silently relabeled as `NEST_INFORMATION_PACKAGE_MANAGER_FAILED`:

    smirk  cannot read your project package.json file,
    are you inside your project directory?

…which is a flat-out lie when the read itself succeeded, and sends users
hunting for a non-existent file/directory issue.

Fix it in two complementary places so the contract is right end-to-end:

* `format()` is now defensive — it returns an empty array unchanged
  instead of dereferencing `sorted[0]`. This means the helper is safe
  to call with any input, including from tests.
* `displayNestVersions()` short-circuits for an empty list and prints
  an honest, actionable message
  (`No @nestjs/* dependencies were found in package.json.`) instead of
  letting an exception fall into the misleading package-manager catch.

Tests cover both: `format([])` no longer throws, the existing
non-empty padding/version-range cleanup still works, and
`displayNestVersions({})` writes the new explanatory line.
Replace stray jest.Mock casts in the bun lock-file tests with vitest's
Mock type that the surrounding tests already use. The casts were left over
from #3258 and reference a global that does not exist on v12 (tests pass
at runtime only because vitest does not type-check by default).

Also add a unit test for the existing catch branch in
PackageManagerFactory.find that falls back to NpmPackageManager when
fs.promises.readdir rejects, so the behaviour is locked in.
The existing plugins-loader spec exercised only the most common shape: a
plugin exporting both `before` and `after` transformer factories. It left
several user-visible contracts of `PluginsLoader.load` untested:

- A plugin that exports only `afterDeclarations` is still valid and must
  produce an entry in `afterDeclarationsHooks` (not in beforeHooks /
  afterHooks).
- When a plugin exports a `ReadonlyVisitor` class, the loader must
  instantiate it with the merged user options + extras + `readonly: true`
  and stamp the plugin name onto `instance.key` so downstream metadata
  generation can identify the visitor.
- The `extras.pathToSource` argument must reach the visitor constructor.
- The bound hooks returned in `MultiNestCompilerPlugins` must, when called
  with a `ts.Program`, invoke the original transformer factory with the
  user-supplied options as the first argument and the program as the
  second.
- Plugin order in the input array must be preserved across the returned
  hook arrays.

These behaviors are relied on by `compiler.ts`, `watch-compiler.ts`,
`swc-compiler.ts`, `forked-type-checker.ts`, and the metadata pipeline,
so a regression in any of them would silently break swagger/graphql
plugin integration.

No production code is touched.
…age-v12

test(plugins-loader): cover declaration hooks and visitor wiring
…ry-jest-mock

test(package-managers): use vitest mock type and cover readdir rejection
fix(info): show clear message when no @nestjs/* deps are declared
The TypeCheckerHost spec only exercised the runOnce diagnostic-throwing paths. Add coverage for behaviours that real users hit but had no regression protection:

- Early throw when tsconfigPath is missing or empty
- spinner.fail() is invoked on type errors and spinner.succeed() is not (this branch was added in v12 to replace the previous process.exit)
- runInWatchMode wires the compiler options with noEmit and preserveWatchOutput so the type checker never emits files
- Watch status callback dispatches: onTypeCheck on success, console.error on 'Found N errors' diagnostics, silent ignore for unrelated messages
- onProgramInit is scheduled on the next tick with the program ref
JSON.parse failures from a malformed nest-cli.json bubbled up as a raw
SyntaxError with no indication of which file was being parsed. Wrap the
parse call so the thrown error names the configuration file and includes
the underlying parser reason, matching the pattern introduced for
TsConfigProvider in #3332.
The existing spec only exercises the happy path of FileSystemReader and the
empty-input branch of readAnyOf, leaving the EACCES/EPERM permission flow
and the fall-through-on-error logic uncovered.

Add tests that:

- assert list() and read() pass the configured directory through to fs
- exercise readAnyOf falling through to a later file after ENOENT
- pin the EACCES and EPERM branches to ReaderFileLackPermissionsError,
  including the subtle behaviour that filePath sticks to the *first*
  permission-blocked file even when later files raise different errors
- confirm a successful later read still wins over an earlier permission
  error (no premature ReaderFileLackPermissionsError leak)
- verify errors without a string code property are ignored cleanly
- cover the ReaderFileLackPermissionsError class itself (instanceof,
  exposed properties, message formatting)

No production code changes.
The --parallel flag accepted any value parseInt could partially parse,
including 0, negative numbers, and non-numeric strings. parseInt('abc')
returns NaN, parseInt('-1') returns -1, and either value drove
runBuild's chunked loop into an infinite loop because i += concurrency
never advances toward appNames.length.

Validate the user-supplied value at the command layer with a clear
error message, and harden the action to fall back to unlimited
concurrency (= appNames.length) for any non-positive or non-finite
value to defend against programmatic callers passing through bad
input. Add unit tests covering negative, NaN, true, positive, and
zero (sequential) values.
…ctly when peer deps fail to load

When `webpack` / `rspack` peer dependencies fail to load, the defaults
factories wrap the error with install advice. Two issues with the
existing logic:

1. The error wrapping fired for any thrown error, including ones
   unrelated to a missing module (e.g. version mismatch, syntax error
   inside the dep). This sent users to reinstall packages that were
   already installed and hid the real failure. Now only errors with
   `MODULE_NOT_FOUND` / `ERR_MODULE_NOT_FOUND` codes are wrapped, and
   the original error is attached as `cause` for diagnostics.

2. The greedy regex `/Cannot find.*'([^']+)'/` extracted the last
   quoted segment in the message. For Node ESM-style errors like
   `Cannot find package 'foo' imported from '/abs/file.mjs'` it
   captured `/abs/file.mjs` and told the user to install that as a
   package. Switched to non-greedy so the first quoted segment (the
   actual missing package) is captured.

Adds tests for both webpack-defaults and rspack-defaults covering the
happy path, the ESM error format, fallback when the regex misses, and
the unrelated-error rethrow case.
…t-coverage

test(swc): cover watch mode and spinner failure in type-checker-host
…json-parse-error

fix(configuration): surface clear error for invalid nest-cli.json
…te-concurrency

fix(build): validate --parallel value and guard against infinite loop
…ermissions

test(file-system-reader): cover permission-error and fallback paths
…ts-error-handling

fix(compiler): preserve original error and extract package name correctly when peer deps fail to load
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants