576 Commits

Author SHA1 Message Date
Craigory Coppola ee0bc60a33 fix(misc): move plugin internal imports to devkit/internal (#36430)
## Current Behavior

First-party plugins reach into `nx` internals through deep subpaths
(`nx/src/executors/run-commands/run-commands.impl`,
`nx/src/generators/tree`, `nx/src/plugins/js/lock-file/lock-file`, …),
so there is no single boundary between the plugins and nx's internal
module layout.

## Expected Behavior

Those imports are routed through the `@nx/devkit/internal` barrel, and a
lint rule enforces the boundary. `nx/release` stays exempt as a public,
stable entry point for release-extension plugins.

### Note on `packages/nest/test-setup.ts`

This one is a latent-bug fix, not lint appeasement. The previous setup
installed the project-graph stub with
`jest.spyOn(require('nx/src/project-graph/project-graph'),
'createProjectGraphAsync')` at module scope. `jest.restoreAllMocks()`
undoes anything installed via `jest.spyOn`, and four nest suites —
`init`, `library`, `application` and `run-nest-schematic` — call it from
an `afterAll` hook. Since `test-setup.ts` is wired in through
`setupFilesAfterEnv` and runs once per test file, that `afterAll` tore
the stub down for the rest of the file, so any later `describe` block
silently fell through to the **real** `createProjectGraphAsync` instead
of the stub. Switching to a `jest.mock(...)` module factory fixes it:
module-registry substitution is not affected by `restoreAllMocks()`, so
the stub now survives the whole file as intended.

## Related Issue(s)

<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes NXC-4748

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-08-05 13:00:44 -04:00
polygraph-snapshot-app[bot] 44706cdfa2 fix(linter): use projectService for typed linting in flat configs (#35727)
## Current Behavior

Nx's ESLint generators emit typed-linting config using the legacy
`parserOptions.project` array regardless of the eslint config kind:

```js
languageOptions: {
  parserOptions: {
    project: ['apps/products/tsconfig.*?.json'],
  },
},
```

In flat configs, this has two recurring problems:

1. In TS solution-style workspaces, cross-project imports resolve to the
referenced project's `out-tsc/*.d.ts` output rather than `src/*.ts`,
which surfaces as `unexpectedReads` of dependency projects' declarations
during task-sandboxed `lint` runs.
2. Files outside any listed tsconfig fail with the classic "ESLint was
configured to run ... however none of those TSConfigs include this file"
error, forcing `ignores` / `.eslintignore` workarounds.

The flag for opting into typed linting is also named after its low-level
emission detail (`setParserOptionsProject`), which is no longer
accurate.

## Expected Behavior

For flat configs, generators emit typescript-eslint's recommended
project-service shape:

```js
languageOptions: {
  parserOptions: {
    projectService: true,
    tsconfigRootDir: import.meta.dirname, // or `__dirname` for cjs
  },
},
```

The project service resolves project references to source and handles
out-of-project files gracefully, fixing both issues above.

Legacy `.eslintrc` configs keep emitting `parserOptions.project`. They
are JSON, which cannot express the `__dirname` that `tsconfigRootDir`
needs.

A new generic `enableTypedLinting` flag replaces
`setParserOptionsProject`, which is deprecated for removal in v24. Both
flags behave identically during the deprecation window; users on the
deprecated flag get the new emission automatically.

## Implementation Details

- New `enableTypedLinting?: boolean` option added to every
ESLint-related generator schema. `setParserOptionsProject` marked
`x-deprecated` (schema.json) and `@deprecated` (schema.d.ts).
- New `isTypedLintingEnabled(options)` helper exported from `@nx/eslint`
centralizes the merge between the new and deprecated flags. Generators
normalize at the forwarding boundary so downstream calls receive a
single normalized flag.
- New AST helpers `generateProjectServiceParserOptions(format)` and
`generateTypedLintingFlatConfigOverride(format)` emit the projectService
block with `import.meta.dirname` (mjs) or `__dirname` (cjs).
- New `addTypedLintingToFlatConfig(tree, root)` re-emits the
projectService block after operations that strip overrides (e.g. cypress
`replaceOverridesInLintConfig`).
- New `inspectTypedLinting(content)` helper reports what a config
already configures for typed linting: `projectService`, the legacy
`parserOptions.project`, an explicit `projectService: false` opt-out, or
nothing. Angular `add-linting` uses it instead of the brittle
`tsconfig.*?.json` literal string match. It walks the exported config
value structurally, resolving const bindings, member access, ES
shorthand, wrapper calls like `tseslint.config(...)`, and the local
arrays a config spreads in, so parser options assembled indirectly are
still recognized.
- When a local `parserOptions` is built from an expression the walk
cannot read statically (a call, an imported reference, a dynamic key),
typed linting is left undecided, so the generator warns and leaves the
config unchanged rather than appending a block that could silently
convert a `project` setup to the project service. A config that only
spreads in another file has no local `parserOptions` of its own and
stays safe to append to.
- The appended block always sets `project: null` next to
`projectService: true`. ESLint merges `parserOptions` across flat config
entries and typescript-eslint rejects a merged truthy `project` beside
`projectService`, so a `project` inherited from a base config the
workspace spreads in would otherwise turn every type-checked file into a
parsing error. `project: null` wins that merge and is inert. Before this
change the generators emitted `project` themselves, so the combination
could not arise.
- A legacy config is read as JSON, JS or YAML. A bare `.eslintrc` can be
any of the three and takes precedence over every other config filename,
so reading only the first two dropped an existing
`parserOptions.project` when `@nx/angular:add-linting` carried it across
an override rewrite.
- The module system of a flat config is taken from its extension where
the extension is decisive (`.cts`, `.mts`), not from its content. An
`eslint.config.cts` written idiomatically with `export default` used to
read as ESM, so its typed-linting block got `tsconfigRootDir:
import.meta.dirname` and an added override got `parser: await
import(...)` (a top-level await), both of which its CommonJS output
rejects. The typed-linting path, `addOverrideToLintConfig`, and
`replaceOverridesInLintConfig` all derive the format from the extension
now; only `.js` and `.ts` fall back to content.
- The Nuxt flat-config template inlines the projectService block
directly because the generated `createConfigForNuxt(...).append(...)`
chain is a call expression, not an array literal that AST helpers can
append to.
- `@nx/cypress` and `@nx/playwright` added as optional peer dependencies
of `@nx/angular`, `@nx/expo` and `@nx/nuxt` so cross-plugin `typeof
import('@nx/cypress' | '@nx/playwright')` resolves to local source.
Angular's existing `@nx/cypress` declaration moves from
`devDependencies` to optional peer for consistency.
- `@nx/js` cannot reference `@nx/eslint` (eslint depends on js), so the
merge is inlined there.
- The typed-linting guide (`astro-docs/.../eslint.mdoc`) taught the
flat-config tab to fix a type-aware rule by adding
`parserOptions.project`, which now conflicts with what the generators
emit. Its flat tab teaches the project service instead, and
`enableTypedLinting` is documented.
- `--setParserOptionsProject=true` on a flat-config workspace now
produces a different output shape than before: the projectService block
rather than `parserOptions.project`. Intended, and covered by tests, but
it is a behavior change to an existing flag.
- No migration: existing generated `parserOptions.project` configs are
left untouched.
- `convert-to-flat-config` preserves the legacy shape during conversion.

> [!NOTE]
> Reviewing this PR surfaced pre-existing defects in how the ESLint
generators resolve a project's config file when its format differs from
the workspace's. They reproduce on master, are not introduced or made
worse by this change, and are being addressed in separate follow-up PRs.

<!-- polygraph-session-start -->
---
[View session information
↗](https://snapshot.app.trypolygraph.com/orgs/69cdc268b6aa527e4129c2b4/sessions/nxc-4473-e27e6e99)
<!-- polygraph-session-end -->

---------

Co-authored-by: Leosvel Pérez Espinosa <leosvel.perez.espinosa@gmail.com>
2026-07-24 12:22:58 -04:00
Jason Jean 5bda3c76d8 chore(testing): make node/express app generator specs package-manager-agnostic (#36369)
## Current Behavior

The `@nx/node` and `@nx/express` application generator specs snapshot
values that are derived from the **host** package manager, so running
them under a different package manager than CI (e.g. pnpm locally vs.
npm in CI) produces spurious snapshot churn:

- The **lock file name** in `prune-lockfile` outputs
(`package-lock.json` vs. `pnpm-lock.yaml`) — from `getPruneTargets`,
which calls `detectPackageManager()`.
- The **`runtimeExecutable`** in the generated VS Code debug config —
from `getPackageManagerCommand()`.

`detectPackageManager()` checks the cwd's lock files before the
invoked-package-manager fallback, and the nx repo root has a
`pnpm-lock.yaml`, so these specs detect pnpm locally regardless of
`npm_config_user_agent`. A contributor who runs the tests under pnpm and
regenerates snapshots ends up committing pnpm-specific values that then
fail on npm CI.

## Expected Behavior

The specs are deterministic regardless of which package manager runs
them. Both specs mock `@nx/devkit` to pin `detectPackageManager` to
`'npm'` and delegate `getPackageManagerCommand` to the real npm command
(mirroring the existing mock in the `@nx/react` application spec).
Because the committed snapshots are already the npm result, this
introduces **no** snapshot changes — only immunity to the host package
manager.

`detectPackageManager()`'s production behavior is unchanged (in a real
workspace the cwd is the workspace root); this is purely test
determinism, so there is no source change.

## Related Issue(s)

N/A — proactive test hardening. Surfaced while reviewing #35551, whose
generator snapshot churn was caused by this host-package-manager
sensitivity.

<!-- polygraph-session-start -->
---
[View session information
↗](https://app.trypolygraph.com/orgs/6a061dcb561c062131116eca/sessions/Make-node-express-app-generator-specs-package-manager-agnostic-a382908b)
<!-- polygraph-session-end -->
2026-07-16 22:25:42 -04:00
Jason Jean a88e49473f chore(repo): migrate to nx 23.1.0-rc.2 (#36269)
## Current Behavior

The repo is on nx 23.1.0-beta.7.

## Expected Behavior

The repo is on nx 23.1.0-rc.2. All 22 `nx`/`@nx/*` packages are bumped
to exactly 23.1.0-rc.0 (pnpm lockfile updated).

Migrations applied (one `nx migrate --run-migrations` pass):

- `@nx/js: 23-1-0-add-ignore-deprecations-for-ts6` — ensured
`"ignoreDeprecations": "6.0"` on 125 `tsconfig.json` files
(config-loader safety for TS6), added it to 2 tsconfigs carrying
TS6-deprecated options, and pinned pre-TS6 defaults on 4 chain-root
tsconfigs
- `@nx/js: 23-1-0-set-tsconfig-root-dir-for-ts6` — ran, no changes
needed

No AI migration prompts were generated.

Verification: project graph resolves (~140 projects); lint for nx,
devkit, js, eslint, workspace (+23 dependent tasks, including the native
build) and typecheck spot-checks pass with `--skip-nx-cache`. Full suite
runs in CI.

## Related Issue(s)

Part of the coordinated nx 23.1.0-rc.2 migration across nrwl repos (see
linked Polygraph session).

<!-- polygraph-session-start -->
---
[View session information
↗](https://app.trypolygraph.com/orgs/6a061dcb561c062131116eca/sessions/Migrate-nrwl-repos-to-nx-23.1.0-rc.0-b8c94700)
<!-- polygraph-session-end -->

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-07-13 13:09:48 -04:00
Jason Jean f22e75c8f0 feat(repo): enable the tsgo compiler workspace-wide (#35926)
## Current Behavior

The workspace builds and typechecks every first-party package with
`tsc`. An earlier attempt to enable the Go-based TypeScript compiler
(tsgo) for just `packages/nx` (#35047) was reverted (#35167) because
mixing compilers caused `.tsbuildinfo` version-mismatch cascades — a
downstream `tsc --build` would see a tsgo-stamped build info and
recompile everything.

## Expected Behavior

The entire `@nx/js/typescript` build + typecheck graph — packages,
graph, tools, `e2e/**`, and `nx-dev/ui-fence` — compiles with `tsgo`.
Now that every package is on `nodenext` (NXC-4538), a single compiler
runs across the whole graph, so there is no cross-compiler
`.tsbuildinfo` cascade.

### Changes

- Install `@typescript/native-preview` and set `compiler: "tsgo"` on
**both** `@nx/js/typescript` plugin entries (the package build/typecheck
entry and the e2e/nx-dev typecheck entry — the latter does `tsc --build`
with references into `packages/*`, so it had to move too).
- `tsconfig.base.json`: switch to `nodenext` module resolution, remove
`baseUrl` (tsgo removed it — `TS5102`), and set `strict: false` to
preserve current tsc behavior (tsgo defaults strict on).
- Align spec/e2e tsconfig `module` to `nodenext` (`TS5110`) and add
`customConditions: ["@nx/nx-source"]` to the spec tsconfigs so test
files resolve `@nx/*` subpaths to workspace **source** — notably
`@nx/devkit/internal-testing-utils`, which is excluded from devkit's
build so no declaration is emitted under nodenext.
- Source fixes surfaced by tsgo: two accidental workspace-root
(`baseUrl`-anchored) imports now use package names; `@nx/expo` `addJest`
gets an explicit `Promise<GeneratorCallback>` return type; `@nx/angular`
webpack-browser casts past the angular/webpack plugin type difference;
`graph/client-e2e` cypress global augmentations and `AUTWindow` casts;
`Task` mocks get the required `cache` field;
`update-repos`/`create-embeddings` config fixes.

## Validation

- `build-base`: **42 projects green** under tsgo.
- `typecheck`: **53 projects, 0 errors** under tsgo (entire
`@nx/js/typescript` graph).
- lint / test / e2e: pending CI.

> Note: tsgo's incremental/cached builds occasionally drop emitted
declarations (observed with devkit's `internal-testing-utils`); a
from-scratch build emits them. Worth watching in CI.

The remaining `tsc` users — `astro-docs` (astro check), `nx-dev`'s
Next.js build, and `@nx/angular`'s ng-packagr (ngc) — do **not** `tsc
--build` the packages, so they don't share `.tsbuildinfo` with the tsgo
graph and coexist safely.

## Related Issue(s)

Implements Linear NXC-4539 (builds on NXC-4538 — all packages on
nodenext). No GitHub issue to close.

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-06-18 16:34:46 -04:00
Leosvel Pérez Espinosa 192f66811d feat(angular): support angular v22 (#35851)
## Current behavior

Angular v22 is not supported.

## Expected behavior

Angular v22 should be supported.

BREAKING CHANGE: Angular v19 is no longer supported.

## Related issues

Fixes #35910 

<!-- polygraph-session-start -->
---
[View session information
↗](https://app.trypolygraph.com/orgs/6a061dcb561c062131116eca/sessions/angular-v22-3d830e58)
<!-- polygraph-session-end -->

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-06-18 11:17:51 -04:00
Leosvel Pérez Espinosa a44086e45f feat(linter)!: drop eslint v8 support (#36006)
## Current Behavior

Nx supported both ESLint v8 and v9. The `@nx/eslint` runtime,
generators, executors, and inferred plugin carried v8-specific branches,
version floors allowed v8-only ranges, and `useFlatConfig` chose flat vs
eslintrc purely by the installed ESLint version - which could select
flat config on an eslintrc workspace and crash generators.

## Expected Behavior

ESLint v8 support is removed and Nx targets ESLint v9+. Flat config is
the default for new workspaces, while existing eslintrc workspaces stay
supported: `useFlatConfig` now respects a root flat/eslintrc config file
and the `ESLINT_USE_FLAT_CONFIG` env var. Version floors, the lockfile,
and docs move to v9+. Generator specs across the linting-capable plugins
assert flat config by default and each retains at least one eslintrc
test.

BREAKING CHANGE: ESLint v8 is no longer supported. Nx requires ESLint v9
or later.

<!-- polygraph-session-start -->
---
[View session information
↗](https://app.trypolygraph.com/orgs/6a061dcb561c062131116eca/sessions/drop-eslint-v8-ad08cc1c)
<!-- polygraph-session-end -->

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: leosvelperez <leosvelperez@users.noreply.github.com>
2026-06-17 12:15:26 -04:00
Jason Jean 6598a0bb29 chore(core): rename supportsOptionalUpdates to supportsOptionalMigrations (#35924)
## Current Behavior

The migration config flag that opts a package into `nx migrate
--include`
(optional migrations) is named `supportsOptionalUpdates`. It is read
from a
package's `nx-migrations` / `ng-update` config and threaded through the
migrate
command. The name says "updates", but the feature it gates is optional
**migrations**, so the name is misleading.

## Expected Behavior

The flag is renamed to `supportsOptionalMigrations` everywhere it is
defined and
consumed:

- The `NxMigrationsConfiguration` / `NxPackageJson` type field and the
`readNxMigrateConfig` parsing in `packages/nx/src/utils/package-json.ts`
- The `--include` gate and related plumbing in
  `packages/nx/src/command-line/migrate/migrate.ts`
- The `"supportsOptionalMigrations": true` flag in every first-party
plugin's
  `package.json`
- The associated unit tests

This is a purely internal rename — the flag is both defined and consumed
inside
the nx repo, so no backwards-compat shim is required. Behavior is
unchanged.

## Related Issue(s)

N/A — internal naming cleanup.

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-06-10 11:11:15 -04:00
Jason Jean 27f9ae0a37 chore(repo): finish migrating builds off the workspace-root dist (#35915)
## Current Behavior

Following the local-dist migration (#35900), several projects were still
writing build or typecheck output to the shared workspace-root `dist/`:

- **Five packages' spec configs** — `@nx/angular-rspack`,
`@nx/angular-rspack-compiler`, `@nx/dotnet`, `@nx/maven`, and `nx` —
kept their `tsconfig.spec.json` `outDir` pointing at
`dist/packages/<name>/spec` even though their lib output had been
migrated to a local `dist`.
- **`tools/workspace-plugin`** built to the shared
`dist/workspace-plugin` (its conformance rules are loaded from the built
output).
- **The graph client** bundled to `dist/apps/graph` and emitted its
typecheck declarations to `dist/graph/client`; the graph libs wrote
spec/storybook typecheck output under `dist/out-tsc`.
- **nx-dev** lib/spec tsconfigs pointed their `outDir` at
`dist/out-tsc/...`.

Separately, astro-docs documentation generation resolved each plugin's
`schema.json` from its **built** `dist` (the migrated
`generators.json`/`executors.json` refs point at `./dist/src/...`).
Reading another project's build output tripped the task sandbox with
undeclared `dist/**/schema.json` reads.

## Expected Behavior

Each project builds to its own local directory, leaving the
workspace-root `dist/` alone:

- The five spec configs now emit to local `dist/spec`.
- `tools/workspace-plugin` builds to `tools/workspace-plugin/dist`; the
seven conformance rule paths in `nx.json` are updated, and
`main`/`typings` are repointed into `dist` so the `@nx/js/typescript`
plugin still infers its build target.
- The graph client bundles to `graph/client/dist` (the `nx` package's
`assets.json` input is updated to match); its typecheck output goes to a
local `out-tsc` so the emitted declarations stay out of the copied
bundle dir. Graph lib spec/storybook output moves to local `dist`.
- nx-dev lib/spec outputs move to local `dist` / `dist/spec`
(preventative — these were vestigial as no target runs `tsc` on most
nx-dev libs today).

astro-docs now reads plugin `schema.json` from source (the verbatim
copy), which also matches `astro-docs:build`'s already-declared
`packages/*/src/.../schema.json` inputs — removing the sandbox violation
without weakening cache correctness.

Validation: `nx build workspace-plugin` + `nx conformance` pass from the
new path; the graph client builds and copies into
`packages/nx/dist/src/core/graph` with no declaration leakage; `nx build
astro-docs` is green (753 pages, no schema-resolution errors); affected
graph and nx-dev `typecheck`/`lint`/`test` pass.

## Related Issue(s)

Follow-up to #35900 (local-dist build migration). No separate issue.

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-06-09 18:01:46 -04:00
Jason Jean 8039b7bae3 feat(misc): remove migrations prior to v21 in preparation for v23 (#35909)
## Current Behavior

The first-party Nx plugins still ship migration code (and
`packageJsonUpdates`) targeting Nx **v20 and earlier**. With v23 on the
way, those migrations are dead weight in the published packages and
their `migrations.json` manifests.

## Expected Behavior

All migrations prior to **v21** are removed across the first-party
plugins via the `@nx/workspace-plugin:remove-migrations` generator
(`--v=21`), keeping the two most recent prior majors (v21, v22) plus v23
— consistent with prior major-release prep (#30839 removed `< v19`,
#32904 removed `< v20`).

`packages/nx` and `packages/angular` are intentionally preserved (`nx`
is needed for `nx repair`; `angular` keeps its migrations until LTS
support is dropped).

Because the recent move to local dist builds (#35900) means every
plugin's `migrations.json` now references built (`./dist/...`) paths,
the generator can no longer auto-delete the corresponding source files.
The orphaned pre-v21 migration source directories were removed manually
as part of this change.

## Related Issue(s)

N/A — release preparation for v23.

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: FrozenPandaz <FrozenPandaz@users.noreply.github.com>
2026-06-09 16:48:09 +00:00
Leosvel Pérez Espinosa 38ca20b45e feat(core): extend nx migrate --include to any package that supports optional updates (#35905)
## Current Behavior

`nx migrate --include` only applies when migrating Nx itself; for any
other target the option is rejected, and eligibility is derived from
Nx-specific version math.

## Expected Behavior

`--include` now applies to any package whose `nx-migrations` /
`ng-update` config declares `supportsOptionalUpdates: true`, read from
the package metadata via the shared fetcher (registry-first, install
fallback). It accepts `required` (the target package and the related
packages it ships with), `optional` (the optional dependency updates
those packages recommend), or `all` (default). The `--interactive` /
`x-prompt` confirmation flow is deprecated in favor of `--include`
(removal slated for Nx v24; no hard error yet), and
`supportsOptionalUpdates: true` is set on the first-party packages.

<!-- polygraph-session-start -->
---
[View session information
↗](https://snapshot.app.trypolygraph.com/orgs/69cdc268b6aa527e4129c2b4/sessions/nxc-4517-37fe1336)
<!-- polygraph-session-end -->
2026-06-09 11:18:48 -04:00
Jason Jean 4bb6115625 chore(repo)!: migrate remaining packages to local dist build (#35900)
## Current Behavior

The remaining 14 publishable packages still build to the shared
workspace output `dist/packages/<name>` and use `commonjs`/`node` module
resolution. This is the last set of packages on the old layout — `nx`,
`devkit`, `js`, `web`, `node`, `webpack`, etc. were already migrated.

## Expected Behavior

Each of the remaining packages now builds to its own local
`packages/<name>/dist` directory with `nodenext` module resolution and
an `exports` map using the custom `@nx/nx-source` condition (so
in-workspace consumers resolve to source, while published/runtime
consumers resolve to built `./dist`). This matches the already-migrated
packages.

Packages migrated (one commit each):

- **Tier 0:** `@nx/react`, `@nx/esbuild`, `@nx/express`, `@nx/vue`,
`@nx/plugin`, `create-nx-workspace`, `@nx/angular`
- **Tier 1:** `@nx/react-native`, `@nx/next`, `@nx/remix`, `@nx/detox`,
`@nx/expo`, `@nx/nuxt`, `create-nx-plugin`

Notes:

- Kept the `./src/*` wildcard exports (the `@nx/nest` pattern) so no
consumer imports needed editing; the optional `./internal` lockdown is
deferred to a follow-up.
- Converted `ensurePackage` + `await import('@nx/...')` pairs to typed
`require()` (ESM dynamic import ignores `Module._initPaths` under
`nodenext`) and replaced `require('../../package.json')` self-references
with the dynamic `require(join('@nx/<name>', 'package.json'))` form.
- `@nx/angular` keeps its dual build: `tsc` (`build-base`) + ng-packagr
(`build-ng`); `ng-package.json` `dest`, both tsconfig `outDir`s, and the
publish `packageRoot` were relocated to `packages/angular/dist`.
- `create-nx-workspace`/`create-nx-plugin` were missing from the
migration board; tracked as NXC-4515 / NXC-4516.

Validation: `nx run-many -t build,lint` is green across all 14 packages
(including angular's real ng-packagr build and
`@nx/nx-plugin-checks`/`@nx/dependency-checks`). **Still needs CI
validation:** full e2e and the `nx release`/publish path (notably
angular's ng-packagr `packageRoot`).

## Breaking Changes

This is a breaking change because only `/internal` can be imported on packages now rather than importing from `src`. There is a migration to take care of this when necessary though.

## Related Issue(s)

Linear: NXC-3580, NXC-3582, NXC-3583, NXC-3584, NXC-3585, NXC-3587,
NXC-3589, NXC-3590, NXC-3596, NXC-3597, NXC-3600, NXC-3601, NXC-4515,
NXC-4516

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-06-08 17:38:20 -04:00
Jason Jean 3a33712668 fix(repo)!: migrate remaining first-party plugins to local dist build (M2-M5) (#35785)
## Current Behavior

The remaining 11 first-party Nx plugins (`@nx/webpack`, `@nx/rollup`,
`@nx/docker`, `@nx/gradle`, `@nx/rsbuild`, `@nx/web`, `@nx/node`,
`@nx/nest`, `@nx/module-federation`, `@nx/rspack`, `@nx/storybook`)
build into `../../dist/packages/<name>/` with `module: commonjs` and no
`exports` map. Releases publish from a separate dist directory.

That layout has the same drawbacks the prior migrations (devkit,
workspace, nx, js, jest, eslint, eslint-plugin, vitest, cypress,
playwright, vite) already addressed:

- workspace consumers reach into `@nx/<name>/src/*` against the old
layout, blocking nodenext / ESM-friendlier resolution
- a single PR cannot publish a coordinated set because each package's
dist must round-trip through `nx release`
- `release.preserveLocalDependencyProtocols` cannot be turned on
workspace-wide

This PR is the final batch in the "Nx Local Dist Migration" project — it
unblocks every still-unmigrated workspace package and finishes the
rollout that started with `@nx/devkit` in #34946.

## Expected Behavior

All 11 packages build to `packages/<name>/dist/` with:

- `tsconfig.lib.json` set to `module: nodenext`, `moduleResolution:
nodenext`, `composite: true`, `outDir: dist`, `declarationDir: dist`,
`tsBuildInfoFile: dist/tsconfig.tsbuildinfo`
- `package.json` `main`/`types` pointing into `dist/`, an `exports` map
with `@nx/nx-source`/`types`/`default` conditions, `typesVersions` for
legacy `moduleResolution: node` consumers, and a `files` allowlist
- `project.json` `release.version.preserveLocalDependencyProtocols:
true`, `manifestRootsToUpdate: ["packages/{projectName}"]`, and
`nx-release-publish.packageRoot: packages/{projectName}`
- `assets.json` `outDir` and eslint `dist` ignore updated
- `src/utils/versions.ts` switched to `require(join('@nx/<name>',
'package.json')).version` so the self-reference survives the new layout
- `README.md` renamed to `readme-template.md` with the build's
`copy-readme.js` invocation passing explicit src/dest paths, and root
`.gitignore` updated for the generated `README.md`
- `scripts/nx-release.ts` `packagesToReset` extended so `nx release`
properly snapshots/restores these source `package.json`s

The 11 packages keep the `./src/*` wildcard in their exports map for now
— the `@nx/devkit/internal`-style lockdown (Step 14b of the
`dist-build-migration` skill) is intentionally deferred to per-package
follow-up PRs to keep this one focused on the layout move. Two runtime
fixes that mirror earlier work in this project:

- `@nx/node` now declares `@nx/webpack` as a `workspace:*` devDep so its
TS compile resolves `@nx/webpack/src/utils/ensure-dependencies` via the
new exports map; the dynamic `import()` of that subpath was swapped to
`require()` after `ensurePackage` so the temp install is visible (same
pattern as the vite/vitest fix in #35743 — under `module: nodenext` a
dynamic `import()` is preserved as a true ESM import and bypasses
`Module._initPaths`).
- `@nx/web` had two dynamic `await import('@nx/eslint/internal')` /
`await import('@nx/vitest/generators')` call sites; both swapped to
`require()` with `typeof import(...)` type annotations for the same
reason.

`e2e/nx-build/src/nx-build.test.ts` was extended to verify the new
output paths for all 11 packages.

## Related Issue(s)

Fixes NXC-3576
Fixes NXC-3577
Fixes NXC-3579
Fixes NXC-3586
Fixes NXC-3588
Fixes NXC-3591
Fixes NXC-3595
Fixes NXC-3598
Fixes NXC-3599
Fixes NXC-3602
Fixes NXC-4474

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-06-05 13:30:19 -04:00
Jason Jean 193b8fce0b fix(misc): multi-version support compliance for rollup, webpack, and module-federation (#35860)
## Current Behavior

`@nx/rollup`, `@nx/webpack`, and `@nx/module-federation` pin their
primary third-party packages as **bundled `dependencies`**:

- `@nx/rollup` → `rollup` (`^4.14.0`)
- `@nx/webpack` → `webpack`, `webpack-dev-server` (v5)
- `@nx/module-federation` → `@module-federation/enhanced`,
`@module-federation/node`, `@module-federation/sdk` (v2)

This causes version conflicts with the version a workspace actually has
installed, prevents `nx migrate --first-party-only` from upgrading Nx
without dragging these along, and lets generators silently overwrite a
user's pinned version. Several cross-version `packageJsonUpdates`
migrations also bump packages **without `requires` gates**, so they fire
on workspaces outside the intended source range (e.g. the `@nx/webpack`
`sass-loader` v12 → v16 bump, and all of `@nx/module-federation`'s
`@module-federation/*` bumps).

## Expected Behavior

Each plugin declares the packages it invokes at runtime as **optional
peer dependencies** matching its real support window, so the workspace
controls the version:

- `@nx/rollup` → `rollup` `^3.0.0 || ^4.0.0`
- `@nx/webpack` → `webpack` / `webpack-dev-server` / `webpack-cli`
`^5.0.0` (the effective floor — the plugin already calls webpack-5-only
APIs such as `ids.HashedModuleIdsPlugin`, `webpack.sources`, and
`experiments.cacheUnaffected`)
- `@nx/module-federation` → `@module-federation/enhanced` /
`@module-federation/node` `^2.0.0` (`@module-federation/sdk` stays a
dependency — it is a type-only import surfaced in the public `.d.ts`)

Generators assert the supported floor (with a parameterized
`all-generators-enforce-floor` spec), preserve user-pinned versions
(`keepExistingVersions` now defaults to `true`), and install the
now-peer build tool for new workspaces. Because every workspace that
uses these tools already has the corresponding `@nx/*` plugin as a
direct dependency, a `packageJsonUpdates` bridge in each plugin installs
the peer into existing workspaces on `nx migrate` (and correctly skips
workspaces that only carry the plugin transitively). Cross-version
migrations are gated on their source range.

### Changes per plugin

- **`@nx/rollup` (NXC-4403)** — `rollup` → optional peer; floor assert
(v3) in all generators; `keepExistingVersions` default `true`; install
`rollup` on both the inferred-plugin and executor paths; bridge
migration for existing workspaces. No runtime branching/version map is
needed — the plugin's Rollup usage is already v2/v3/v4-agnostic.
- **`@nx/webpack` (NXC-4410)** —
`webpack`/`webpack-dev-server`/`webpack-cli` → optional peers (`^5`);
floor assert (v5) in all generators; `keepExistingVersions` default
`true`; gate the `sass-loader` v12 → v16 migration on `^12`; install
`webpack`/`webpack-dev-server` for new workspaces; bridge migration for
existing ones.
- **`@nx/module-federation` (NXC-4393)** —
`@module-federation/enhanced`/`node` → optional peers (`^2`); add
`requires` gates (on the `@module-federation/enhanced` source range,
mirroring the existing `@nx/angular` MF migrations) to every
`packageJsonUpdates` entry, and split the independent
`http-proxy-middleware` v2 → v3 bump into its own gated entry. This is a
runtime-only library (no generators/executors), so there is no floor
assert/spec.

## Related Issue(s)

Tracked in Linear under the "Multi-version supported across plugins"
milestone:

- NXC-4403 — `@nx/rollup`
- NXC-4410 — `@nx/webpack`
- NXC-4393 — `@nx/module-federation`

No GitHub issue to auto-close.

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-06-05 09:56:28 +02:00
Craigory Coppola 2ded1d14b2 feat(devkit)!: deprecate the standalone parameter of addProjectConfiguration (#35883)
## Current Behavior

`addProjectConfiguration` exposes a 4th `standalone` parameter
(defaulting to `true`). Nx only supports standalone projects, so any
value other than `true` is ignored — passing `standalone: false` simply
prints a warning and otherwise has no effect. Despite being a no-op, the
parameter is still part of the public signature, and a number of
first-party generators expose a matching `standaloneConfig` schema
option whose only job is to feed this dead parameter.

## Expected Behavior

`addProjectConfiguration` now presents two TypeScript overloads:

- a clean 3-argument signature (`tree`, `projectName`,
`projectConfiguration`), and
- a `@deprecated` 4-argument signature that still accepts `standalone`
for backwards compatibility (the runtime behavior is unchanged — it
continues to warn when set to `false`).

Existing callers keep compiling, but new ones are steered onto the
3-argument form and editors surface a deprecation strikethrough on the
old one.

The vestigial `standaloneConfig` option has been removed from the
affected generator schemas (`schema.json` + `schema.d.ts`) across
`expo`, `node` (application + library), `nest`, `storybook`, `workspace`
preset, `plugin`, and `express`, since it only ever populated the
ignored parameter. The remaining internal 4-argument call sites (`expo`,
`node`, and the `angular` ng-add e2e migrator) were reduced to the
3-argument overload, the `nest`/`workspace` passthroughs were cleaned
up, and the storybook generator specs no longer pass `standaloneConfig`.

Verified locally: `lint` passes for the 9 touched projects, `build`
(typecheck) passes for `nx`, `node`, `expo`, `nest`, and `workspace`,
and the storybook `configuration` spec passes.

## Related Issue(s)

N/A

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-06-04 16:05:17 -04:00
polygraph-snapshot-app[bot] 412a37a16c fix(misc): multi-version compliance for @nx/express, @nx/node, and @nx/nest (#35807)
## Current Behavior

**`@nx/express`**
- Peer dep declares `express: ^4.21.2` only; Express v5 has been ACTIVE
since 2025-03-31 and v4 is in MAINT, so the plugin's advertised window
doesn't match the upstream support window.
- Generators install a single literal `express ^4.21.2` regardless of
what's installed; no per-major routing for `express` or
`@types/express`.
- `keepExistingVersions` schema default is `false`, so re-running the
generator silently overwrites a user's pinned `express` version.
- No floor enforcement: a workspace on an unsupported (sub-floor)
`express` version sees no error.

**`@nx/node`**
- No `peerDependencies` for `express`, `koa`, or `fastify`.
Init/application generators write framework deps unconditionally,
ignoring what's installed.
- `keepExistingVersions` defaults to `false`; framework installs
override user pins.
- `migrations.json` `22.0.2` bumps `koa` from v2 to v3 with no
`requires` block — every workspace on koa v2 gets pushed to v3
unconditionally.
- `migrations.json` `20.4.0` mixes a cross-major fastify v4→v5 bump with
same-major express bumps under one entry, with no `requires`
(AND-semantics would gate same-major bumps incorrectly if added
naively).
- No floor enforcement for any framework.

**`@nx/nest`**
- `peerDependencies` block is missing from `package.json` entirely;
workspaces have no advertised compatible range for any `@nestjs/*`
package.
- Versions module has flat constants; the plugin ships v11-only even
though NestJS v10.4.x still receives upstream patches (N & N-1 baseline
calls for v10 + v11).
- `migrations.json` `21.2.0-beta.2` uses a bare key `nest` which is not
a real npm package — the entry is effectively a no-op. The real
cross-major v10→v11 bump for `@nestjs/common`, `@nestjs/core`,
`@nestjs/platform-express`, `@nestjs/testing` is missing, as is a
`requires` source-major gate.
- No floor enforcement for any NestJS version.

## Expected Behavior

**`@nx/express` (NXC-4390)**
- Per-major `versionMap` keyed on `express` major covering both
`express` and `@types/express` for v4 and v5; fresh installs default to
v5.1.0.
- `assertSupportedExpressVersion(tree)` (calls shared
`assertSupportedPackageVersion`) is the first statement of
`initGenerator` and `applicationGeneratorInternal`.
- Peer widened to `express: ">=4.0.0 <6.0.0"` (still optional).
- All `addDependenciesToPackageJson` call sites from generators pass
`keepExistingVersions ?? true`; schema defaults flipped to `true`. Init
now installs `@types/express` (previously a dead export).
- New `all-generators-enforce-floor.spec.ts` exercises every generator
entry at `subFloorVersion: '~3.21.0'`.
- Supported-versions docs page updated.

**`@nx/node` (NXC-4396)**
- Per-package `versionMap` + `versions(tree)` for `express` (v4+v5),
`koa` (v2+v3), `fastify` (v4+v5), and `@types/node` (v22+v24). Fresh
installs default to active LTS / latest stable.
- `assertSupportedFrameworkVersion(tree, schema.framework)` (dispatches
to one wrapper per framework) is the first statement of
`applicationGeneratorInternal`, only firing when `--framework` selects a
non-`none`/`nest` lane.
- Framework + `@types/node` installs route through `versions(tree)` and
pass `keepExistingVersions ?? true`. Init schema default flipped to
`true`.
- `migrations.json`: `22.0.2` koa v2→v3 gated with `requires: { koa:
">=2.0.0 <3.0.0" }`. `22.6.0` koa CVE patch gated bilaterally to v3
only. `20.4.0` split into the original same-major express bumps (no
gate) and a new `20.4.0-fastify` entry gated on `requires: { fastify:
">=4.0.0 <5.0.0" }`.
- Optional `peerDependencies` declared for `express`, `koa`, `fastify`.
Added `semver: "catalog:"` to deps (now imported by `versions.ts`).
`@nx/dependency-checks` allow-list updated.

**`@nx/nest` (NXC-4394)**
- Per-major `versionMap` covering NestJS v10 and v11 for the full
`@nestjs/*` family plus `rxjs` and `reflect-metadata`. Fresh installs
default to NestJS v11, with `reflect-metadata` bumped from `^0.1.13` to
`^0.2.0` to match v11's requirement.
- `assertSupportedNestJsVersion(tree)` is the first statement of the
`init`, `application`, and `library` generators.
- `ensureDependencies` and the init `addDependencies` helper route
through `versions(tree)` and pass `keepExistingVersions: true` (`??
true` on the init path); init schema default flipped to `true`.
- Optional `peerDependencies` declared for `@nestjs/core`,
`@nestjs/common`, `reflect-metadata`, `rxjs`. Added `semver: "catalog:"`
to deps and extended `@nx/dependency-checks` allow-list.
- `migrations.json` `21.2.0-beta.2` rewritten with real package keys
(the previous bare `nest` key was a typo / no-op) and gated on
`requires: { "@nestjs/core": ">=10.0.0 <11.0.0" }`. The entry now also
bumps `reflect-metadata` to `^0.2.0` for v11 compatibility.
- Supported-versions docs page updated.

## Related Issue(s)

Fixes NXC-4390
Fixes NXC-4396
Fixes NXC-4394

---------

Co-authored-by: Craigory Coppola <craigorycoppola@gmail.com>
2026-06-01 07:01:49 -04:00
Jason Jean 67d3502adc fix(linter)!: migrate @nx/eslint and @nx/eslint-plugin to local dist build (#35720)
## Current Behavior

`@nx/eslint` and `@nx/eslint-plugin` build into the shared
`dist/packages/<name>` directory at the workspace root. `@nx/eslint`
exposes no `exports` map, so first-party packages reach into
`@nx/eslint/src/*` for internal utilities.

## Expected Behavior

Both packages build locally into `packages/<name>/dist/` with `nodenext`
module resolution and an `exports` map, matching the already-migrated
`nx`, `devkit`, `js`, and `jest`.

### `@nx/eslint`

- `tsconfig.lib.json` / `tsconfig.spec.json` switched to the nodenext +
local-`dist` pattern.
- `package.json` gains an `exports` map (`.`, `./plugin`, `./internal`,
plus the JSON configs), `typesVersions`, and a `files` field.
- `project.json` gains `release` and `nx-release-publish` configuration.
- `executors.json` / `generators.json` / `migrations.json` paths
repointed to `./dist/src/...`.
- `src/utils/versions.ts` resolves its own `package.json` via a
`@nx/eslint` self-reference.
- **Exports lockdown:** the broad `src/*` surface is dropped; a curated
`@nx/eslint/internal` entry replaces it. 27 first-party consumer files
are rewritten from `@nx/eslint/src/*` to `@nx/eslint/internal`, and a
`rewrite-eslint-internal-subpath-imports` migration rewrites user
imports (symbol-aware — public symbols → `@nx/eslint`, internals →
`@nx/eslint/internal`).
- `@nx/vite` and `@nx/remix` imported `@nx/eslint` utilities without
declaring the dependency; `@nx/eslint` is now a declared dependency of
both.

### `@nx/eslint-plugin`

- Same nodenext + local-`dist` migration.
- `package.json` `exports` map covers `.`, `./angular`, `./nx`,
`./react`, `./typescript`. No `./internal` entry — nothing imports its
`src/*`.

## Related Issue(s)

Tracked by Linear NXC-3575 (`@nx/eslint`) and NXC-4475
(`@nx/eslint-plugin`). No GitHub issue.

<details>
<summary>Notes</summary>

- `@nx/js` and `@nx/workspace` also call `@nx/eslint` utilities but
cannot declare the dependency — `@nx/eslint` depends on `@nx/js`, so
declaring it back would create a cycle. They use a runtime
`require('@nx/eslint/internal')` (no static import), which `tsc` does
not resolve, so their builds are unaffected.
- Validation: `nx affected -t build,lint` is green (only the
pre-existing `MsbuildAnalyzer` dotnet build fails). Affected `:test`
failures are pre-existing environment/snapshot drift — `devkit:test` and
`nx:test` fail identically with zero dependency on these packages, and
`web`/`react` test failure counts match `master` exactly. The
`@nx/eslint`/`@nx/eslint-plugin` suites pass apart from the pre-existing
`workspace-rules-project.spec.ts` "TS solution setup" failure (also
fails on `master`).

</details>

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-05-19 13:58:40 -04:00
Jason Jean 0f298759a3 fix(testing)!: migrate @nx/jest to local dist build (#35713)
## Current Behavior

`@nx/jest` builds into the shared `dist/packages/jest` directory at the
workspace root and compiles with `commonjs` module resolution. It
exposes no `exports` map, and first-party packages (`@nx/detox`,
`@nx/node`) reach into `@nx/jest/src/*` for internal utilities. It also
still re-exports the long-deprecated `jestProjectGenerator` alias.

## Expected Behavior

`@nx/jest` builds locally into `packages/jest/dist/` with `nodenext`
module resolution and an `exports` map, matching the already-migrated
`nx`, `devkit`, and `js` packages.

- `tsconfig.lib.json` / `tsconfig.spec.json` switched to the nodenext +
local-`dist` pattern.
- `package.json` gains an `exports` map (`.`, `./plugin`, `./preset`,
`./plugins/resolver`, `./internal`, plus the JSON configs),
`typesVersions`, and a `files` field.
- `project.json` gains `release` (`preserveLocalDependencyProtocols`,
`manifestRootsToUpdate`) and `nx-release-publish` configuration.
- `executors.json` / `generators.json` / `migrations.json` factory and
schema paths repointed to `./dist/src/...`.
- `assets.json` outputs to the local `dist/` and copies
`src/**/schema.json`.
- `src/utils/versions.ts` resolves its own `package.json` via a
`@nx/jest` self-reference instead of a `../../` relative path, which
breaks once source compiles into `dist/`.
- The `@nx/jest:jest` executor imports its `schema.json` statically
rather than via a dynamic `await import`.
- A curated `@nx/jest/internal` entry (mirroring `@nx/devkit/internal`)
replaces deep `@nx/jest/src/*` imports for first-party consumers;
`@nx/detox` and `@nx/node` are routed through it.
- A migration (`rewrite-jest-internal-subpath-imports`) rewrites user
`@nx/jest/src/*` imports: named imports/exports of public symbols go to
`@nx/jest`, everything else to `@nx/jest/internal`.
- A migration (`rewrite-jest-project-generator`) rewrites
`jestProjectGenerator` imported from `@nx/jest` to
`configurationGenerator`.

The `dist-build-migration` skill is also updated to document the
symbol-aware routing rule for the migration step.

### Breaking Changes

- The deprecated `jestProjectGenerator` export is removed — its "removed
in Nx v22" notice is two majors overdue. It was always just an alias for
`configurationGenerator`; the `rewrite-jest-project-generator` migration
rewrites existing usages automatically.

## Related Issue(s)

Tracked by Linear NXC-3592 — `[M4] [Epic] Migrate @nx/jest to local
dist`. No GitHub issue.

<details>
<summary>Pre-create review (run before PR open)</summary>

### Critical

- The subpath-import migration originally rewrote every `@nx/jest/src/*`
import to `@nx/jest/internal`, which would silently break consumers
importing a public symbol (e.g. `findJestConfig`) that way. **Fixed** —
the migration now partitions named imports/exports: public symbols route
to `@nx/jest`, internals to `@nx/jest/internal`, splitting mixed
declarations into two.

### Important

- `export { ... } from '@nx/jest/src/*'` and additional `jest`/`vi` mock
helpers were not handled by the initial migration. **Fixed** — export
declarations are now partitioned like imports, and all eight mock-helper
methods are covered with tests.

### Suggestions

- `versions.ts` self-resolve and the inferred `build-base` target were
flagged; both verified correct (matches the `@nx/js` pattern;
`build-base` is inferred by `@nx/js/typescript` from
`tsconfig.lib.json`).
- Softened an over-broad "used only" comment in `eslint.config.mjs`.

</details>

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-05-17 17:39:48 -04:00
Jason Jean 4f14067546 fix(js): build to local dist and use nodenext (#35538)
## Current Behavior

`@nx/js` builds to the shared workspace-root `dist/packages/js/`
directory, uses CommonJS `module`/`moduleResolution`, has no `exports`
map, and relies on `.npmignore`-style filtering through assets.json
copies. This makes the package layout diverge from the new pattern
already adopted by `nx` and `@nx/devkit`.

## Expected Behavior

`@nx/js` follows the same local-dist build pattern as `nx` and
`@nx/devkit`:

- Builds to `packages/js/dist/` instead of `dist/packages/js/`.
- `tsconfig.lib.json` uses `module`/`moduleResolution: "nodenext"` with
`composite`, `rootDir: "."`, and `declarationDir: "dist"`.
- `package.json` declares an `exports` map with the `@nx/nx-source`
condition (workspace consumers resolve to `.ts` source, published
consumers get built `.js`), plus a `./src/*` wildcard so the ~296
existing internal imports of `@nx/js/src/...` keep working.
- `typesVersions` added for legacy `moduleResolution: "node"` consumers.
- Adopts an explicit `files` field on `package.json` instead of
asset-copying the root JSONs.
- `generators.json`, `executors.json`, `migrations.json` factory/schema
paths rewritten `./src/...` → `./dist/src/...` (matches `nx`). Workspace
dev still works via the `tryResolveFromSource` fallback in
`packages/nx/src/config/schema-utils.ts`.
- `README.md` → `readme-template.md`; build command writes the rendered
README to `packages/js/README.md`. Root `.gitignore` now ignores
`packages/js/README.md` and `packages/js/**/*.d.ts` (with
`!packages/js/src/**/schema.d.ts` exception for committed schema
declarations).
- ESLint flat config ignores `dist` and `**/*.d.ts`.
- `project.json` adds `release.version` config (with
`preserveLocalDependencyProtocols: false` so the not-yet-migrated
`@nx/workspace` dep is substituted to a concrete version at version-time
rather than left as `workspace:*` for pnpm publish to resolve from the
un-bumped source `packages/workspace/package.json`) and
`nx-release-publish.packageRoot`. The `build` target keeps its existing
`dependsOn: ["build-base"]` (cannot use `^build` because js's
`implicitDependencies` create a cycle through `eslint` /
`eslint-plugin`).
- `scripts/nx-release.ts`: adds `packages/js` to `packagesToReset` so
the source `packages/js/package.json` is restored after release.

## Related Issue(s)

Part of the ongoing migration of Nx packages to the local-dist build
layout (following `nx` and `@nx/devkit`).

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: FrozenPandaz <FrozenPandaz@users.noreply.github.com>
2026-05-15 18:41:15 -04:00
Jack Hsu 5f534cd5cd feat(misc): drop Node 20 support and bump @types/node (#35591)
## Current Behavior

CI matrices test Node 20. `@types/node` pinned to v20 in the repo
catalog and generator constants.

## Expected Behavior

Generator `typesNodeVersion` bumps to `^22.0.0`. 

Node 20 dropped from e2e + nightly matrices and ESLint docs (EOL Apr
2026). Repo catalog bumps to `^24.11.0` to match
`mise.toml`. Nightly `nodeTLS` renamed to `lowestNodeLTS` and bumped to
22.

## Related Issue(s)

NXC-4159
2026-05-07 11:52:58 -04:00
Jason Jean 59e2e51a42 chore(devkit): build devkit to local dist and use nodenext (#34946)
## Current Behavior

`@nx/devkit` builds to `<workspaceRoot>/dist/packages/devkit/` — outside
its own package directory. Other packages reach into that shared
workspace `dist/` to consume devkit, and consumers using
`moduleResolution: nodenext` need a custom Module._resolveFilename hack
to find it.

This is inconsistent with `nx` itself, which already builds to
`packages/nx/dist/` (#34111).

## Expected Behavior

`@nx/devkit` builds to `packages/devkit/dist/` and is consumed via a
standard `exports` map. Workspace consumers resolve through normal pnpm
symlinks; the `@nx/nx-source` condition still lets in-repo code resolve
to `.ts` source during dev.

This unblocks the rest of the workspace from migrating in the same
direction (a `dist-build-migration` Claude skill is included as a
per-package playbook).

## How to review

The PR has **307 files but only 3 categories of work**. Most of the diff
is mechanical.

### 1. The actual migration — review carefully (~10 files)

Devkit package config and the `@nx/nx-source` condition wiring:

- `packages/devkit/package.json` — `exports` map, `typesVersions`,
`files`, `type: commonjs`, `main`/`types` repointed to `dist/`
- `packages/devkit/tsconfig.lib.json` — `outDir: dist`, `nodenext`
module/resolution
- `packages/devkit/project.json` — `build-base` outputs,
`nx-release-publish.packageRoot`, `manifestRootsToUpdate`
- `packages/devkit/internal.ts` — re-exports `src/utils/*` and
`src/generators/*` symbols (replaces the `@nx/devkit/src/*` deep-import
pattern)
- `packages/devkit/eslint.config.mjs` — ignore `dist`
- `packages/devkit/{README.md → readme-template.md}` — README is now
generated, gitignored
- `.gitignore` — ignore the generated `packages/devkit/README.md`
- `scripts/nx-release.ts` — devkit's `package.json` now lives at
`packages/devkit/package.json`, not `dist/packages/devkit/package.json`
- `scripts/patched-jest-resolver.js` — adds `@nx/nx-source` condition

### 2. Mass import rewrites — already marked viewed (~205 files)

Every internal import of `@nx/devkit/src/utils/<x>` or
`@nx/devkit/src/generators/<x>` was rewritten to `@nx/devkit/internal`:

```diff
-import { foo } from '@nx/devkit/src/utils/bar';
+import { foo } from '@nx/devkit/internal';
```

I marked these files as **viewed** in the GitHub review UI to clear them
from the unread queue. They're across nearly every plugin (angular,
react, next, vite, webpack, rspack, expo, jest, etc.).

### 3. Follow-on cleanups (~10 files)

These appear in their own commits and are easy to review individually:

- **`cleanup(angular-rspack)`** — removes the `patchDevkitRequestPath`
runtime hack from 14 example configs; devkit now resolves through
standard node_modules (the MF patch stays — module-federation isn't
migrated yet).
- **`cleanup(devkit)` typedoc** — drops dead path mappings + redundant
include manipulation in
`astro-docs/src/plugins/utils/typedoc/typedoc.ts` (verified docs build
is byte-identical with/without the removed config).
- **`cleanup(devkit)` eslint ignores** — drops `'**/*.d.ts'` from
`packages/devkit/eslint.config.mjs` since `.d.ts` files only emit to
`dist/` (already ignored).
- **`fix(angular)` eslint quote** — quote-agnostic regex in
`e2e/angular/src/projects-linting.test.ts` so the test still disables
`prefer-standalone` after the angular-eslint generator switched to
double quotes (latent bug surfaced by CI).
- **`fix(testing)` jest migration** — removes a stray unused
`@nx/devkit/internal` import.
- **e2e test fallout** — `e2e/{angular,next}/src/*.test.ts` lose access
to `@nx/devkit/src/utils/string-utils` (e2e tests can't use
`/internal`); inline equivalents using public `names()` API.
`e2e/nx-build/src/nx-build.test.ts` updates the expected output path.

## Local verification

- `pnpm nx run-many -t test,build,lint -p devkit` ✓
- `pnpm nx run astro-docs:build` ✓ — devkit reference pages still
generate (148 markdown files; identical to a build with the path
mappings re-added as a control)
- `pnpm nx run-many -t build -p
examples-angular-rspack-csr-css,examples-angular-rspack-ssr-css,examples-angular-rspack-zoneless-csr-css,examples-angular-rspack-mf-host,examples-angular-rspack-mf-remote
--skip-nx-cache` ✓ — examples build without the devkit patch

## Known follow-up (not in this PR)

- `scripts/nx-release.ts` still has `hackFixForDevkitPeerDependencies()`
(a band-aid from #32406 that re-adds `<=` to devkit's `nx` peer-dep
range after `nx release version` strips it). The proper fix is to set
`preserveMatchingDependencyRanges: true` in
`packages/devkit/project.json` and delete the hack — that needs a
release dry-run to verify, so it's queued separately.

## Related Issue(s)

Follow-up to #34111.

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-05-01 22:42:16 -04:00
Craigory Coppola 2a412a3aea chore(repo): declare lazy-loaded packages as implicit deps (#35392)
## Current Behavior

Many Nx plugin packages lazy-load other plugins at runtime via
`ensurePackage()` or the `require('@nx' + '/...')` pattern. These
dependencies weren't declared in `package.json` at all, which meant:

- `pnpm install` in the monorepo happened to find them only via hoisting
from unrelated `devDependencies`.
- Published packages gave no install-time signal about what optional
peers a consumer might want.
- The dependencies were invisible to dependency-graph tooling,
supply-chain audits, and any future semantic-versioning logic.

## Expected Behavior

Every lazy-loaded plugin or tool is declared as `peerDependencies` +
`peerDependenciesMeta: { X: { optional: true } }`, matching the existing
convention in `@nx/angular`, `@nx/angular-rspack`, `@nx/eslint`, etc.
This gives consumers correct install/publish semantics without requiring
them to install peers they don't use.

For two packages — `@nx/workspace` and `@nx/js` — several of their
newly-declared peers transitively reverse-depend on them. Raw
package.json edges would cause `@nx/js:typescript-sync` to produce
circular TypeScript project references (TS6202). Those two packages use
`implicitDependencies: ["!name", …]` in `project.json` to drop the
cyclic graph edges, keeping the task graph and tsc builds cycle-free
without modifying the sync generator itself.

Commits:
1. **workspace + js**: 14 optional peers on `@nx/workspace`, 5 on
`@nx/js`, plus `implicitDependencies` negations in each `project.json`.
2. **Plugin packages**: `@nx/angular`, `@nx/expo`, `@nx/next`,
`@nx/nuxt`, `@nx/react-native`, `@nx/storybook`, `@nx/vite`, `@nx/vue`,
`@nx/web`. Cycles don't form for any of these, so no
`implicitDependencies` negations were needed. `@nx/js:typescript-sync`
populated the corresponding `tsconfig.lib.json` project references,
which are committed alongside the `package.json` changes.

## Related Issue(s)

Fixes #

## Test plan

see:
https://staging.nx.app/runs/m3Otv2Xl7m?sandboxViolations=true&query=%3Atest

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-04-29 17:15:43 -04:00
polygraph-app[bot] ca7671afd6 fix(bundling): include tsconfig solution input for webpack (#35477)
## Current Behavior

The `@nx/webpack` executor, inferred plugin, and config builder all call
`isUsingTsSolutionSetup()` and let its result influence task outputs
(e.g. `useTsconfigPaths`). However, the root `tsconfig.json` is not part
of the build task's cache inputs — so edits to root `tsconfig.json`
(`extends`, `files`, `include`) don't invalidate webpack task caches and
stale outputs are reused.

The same gap exists in `@nx/node`'s webpack-bundler branch of the
application generator: it calls `addBuildTargetDefaults(tree,
'@nx/webpack:webpack')` without the tsconfig input, even though the
parallel esbuild branch in the same file already passes
`TS_SOLUTION_SETUP_TSCONFIG_INPUT`.

## Expected Behavior

Matches the rollup fix in #35476: the root `tsconfig.json` is included
as a structured input (`{ json: '{workspaceRoot}/tsconfig.json', fields:
['extends', 'files', 'include'] }`) on `@nx/webpack:webpack` task
defaults and in the inferred plugin's build target inputs, so changes to
the relevant fields invalidate caches.

### Changes
- `packages/webpack/src/plugins/plugin.ts` — append
`TS_SOLUTION_SETUP_TSCONFIG_INPUT` to the inferred build target's
`inputs`. Also gate the targets cache on `NX_CACHE_PROJECT_GRAPH`
(mirrors the rollup PR) and update the spec accordingly.
- `packages/webpack/src/generators/configuration/configuration.ts` —
pass `'build', [TS_SOLUTION_SETUP_TSCONFIG_INPUT]` to
`addBuildTargetDefaults`.
- `packages/node/src/generators/application/lib/create-project.ts` —
same on the webpack branch (the esbuild branch already had it).
- `packages/webpack/src/plugins/plugin.spec.ts` — mock spreads
`requireActual` so the constant is real; sets/restores
`NX_CACHE_PROJECT_GRAPH`; snapshot updated to include the new input.

<!-- polygraph-session-start -->
---
[View session information
↗](https://snapshot.app.trypolygraph.com/orgs/69cdc268b6aa527e4129c2b4/sessions/73d1eed2)
<!-- polygraph-session-end -->

Co-authored-by: Leosvel Pérez Espinosa <leosvel.perez.espinosa@gmail.com>
2026-04-28 17:56:56 -04:00
Leosvel Pérez Espinosa 1d6f14dff3 fix(node): include tsconfig input in node-app esbuild scaffold (#35466)
## Current Behavior

The node app generator with `bundler=esbuild` does not include the
field-scoped `tsconfig.json` input needed for proper cache hashing. This
means builds may return stale cached results when the workspace's
`tsconfig.json` is edited (e.g., changing `extends`, `files`, or
`include` fields).

## Expected Behavior

The node app generator now includes `TS_SOLUTION_SETUP_TSCONFIG_INPUT`
as an input for `@nx/esbuild:esbuild` targets, ensuring cache hashes
properly reflect changes to the workspace root `tsconfig.json`.

Additionally, exports `TS_SOLUTION_SETUP_TSCONFIG_INPUT` from `@nx/js`
so other Nx packages can reuse this constant for their own executors and
plugins.
2026-04-27 21:58:31 -04:00
Leosvel Pérez Espinosa 4bbd4b1adc chore(repo): migrate nx repo to eslint v9 flat config (#35359)
## Current Behavior

The nx repo uses the legacy eslintrc format (`.eslintrc.json`,
`.eslintignore`) with ESLint v8 and outdated plugin versions. Several of
those plugins ship code that crashes under ESLint v9 (`context.getScope`
removed, etc.), and the `@nx/eslint` / `@nx/eslint-plugin` sources still
reference APIs removed in v9 (`Linter.defineParser`,
`Linter.defineRule`, classic `Linter.Config` shape).

## Expected Behavior

Every project in the repo uses an `eslint.config.mjs` flat config, the
ESLint ecosystem is on the latest v9-compatible versions, and
`@nx/eslint` / `@nx/eslint-plugin` sources compile and test cleanly
against the v9 type split.

### Main changes

- Replaced every `.eslintrc.json` / `.eslintignore` with an
`eslint.config.mjs`.
- Rewrote the root `eslint.config.mjs`:
- Exports a named `baseConfig` that leaf configs import, plus a default
export that opts out of linting (`**/*` ignored) so the root acts as a
shared base and isn't lint-checked directly.
  - Drops `FlatCompat` in favor of native plugin imports.
- Exports shared `reactHooksV7Off`, `e2eTestOnlyIgnores`, and a
`storybookConfigs` wrapper that filters v10's preset down to
`storybook/*` rules (the preset references foreign rules like
`import-x/*` that we don't register).
- Cleaned up leaf configs emitted by the generator: deduplicated file
entries, merged split `package.json` blocks, fixed the jsonc parser
namespace import after v3 dropped its default export.
- Scoped the 15 e2e projects to `*.test.ts` files only via a shared
export.
- Bumped the ESLint ecosystem to the latest v9-compatible versions:
`eslint@^9.39.4`, `@eslint/js@^9.39.4`, `@typescript-eslint/*@^8.58.2`,
`eslint-plugin-storybook@^10.3.5`, `eslint-plugin-react-hooks@^7.1.0`,
`eslint-plugin-jsx-a11y@^6.10.2`, `eslint-plugin-import@^2.32.0`,
`eslint-plugin-playwright@^2.10.1`, `eslint-plugin-cypress@^6.3.1`,
`eslint-plugin-jest@^29.15.2`, `toml-eslint-parser@^1.0.3`,
`jsonc-eslint-parser@^3.1.0`, `angular-eslint@^21.3.1`. Dropped
`@eslint/eslintrc`, `@types/eslint`, `@types/eslint__js`.
- Aligned `@nx/eslint` source with the v9 type split: `Linter.Config` →
`Linter.LegacyConfig`, `ESLint.Options` → `ESLint.LegacyOptions`
wherever the underlying shape is eslintrc.
- Rewrote `@nx/eslint-plugin` rule specs (`dependency-checks`,
`enforce-module-boundaries`) as flat-config inline tests since
`Linter.defineParser`/`defineRule` were removed in v9.
- Aligned react/angular/expo/react-native add-linting helpers with the
v9 type split.
- Migrated `tools/eslint-rules` specs from `TSESLint.RuleTester` (legacy
config, rejected by v9's flat Linter) to
`@typescript-eslint/rule-tester`; added `isolatedModules: true` so
ts-jest resolves the new types under `module: node16`.
- Downgraded the new react-hooks v7 rules to `off` via a shared export
so the migration doesn't require rewriting legacy code.
- Auto-fixed unused eslint-disable directives
(`linterOptions.reportUnusedDisableDirectives` defaults to warn in v9).
- Ignored `packages/workspace/**/__fixtures__/**` in lint and updated
the affected snapshot so the fixture matches what the jest generator
templates actually emit.

### Note on ESLint v10

This PR stays on ESLint v9. A few plugins we rely on
(`eslint-plugin-import`, `eslint-plugin-jsx-a11y`,
`eslint-plugin-react`) still don't declare v10 peer support. The jump to
v10 will happen in a follow-up PR once those plugins publish
v10-compatible releases.
2026-04-22 08:34:44 -04:00
Sai Asish Y cf9c96afcb fix(node): split package-manager exec command for VS Code launch.json (#35295)
## Current Behavior

When generating a `@nx/node:application` from a pnpm workspace, the
generated `launch.json` sets `runtimeExecutable` to the full
`getPackageManagerCommand().exec` string (e.g. `"pnpm exec"`). On
Windows, VS Code rejects this:

```
Can't find Node.js binary "pnpm exec": path does not exist.
Make sure Node.js is installed and in your PATH, or set the
"runtimeExecutable" in your launch.json.
```

The same issue affects npm (`npm exec --`) and yarn (`yarn exec`).

## Expected Behavior

`runtimeExecutable` should be a real binary path (`pnpm`, `npm`,
`yarn`), and the `exec` / `exec --` tokens should live in `runtimeArgs`.

## Fix

Split `getPackageManagerCommand().exec` on whitespace, feed the first
token into `runtimeExecutable`, and prepend the remaining tokens to
`runtimeArgs`. Bun (`bunx`) is unaffected because it has no space.

## Related Issue(s)

Fixes #35276
2026-04-15 11:26:15 +02:00
Leosvel Pérez Espinosa dc479c50a5 fix(js): stop generating baseUrl in tsconfig, use ./ prefix for path mappings (#34965)
## Current Behavior

Nx generators set `compilerOptions.baseUrl: "."` in generated tsconfig
files and write path mappings as bare relative paths (e.g.,
`my-lib/src/index.ts`). `baseUrl` is deprecated in TS 6 and removed in
TS 7.

## Expected Behavior

Nx no longer generates `baseUrl` in any tsconfig. Path mappings use `./`
prefix (e.g., `./my-lib/src/index.ts`), making them relative to the
tsconfig file without needing `baseUrl`. Existing user tsconfigs with
`baseUrl` continue to work correctly.

### Generator and template changes
- Remove `baseUrl` from all templates and generator code
- `addTsConfigPath` normalizes lookup paths with `./` prefix
- Move and remove generators handle `./`-prefixed paths correctly
- Angular secondary entry points and Remix server entry paths use `./`
prefix

### `resolvePathsBaseUrl` helper
New function (in `ts-config.ts`, duplicated in `register.ts`) that walks
the tsconfig `extends` chain to determine the correct directory for
resolving `paths` values. Finds where `paths` is defined, then looks for
the applicable `baseUrl` from that point toward the root — ignoring
child overrides that don't apply to the paths-defining tsconfig. When no
`baseUrl` applies, returns the directory of the tsconfig that defines
`paths`. All path resolver plugins and buildable-libs-utils use this
helper.

### Runtime and bundler fixes for baseUrl-less tsconfigs
- **Rollup**: resolve path mappings to absolute in compiler options
override using `resolvePathsBaseUrl`; use original tsconfig path (not
tmp) for resolution base
- **Module Federation**: add `workspaceRoot` to `resolve.modules` in all
8 MF plugin variants (Angular/React webpack, Angular/React rspack,
webpack SSR, rspack SSR, Angular rspack plugin, rspack plugin) so
workspace-relative expose paths resolve without `baseUrl`
- **Next.js/Jest**: null out SWC `resolvedBaseUrl` in generated jest
configs to prevent SWC from doing incorrect path alias resolution; Nx
jest resolver handles this via `resolvePathsBaseUrl`
- **register.ts**: use `resolvePathsBaseUrl` for correct path alias
registration
- **Path resolver plugins** (webpack, rspack, vite, expo, react-native,
jest, react component testing): use `resolvePathsBaseUrl` for correct
path resolution
- **buildable-libs-utils**: resolve tmp tsconfig paths to absolute so
they work without `baseUrl`
- **eslint-plugin**: handle `./`-prefixed paths in AST utils

## Related Issue(s)

Fixes #32958
2026-04-10 16:33:02 +02:00
Leosvel Pérez Espinosa 887fca4ac8 fix(repo): narrow copy-assets outputs to prevent overlap with build-base (#35097)
## Current Behavior

Broad globs in `assets.json` (`**/*.json`, `**/*.js`, `**/*.d.ts`) cause
`copy-assets` targets to claim cache ownership over files also produced
by `build-base` (tsc). Even though a recent change reordered
`copy-assets` to run before `build-base` (reducing the likelihood of the
race condition), the underlying task ownership model is still broken —
both targets claim overlapping files in their output patterns.

## Expected Behavior

Each target exclusively owns its output files. `copy-assets` only claims
non-tsc assets (templates, type declarations, native artifacts), and
`build-base` owns all compiler outputs.

## Changes

**37 `assets.json` files** — replaced broad extension globs with narrow,
destination-safe patterns: template dirs (`**/files/**`), schema type
declarations (`src/**/schema.d.ts`), non-tsc extensions (`.jar`,
`.node`, `.wasm`, `.md`), and explicit file paths for package-specific
assets.

**30 `tsconfig.lib.json` files** — removed `**/*.json` from `include` so
tsc only compiles TypeScript. JSON files are now handled by copy-assets
with explicit entries
(`@(package|executors|generators|migrations).json`,
`src/**/schema.json`).

**Exception:** jest and vite use `import('./schema.json')` which
requires JSON in tsconfig scope with `composite: true`. These keep
`src/**/schema.json` in tsconfig.

**vite** — excludes `test-utils.ts` from lib build (only used by specs)
and includes it in `tsconfig.spec.json`.
2026-04-01 17:56:09 -04:00
Leosvel Pérez Espinosa 37eb4ecde4 fix(bundling): bump esbuild for new projects to a version compatible with vite 8 (#35132)
## Current Behavior

Newly generated JS/esbuild-based projects still default `esbuild` to
`^0.19.2`. That conflicts with Vite 8 which requires `esbuild ^0.27.0`.

## Expected Behavior

Newly generated projects should default to an `esbuild` version
compatible with Vite 8. If a workspace already has `esbuild` installed,
generators should preserve that version instead of blindly bumping it,
and Vite init should fall back to Vite 7 with a warning when the
installed `esbuild` range is incompatible with Vite 8.

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2026-04-01 16:23:42 -04:00
Jason Jean a040a93791 fix(repo): add copy-assets plugin and migrate all packages from legacy-post-build (#34994)
## Current Behavior

Each package defines a `legacy-post-build` target in `project.json` with
inline asset copy configuration. Inputs are not accurately declared,
leading to sandbox violations in CI. The asset globs, ignores, and
outputs must be manually kept in sync across 37 packages.

## Expected Behavior

A `copy-assets` createNodesV2 plugin reads `assets.json` from each
package and automatically generates the target with:
- Inputs derived from asset globs (positive patterns first, then
negations)
- Outputs derived using the same dest logic as `CopyAssetsHandler`
- `dependentTasksOutputFiles` for gitignored build artifacts (jars,
native binaries)
- Automatic `outDir` exclusion from asset copies

## Changes

**New infrastructure:**
- Add `copy-assets` createNodesV2 plugin in `tools/workspace-plugin`
- Add `copy-assets` executor (simplified from `legacy-post-build` — just
copies assets, no package.json field manipulation)
- Extract `normalizeAssets` and `getAssetOutputPath` into reusable
module in `packages/js`
- Add `copyReadme` namedInput for copy-readme build targets

**Migration (all 37 packages):**
- Create `assets.json` for every package defining what to copy
- Remove all `legacy-post-build` targets from project.json files
- Remove `legacy-post-build` target defaults from nx.json
- Remove redundant `copy-local-native` target (replaced by
`.node`/`.wasm` in asset glob)

**Cleanup:**
- Remove dead config: `creator-files` globs, non-existent template
files, typo'd directory names
- Use root-level `tsconfig*.json` ignore instead of recursive (so
template tsconfigs in `files/` dirs are still copied)
- Replace `!(*.ts)` extglob patterns with explicit globs (extglobs don't
work correctly in Nx inputs)
- Fix gradle lint: add `buildTargets: ["build-base"]` and `tslib`
dependency
- Add jar outputs to maven `_package` target for correct
`dependentTasksOutputFiles` resolution

**Other fixes:**
- Exclude `.swc` directories from sandbox write checks
- Enable typecheck for `angular-rspack` packages (remove
`addTypecheckTarget: false`)
- Pin workspace-plugin deps to explicit versions, add `@nx/plugin` to
root package.json

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: FrozenPandaz <FrozenPandaz@users.noreply.github.com>
2026-03-27 11:49:24 -04:00
Colum Ferry 732a08c77d chore(core): build nx to local dist and use nodenext (#34111)
## Current Behavior

The `nx` package compiles its TypeScript output to
`../../dist/packages/nx/` (relative to the package root), which places
build artifacts outside the package directory at the repo root level
(`dist/packages/nx/`). This makes the package structure harder to reason
about, complicates the build pipeline, and doesn't align with how most
packages organize their output.

The package uses `"module": "commonjs"` with basic module resolution,
which limits future migration paths toward ESM.

## Expected Behavior

The `nx` package now builds to a local `dist/` directory within the
package itself (`packages/nx/dist/`). This is a cleaner, more standard
layout — like having your tools in your own toolbox instead of scattered
across the workshop.

### Key changes:

**Build configuration (`packages/nx/tsconfig.lib.json`):**
- `outDir` changed from `../../dist/packages/nx` to `dist` (local to
package)
- `module` changed to `nodenext` with `moduleResolution: nodenext`
- Updated `include` patterns to explicitly list source directories

**Package entry points (`packages/nx/package.json`):**
- `bin` paths updated: `./bin/nx.js` → `./dist/bin/nx.js`
- Added `"type": "commonjs"` explicitly
- Added comprehensive `exports` map with `@nx/nx-source` condition for
dev/test resolution back to TS source
- Added `postinstall` path update to `./dist/bin/post-install`

**Module resolution fixes:**
- Created `src/utils/handle-import.ts` — a CJS-first import utility that
falls back to ESM `import()` for ESM-only packages, providing a single
migration point for future ESM work
- Converted dynamic `await import()` calls to
`require(require.resolve())` pattern where needed to satisfy `nodenext`
extension requirements
- Plugin worker spawn path now uses correct `.ts`/`.js` extension based
on runtime context (source vs compiled)

**Test infrastructure:**
- Added custom `jest-resolver.js` for the `nx` package that resolves
`nx/...` imports using the `@nx/nx-source` exports condition, so tests
run against TS source
- Updated `jest.preset.js` with SWC transformer configuration
- Added chalk mock for test compatibility

**CI and tooling:**
- Conformance check updated to build `workspace-plugin` first (the Nx
Cloud runner lacks `@swc-node/register` for TS resolution)
- Conformance rule paths in `nx.json` now point to compiled
`dist/workspace-plugin/src/...` output
- Added `dist` to eslint ignore patterns to prevent linting compiled
output
- Added workspace-plugin build target and updated its dependencies

**Other fixes:**
- Various import path fixes across `create-nx-workspace`, gradle, and
other packages to work with `nodenext` resolution
- Updated e2e test paths to reference the new dist location
- Fixed `.gitignore` and `.npmignore` for the new output structure

## Related Issue(s)

Internal infrastructure improvement — no external issue.

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: Coly010 <Coly010@users.noreply.github.com>
Co-authored-by: FrozenPandaz <jasonjean1993@gmail.com>
2026-03-20 19:30:03 -04:00
Jack Hsu cb6452ce93 fix(misc): address security CVE cluster (copy-webpack-plugin, koa, minimatch) (#34708)
## Current Behavior

Several security-related issues reported:

1. **copy-webpack-plugin** (#34632): `@nx/webpack` and `@nx/next` pin
`copy-webpack-plugin@^10.2.4` which transitively depends on
`serialize-javascript@^6.0.1` (vulnerable) and `fast-glob` (supply-chain
risk).

2. **koa** (#34621): `@nx/module-federation` transitively pulls
`koa@3.0.3` via `@module-federation/dts-plugin`, which is vulnerable to
CVE-2026-27959 (Host Header Injection, fixed in koa 3.1.2).

3. **css-minimizer-webpack-plugin**: `@nx/webpack` pins `^5.0.0` which
also depends on vulnerable `serialize-javascript@^6.0.1`.

4. **@module-federation/enhanced**: Versions `<2.1.0` transitively
install vulnerable `koa` via `dts-plugin`.

5. **Next.js**: Versions `16.0.x` are vulnerable to GHSA-9g9p-9gw9-jx7f
(Image Optimizer DoS) and GHSA-5f7q-jpqc-wp7h (PPR Resume memory
consumption).

6. **minimatch** (#34701): User reports minimatch vulnerability, but the
Nx pnpm catalog already pins the patched version `10.2.4`. No code
change needed — users should delete their lockfile and reinstall.

## Expected Behavior

1. **copy-webpack-plugin** bumped to `^14.0.0` which uses
`serialize-javascript@^7.0.3` (patched). Added `noErrorOnMissing: true`
to all 3 copy-webpack-plugin usage sites to handle the v14 breaking
change where missing glob patterns now throw errors by default.

2. **css-minimizer-webpack-plugin** bumped to `^8.0.0` which uses
`serialize-javascript@^7.0.3` (patched).

3. **koa** bumped to `^3.1.2` in `@nx/node` versions.ts.
`@module-federation/dts-plugin@2.1.0` completely removes koa dependency.

4. **@module-federation/enhanced**, **runtime**, **sdk** bumped to
`^2.1.0` across all packages (`@nx/module-federation`, `@nx/react`,
`@nx/angular`, `@nx/rspack`). Added `noErrorOnMissing` fix for
`@module-federation/enhanced` 2.x `runtime-library-control.plugin.ts`
compatibility.

5. **Next.js** bumped to `~16.1.6` and `eslint-config-next` to
`^16.1.6`.

6. **minimatch** — no change needed, already resolved.

### Migrations added (22.6.0-beta.10)
- `@nx/module-federation`: Bump MF packages to `^2.1.0`
- `@nx/react`: Bump `@module-federation/enhanced` to `^2.1.0`
- `@nx/angular`: Bump `@module-federation/enhanced` to `^2.1.0`
- `@nx/node`: Bump `koa` to `^3.1.2`
- `@nx/next`: Bump `next` to `~16.1.6`

### Skipped
- **esbuild** (`<=0.24.2`, moderate severity, dev server only): Fix
requires breaking change jump from `^0.19.2` to `0.25+`. Will address
separately.

## Testing

Created a fresh Nx workspace with all affected plugins to verify `npm
audit` is clean after changes:

- `@nx/next` (nextapp)
- `@nx/webpack` (webpackapp)
- `@nx/rspack` (shell, remote1, remote2 via Module Federation)
- `@nx/module-federation` (shell + remotes with MF config)
- `@nx/react` (MF host/remotes)
- `@nx/node` + koa (api)

```
├── apps
│   ├── api                  # @nx/node (koa)
│   ├── nextapp              # @nx/next
│   ├── remote1              # @nx/react + rspack + MF
│   ├── remote2              # @nx/react + rspack + MF
│   ├── shell                # @nx/react + rspack + MF (host)
│   └── webpackapp           # @nx/webpack
```

Post-change audit result — only remaining issue is esbuild (moderate,
skipped intentionally):

```
# npm audit report

esbuild  <=0.24.2
Severity: moderate
esbuild enables any website to send any requests to the development server
and read the response - https://github.com/advisories/GHSA-67mh-4wv8-2f99
fix available via `npm audit fix --force`
Will install esbuild@0.27.3, which is a breaking change

1 moderate severity vulnerability
```

## Related Issue(s)

Fixes #34632
Fixes #34621
Fixes #34701
2026-03-05 17:56:19 -05:00
Victor Savkin e3eedf9e94 docs(misc): update the docs to use more direct language (#34264)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
2026-01-30 14:43:59 -05:00
Colum Ferry 2bd8ef3f72 feat(js): bump swc to latest versions (#34215)
## Current Behavior
SWC versions are a few minors behind.

## Expected Behavior
SWC versions are up to date and are being managed via PNPM Catalogs
2026-01-27 17:14:55 +00:00
Leosvel Pérez Espinosa 6f85b83fd3 cleanup(repo): format all files (#33902)
Format all files after Prettier v3 was merged.
2025-12-17 17:15:06 +00:00
Leosvel Pérez Espinosa 62c13c5477 feat(misc): support prettier v3 (#33898)
## Current Behavior

Nx doesn't generate projects with Prettier v3.

## Expected Behavior

Nx should generate projects with Prettier v3.

## Related Issue(s)

Fixes #30801
2025-12-17 09:26:13 -05:00
Colum Ferry 3db0fb2ce9 fix(node): use @swc/helpers instead of tslib when compiler is swc (#33885)
When using `@nx/node:library` generator with `--compiler=swc`, the
generator
was incorrectly adding `tslib` as a dependency instead of
`@swc/helpers`.

This change fixes two issues:
1. Pass the correct bundler (matching the compiler) to
jsLibraryGenerator
so it adds the correct helper dependency to the project's package.json
2. Update ensureDependencies to only add tslib when compiler is tsc

Fixes #31202
2025-12-16 17:02:42 +00:00
Hamza Khan 7f6db63f91 fix(node): sourceMaps option to sourceMap in webpack config (#33333)
## Current Behavior
when I run the below nx workspace command
nx g @nx/express:app kafka-service --directory=apps/kafka-service
--e2eTestRunner=none

the apps/kafka-service/webpack.config.json is generated with the below
lines

const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin');
const { join } = require('path');

module.exports = {
  output: {
    path: join(__dirname, 'dist'),
    ...(process.env.NODE_ENV !== 'production' && {
      devtoolModuleFilenameTemplate: '[absolute-resource-path]',
    }),
  },
  plugins: [
    new NxAppWebpackPlugin({
      target: 'node',
      compiler: 'tsc',
      main: './src/main.ts',
      tsConfig: './tsconfig.app.json',
      assets: ["./src/assets"],
      optimization: false,
      outputHashing: 'none',
      generatePackageJson: true,
      sourceMaps: true,
    })
  ],
};

there is no such property in NxAppWebpackPluginOptions called
**sourceMaps**, hence source maps are not generated
the correct property is **sourceMap**


## Expected Behavior
module.exports = {
  output: {
    path: join(__dirname, 'dist'),
    ...(process.env.NODE_ENV !== 'production' && {
      devtoolModuleFilenameTemplate: '[absolute-resource-path]',
    }),
  },
  plugins: [
    new NxAppWebpackPlugin({
      target: 'node',
      compiler: 'tsc',
      main: './src/main.ts',
      tsConfig: './tsconfig.app.json',
      assets: ["./src/assets"],
      optimization: false,
      outputHashing: 'none',
      generatePackageJson: true,
      sourceMap: true,
    })
  ],
};

## Related Issue(s)

Fixes #
2025-12-12 17:40:39 +00:00
Colum Ferry cac7f6c3e8 fix(node): set generatePackageJson:false for TS Solution workspaces (#33606)
## Current Behavior
In TS Solution setups, we generate webpack config with
`generatePackageJson: true`. This is confusing and unneeded.
It should be set to false in TS Solution repos.

## Expected Behavior
Set `generatePackageJson: false` in webpack config for TS Solution
Setups


Closes NXC-3521
2025-11-25 08:13:22 -05:00
Jason Jean 50cf84f758 chore(repo): update nx to 22.1.0-rc.2 (#33464)
Updating Nx from 22.1.0-beta.8 to 22.1.0-rc.2
2025-11-13 08:53:58 -05:00
Jack Hsu 62d0ad7f25 chore(repo): rename jest.config.ts to jest.config.cts to be compat with Node 24 strip types (#33401)
This PR is just for the repo itself to use `.cts` to explicitly use CJS
for jest config. Node 24 strip types so having `.ts` files with ESM
syntax even though we're previously transpiling them to CJS is a
problem.
2025-11-06 13:36:44 -05:00
Jack Hsu 9b5768e7fe fix(testing): use .cts config files for Jest 30+ to fix __dirname issues (#33349)
When using Jest 30 with SWC, users are seeing an error where `__dirname`
is not defined for ESM modules. This is because the `.ts` extension is
type-stripped by Node 22.17/24+ via checking for
[`process.features.typescript`](https://github.com/jestjs/jest/blob/fe7f28c9d1941f5c2726831cd9d9e479b401610e/packages/jest-config/src/readConfigFileAndSetRootDir.ts#L46).

This means that instead of using the `commonjs` we set for `ts-node`,
normal Node resolution kicks in, and is now treating `jest.config.ts` as
ESM.

This PR fixes this issue by using an explicit `.cts` extension, which
forces CommonJS that we assume for Jest configs.

Note: For Jest 29 or earlier we need to keep `jest.config.ts` since the
`.cts` extension is not supported prior to Jest 30.

There's also a fix for an existing issue where using `module.exports` of
anything else from `@types/node` will error out if `tsconfig.json` has a
`types` field but does not include `node`. See:
https://www.loom.com/share/7ebe3c90a70e4ec7bfa53cbcccaaea7d

Fixes #32236

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-06 10:25:39 -05:00
Leosvel Pérez Espinosa f76f1ce3df chore(repo): dogfood pnpm catalogs (#33232)
Dogfoods the Pnpm Catalogs feature in the Nx repo. This is the first
step to move all dependencies to Pnpm Catalogs definitions. More work
will be done incrementally later as we consolidate package versions
across the repository.

The initial list of dependencies moved to Pnpm Catalogs is:

- Angular packages
- React packages
- TypeScript packages
- Jest packages
- Rspack packages
- Some common utilities
2025-10-28 19:09:07 +00:00
Colum Ferry 3047fbda9e fix(core): should find dockerfiles to suggest installing docker plugin (#33234)
## Current Behavior
`nx init` does not search for `Dockerfile` patterns to suggest adding
the `@nx/docker` plugin.

## Expected Behavior
`nx init` finds and suggests `@nx/docker` plugin

Fixes NXC-3319
2025-10-24 11:23:50 -04:00
Colum Ferry 1002ad5198 fix(node): migrate to koa 3.0.3 (#33208)
Update `koa` to `3.0.3`
2025-10-23 16:10:45 +01:00
Jack Hsu 836034172c chore(repo): disable duplicated typecheck targets if the project already uses tsc to build (#32926)
This PR removes redundant `typecheck` targets from projects already
building with `tsc`.

- Add `addTypecheckTarget: false` to projects using `tsc` as
`build-base`.
- Exclude `e2e` and `nx-dev` projects from having `tsc` build inferred
but leave the `typecheck` target

Note: angular-rspack and angular-rspack-compiler has an issue where
`@nx/vite/plugin` is inferring the typecheck target. We may want to
check `addTypecheckTarget` for that plugin as well.

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->
Closes NXC-3208
2025-10-03 11:38:05 -04:00
Jack Hsu 4f9eeb4fb1 feat(webpack)!: remove deprecated deleteOutputPath and sassImplementation options (#32828)
The webpack package contains deprecated options that were marked with
TODO(v22) comments for removal:
- deleteOutputPath option
- sassImplementation option

These deprecated options were still being referenced in the codebase and
schema files, potentially causing confusion for users.

Remove the deprecated options from the webpack package to clean up the
API for v22:
- Remove deleteOutputPath option from the webpack executor and related
configurations (use Webpack's output.clean option instead)
- Remove sassImplementation option from the webpack executor and related
configurations (sass-embedded is now the default)
- Add a migration to automatically update existing workspaces that use
these deprecated options

Closes NXC-3108

---------

Co-authored-by: Colum Ferry <cferry09@gmail.com>
Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
2025-09-29 18:24:57 -04:00
Juri c7d540614c docs(misc): update new subtagline 2025-09-02 23:43:24 +02:00
Leosvel Pérez Espinosa ec6b707d13 fix(js): use a unique typescript custom condition name for the workspace (#32429)
## Current Behavior

Workspaces using the new setup with TS project references + package
manager workspaces are generated with a custom condition named
`development`. Projects have a conditional `development` entry point
that points to the source to improve the DX in the repo.

This condition name can cause issues with published libraries because
it's not unique, and some build tools automatically pick it up. While
using it locally in the workspace is safe because the source file
exists, this is not the case when the published library is used outside
the workspace.

## Expected Behavior

Workspaces using the new setup with TS project references + package
manager workspaces should be generated with a unique custom condition
named after the name in the root `package.json` file. Projects should
have a conditional entry point matching that name and pointing to the
source to improve the DX in the repo.

Users can customize the custom condition and name it something else as
long as it starts with `@nx-source/` so that Nx generators and helpers
can identify it. Additionally, in the unlikely scenario that the root
`package.json` doesn't have a `name` set, Nx will fall back to use
`@nx/source`.

This new behavior matches an emerging pattern of using scoped custom
export conditions for pointing to the source:

- [Announcing TypeScript
5.7](https://devblogs.microsoft.com/typescript/announcing-typescript-5-7/#path-rewriting-for-relative-paths:~:text=As%20a%20result%2C%20if%20you%E2%80%99ve%20been%20using%20a%20workspace%2Dstyle%20layout%20with%20multiple%20packages%20referencing%20each%20other%2C%20you%20might%20need%20to%20use%20conditional%20exports%20with%20scoped%20custom%20conditions%20to%20make%20this%20work%3A)
- [Live types in a TypeScript
monorepo](https://colinhacks.com/essays/live-types-typescript-monorepo#:~:text=Here%27s%20how%20a%20custom%20condition%20might%20look%20in%20your%20package.json.%20There%27s%20nothing%20special%20about%20the%20string%20%22%40colinhacks/zod%22%20here!%20It%20could%20be%20anything.)
- [Loading from
Source](https://github.com/isaacs/tshy?tab=readme-ov-file#loading-from-source)

## Related Issue(s)

Fixes #31332
2025-08-26 09:25:06 +02:00
Nicholas Cunningham 6ce1061471 fix(misc): update @types/node to v20.19.9 to support fetch API (#32092)
## Current Behavior

Nx currently installs an outdated `@types/node` version (18.16.9) which
lacks TypeScript definitions for the native `fetch` API introduced in
Node.js 18. This causes "fetch is undefined" TypeScript errors in NestJS
projects and other Node.js applications when using webpack transformers
or other build tools.

## Expected Behavior

The `fetch` API should be properly typed in TypeScript without requiring
additional type packages or workarounds. Users should be able to use the
native fetch API in Node.js applications without TypeScript compilation
errors.

## Related Issue(s)

Fixes #31637
Fixes https://github.com/nrwl/nx/issues/29714

---------

Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
Co-authored-by: Colum Ferry <cferry09@gmail.com>
2025-08-18 14:00:16 +01:00