master
459 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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>
|
||
|
|
820a3a6aaa |
fix(react): make module federation packages optional peer dependencies (#36492)
## Current Behavior Installing `@nx/react` pulls `@nx/module-federation`, `express`, `http-proxy-middleware`, `@svgr/webpack` and `@nx/rollup` as direct dependencies. Installing `@nx/next` pulls `@nx/webpack` and `@svgr/webpack`. Workspaces on esbuild, Vite or Rspack never run any of it. `@nx/module-federation` also pins `webpack` exactly while `@nx/webpack` installs a floating range, so workspaces end up with two webpack copies and Module Federation builds fail. ## Expected Behavior Module Federation packages become optional peers loaded lazily behind an `assertPackageIsInstalled` guard; `@svgr/webpack` is removed outright (SVGR support was removed in v23 and the v22 migrations inlined it to userland); `@nx/module-federation` declares `webpack` as an optional peer so it shares the app's copy. `23.2.0` migrations backfill the Module Federation packages for workspaces that need them, and `@svgr/webpack` for workspaces whose webpack or next configs reference it (the v22 migrations inlined the `require.resolve` without declaring the package). Same shape as #36310 for `@nx/angular`. ## Related Issue(s) NXC-4688 |
||
|
|
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>
|
||
|
|
c8f67bb236 |
fix(react-native): include migration docs in the built package (#36378)
> [!NOTE] > #36377 makes the migrations reference pages read the `documentation` key, which is what surfaces this package's missing file on the page. The two PRs are independent and can merge in any order. ## Current Behavior `@nx/react-native` declares a `documentation` file for `update-23-0-0-migrate-create-nodes-v2-import`, but its assets config never copies `src/migrations/**/*.md` into `dist`. Since the published package ships its built output from `dist`, it points at a file it does not contain: - `nx migrate --run-migrations --agentic` warns that the documentation file could not be resolved and drops it as agent context. - The migrations reference page renders the entry with only its one-line description, while the identical migration in sibling plugins renders its docs. Nothing caught this. `assertValidMigrationPaths` maps the published `./dist/...` path back to the source tree and asserts the source file exists, which it does, so the spec passes while the built package stays broken. ## Expected Behavior The markdown is copied into the built package, so the published tarball contains the file it references and both consumers read it. The `migration-markdown-assets` conformance rule closes the gap the spec leaves open, for every package rather than the 27 with a `migrations.spec.ts`. It checks each `prompt` and `documentation` reference against the files the assets config actually produces, catching a file that is never copied, one copied somewhere other than the declared path, and a reference resolving outside the built output. Rather than reimplementing the glob and output semantics, it drives the copy-assets pipeline with a collecting callback in place of the copying one, so the paths it compares against are the ones a build produces; it needs no `dist`. `toExecutorAssets` moves out of the copy-assets plugin so the rule and the plugin expand an `assets.json` the same way. The generated `copy-assets` targets are unchanged. Relative imports need a `.js` specifier under `nodenext`, which jest resolves back to the TypeScript sources through the added `moduleNameMapper`. ## Related Issue(s) Fixes NXC-4713 <!-- polygraph-session-start --> --- [View session information ↗](https://app.trypolygraph.com/orgs/6a061dcb561c062131116eca/sessions/fix-migration-docs-3961141b) <!-- polygraph-session-end --> --------- Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
ab099bdb94 |
fix(misc): declare continuous on inferred executor targets (#35941)
## Current Behavior
Inferred (`createNodes`) targets that set an `executor` but omit
`continuous` force project-graph normalization (`normalizeTarget`) to
read the executor's `schema.json` — resolved from `dist` via
`executors.json` — solely to infer the `continuous` flag. In this
workspace that surfaces as Nx Cloud sandbox violations (e.g.
`playwright:test` reading
`packages/playwright/dist/src/executors/merge-reports/schema.json`).
## Expected Behavior
The affected `createNodes` targets declare `continuous` explicitly, so
normalization short-circuits the `!('continuous' in target)` guard and
never reads the executor schema:
- `@nx/playwright` — `merge-reports`
- `@nx/docker` — `release-publish`
- `@nx/expo` — `install`, `prebuild`, `build`
- `@nx/react-native` — `sync-deps`
The `@nx/web:file-server` targets in storybook/vite/webpack/rspack/nuxt
already declare `continuous: true`, which is why they were never
affected.
## Related Issue(s)
N/A — Nx Cloud sandbox violation cleanup.
|
||
|
|
aa9ce7a469 |
fix(misc): rename createNodesV2 value usages in v23 migration, not just imports (#35930)
## Current Behavior
The v23.0.0 `migrate-create-nodes-v2-to-create-nodes` migration (shipped
in 22 plugins) rewrites **only import/export named bindings** of
`createNodesV2` to `createNodes` — it never touches value references in
the file body.
So a lone `import { createNodesV2 }` (or one deduped against an existing
`createNodes`) is renamed to `import { createNodes }`, but any value
usage of `createNodesV2` is left dangling:
```ts
import { createNodesV2 } from '@nx/js/typescript'; // → renamed to createNodes
addPlugin(graph, '@nx/js/typescript', createNodesV2, {}); // ← left as-is → TS2304: Cannot find name 'createNodesV2'
```
The existing specs only ever asserted on import lines, never on a file
that *uses* `createNodesV2` as a value, so the gap went unnoticed.
(Found while running the migration against a real workspace.)
## Expected Behavior
When the migration renames a local `createNodesV2` import binding, it
now also renames in-file **value references** to `createNodes`, so the
file still compiles. The rename is AST-scoped and conservative — it
skips:
- property accesses (`x.createNodesV2`) and qualified type names
- object-literal keys
- declaration names that shadow the import
- strings and comments (never `Identifier` nodes)
and expands a shorthand property (`{ createNodesV2 }` → `{
createNodesV2: createNodes }`) to preserve the key. Aliased imports (`{
createNodesV2 as cn }`) and re-exports keep their local name, so they
never trigger a usage rewrite.
Applied to all 22 plugin copies of the migration; regression tests added
for the value-usage cases (21 specs; `@nx/gradle`'s multi-specifier spec
keeps its existing structure and the shared logic is covered by the
others).
## Related Issue(s)
N/A — follow-up hardening of the v23 migration.
<!-- polygraph-session-start -->
---
[View session information
↗](https://app.trypolygraph.com/orgs/6a061dcb561c062131116eca/sessions/humble-koala-715f4ad1)
<!-- polygraph-session-end -->
---------
Co-authored-by: Jason Jean <jasonjean1993@gmail.com>
Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
|
||
|
|
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> |
||
|
|
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> |
||
|
|
b9a0582454 |
feat(misc): multi-version support compliance for detox, expo, react-native, and remix (#35885)
## Current Behavior The React Native ecosystem plugins (`@nx/detox`, `@nx/expo`, `@nx/react-native`) and `@nx/remix` were not compliant with the multi-version support initiative (`nx migrate --first-party-only`). None of them enforced a supported-version floor on their generators or preserved user-pinned versions, and: - **`@nx/detox`** pinned a single `detox` version with no floor; the `22.0.0` migration bundled a cross-major `@config-plugins/detox` bump together with same-major bumps, ungated. - **`@nx/expo`** had SDK 53/54 lanes but not the current SDK 55; several SDK-specific migrations ran unconditionally; `expo` was not declared as a peer. - **`@nx/react-native`** was hardcoded to a single, upstream-Unsupported RN `0.79.3` — no version map, no floor, `react-native` undeclared as a peer. - **`@nx/remix`** generators overwrote installed `react` / `vite` / `@remix-run/*` versions and had no floor enforcement. ## Expected Behavior Each plugin now follows the canonical multi-version compliance shape (`assertSupported<Pkg>Version` on every generator entry point, user-pin preservation via `keepExistingVersions`, source-major-gated migrations, and a parameterized floor spec): - **`@nx/detox`** — v20-only floor (`detox` is v20-only upstream; v19 has been unmaintained since 2022). The `22.0.0` migration is split so the cross-major `@config-plugins/detox` bump is gated on `expo >=53 <54`, while the `detox`/`jest-dom` bumps stay ungated for bare React Native + Detox workspaces. - **`@nx/expo`** — adds the **SDK 55** lane (RN `0.83.6`, React `19.2`) as the new default with `isExpoV55` detection (53/54 lanes retained); declares `expo` as a peer; floor at SDK 53; SDK-specific migrations gated with `requires`. - **`@nx/react-native`** — per-minor version map for the Active line (`0.83`/`0.84`/`0.85`, default `0.85`) routed through `versions(tree)`; declares `react-native` as a peer; floor at `0.83.0`; the `remove-deprecated-deps` migration gated on `react-native >=0.76 <0.79`. - **`@nx/remix`** — stays Remix v2 (React Router v7 remains in `@nx/react`) and documents the split; floor at `@remix-run/dev >=2.0.0`; generators preserve installed `react`/`vite`/`@remix-run/*` versions instead of overwriting them. A shared test utility in `@nx/devkit/internal-testing-utils` (`assertGeneratorsEnforceVersionFloor`) was extended to resolve generators declared via `implementation` as well as `factory`, so `@nx/remix` can use the shared floor spec. Supported-version docs for `@nx/expo`, `@nx/react-native`, and `@nx/remix` are updated. ## Related Issue(s) Tracked in Linear (not GitHub Issues): NXC-4385, NXC-4389, NXC-4400, NXC-4402. |
||
|
|
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> |
||
|
|
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 --> |
||
|
|
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>
|
||
|
|
0596b18dd5 |
chore(repo): update nx to 23.0.0-beta.24 (#35898)
Updating Nx from 23.0.0-beta.22 to 23.0.0-beta.24 --------- Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com> |
||
|
|
5e8ee67275 |
fix(react-native): fix TS2742 build error and retarget rollup/webpack migrations to beta.24 (#35896)
## Current Behavior - The `@nx/react-native` library build (`tsc --build tsconfig.lib.json`) fails with **TS2742**. The exported `addJest` helper has no explicit return type, so TypeScript infers `GeneratorCallback` and can only name it through a non-portable, pnpm-hashed `@nx/devkit` path when emitting the declaration file. - The `rewrite-rollup-internal-subpath-imports` and `rewrite-webpack-internal-subpath-imports` migrations are scheduled to run at `23.0.0-beta.25`. ## Expected Behavior - `addJest` is explicitly annotated as `Promise<GeneratorCallback>` (imported from `@nx/devkit`), giving `tsc` a portable name to emit. The library now builds cleanly. - Both internal-subpath-import migrations are retargeted to `23.0.0-beta.24` to align with the rest of the `23.0.0` migration batch. ## Related Issue(s) N/A |
||
|
|
94d9358e0a |
feat(core): add migrations for createNodesV2 -> createNodes rename (#35893)
## Current Behavior #35386 renamed the canonical plugin API to drop the `V2` suffix — both the exported `createNodesV2` **value** in first-party plugins (now `createNodes`) and the `*V2` **types** in `@nx/devkit` (`CreateNodesV2`, `CreateNodesContextV2`, `CreateNodesResultV2`, `CreateNodesFunctionV2`, `NxPluginV2`). The old names are kept as deprecated aliases, so existing code keeps compiling — but there is no migration to move workspaces onto the canonical names, and `@nx/angular/plugin` / `@nx/vitest` only re-exported `createNodesV2` from their public entry points (the rename was incomplete there). ## Expected Behavior - **Per-plugin value migration** (`migrate-create-nodes-v2-to-create-nodes`): every first-party plugin that publicly exposes `createNodesV2` ships a migration that rewrites named imports/re-exports of `createNodesV2` from that plugin's public specifier(s) to `createNodes` (22 plugins, incl. `@nx/gradle` + `@nx/gradle/plugin-v1`, `@nx/js/typescript`, `@nx/react/router-plugin`). - **devkit type migration** (`rename-create-nodes-v2-types`): rewrites imports/re-exports of the deprecated `*V2` types from `@nx/devkit` to their canonical names (`CreateNodesResultV2` → `CreateNodesResultArray`; the unrelated `CreateNodesResult` is left alone). - `@nx/angular/plugin` and `@nx/vitest` now also re-export `createNodes`, completing the rename so the migration targets resolve. All migrations are AST-based, modeled on devkit's `update-deep-imports`: they handle `as` aliases, dedupe when both names are already imported, preserve `type` modifiers and default imports, and leave strings/comments, dynamic `import`/`require`, and unrelated specifiers untouched. Each has unit + tree-runner specs and a `.md` doc, registered at `23.0.0-beta.5`. ## Related Issue(s) Follow-up to #35386. No issue to close. |
||
|
|
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> |
||
|
|
44ceb6bd7f |
feat(core)!: rename CreateNodes V2 types to canonical OG names (#35386)
## Current Behavior The canonical plugin API types are suffixed with `V2` (`CreateNodesV2`, `CreateNodesContextV2`, `CreateNodesFunctionV2`, `CreateNodesResultV2`, `NxPluginV2`), left over from when the V1 signatures existed. V1 was removed in Nx 22 (#32951), freeing up the un-suffixed identifiers but leaving the V2 names as the public API. ## Expected Behavior The un-suffixed names (`CreateNodes`, `CreateNodesContext`, `CreateNodesFunction`, `NxPlugin`, plus a new `CreateNodesResultArray`) become the canonical public types. The `V2` identifiers remain as `@deprecated` type aliases so plugin code authored against them keeps compiling. The plugin loader still checks both `plugin.createNodes` and `plugin.createNodesV2` property names at runtime, so third-party plugins that export under either name continue to load. ### Rename map | Old (now `@deprecated` alias) | New canonical | |-------------------------------|-----------------------| | `CreateNodesV2<T>` | `CreateNodes<T>` | | `CreateNodesContextV2` | `CreateNodesContext` | | `CreateNodesResultV2` | `CreateNodesResultArray` | | `CreateNodesFunctionV2<T>` | `CreateNodesFunction<T>` | | `NxPluginV2<T>` | `NxPlugin<T>` | ### Changes - `packages/nx` internals (plugin loader, isolation worker, utils, lock-file, package/project.json plugins, specs) switched to canonical types. - `packages/devkit` utilities (`addPlugin`, `findPluginForConfigFile`, `targetDefaultsUtils`, `calculateHashForCreateNodes`, `replaceProjectConfigurationsWithPlugin`, `getNamedInputs`, `executor-to-plugin-migrator`, etc.) migrated to canonical types. `addPlugin` now registers plugin objects with `createNodes:` as the canonical key. - 50 first-party plugin packages updated to canonical type names. Seven packages whose only primary export was `createNodesV2` now export `createNodes` as the primary value, with `createNodesV2 = createNodes` preserved as a backward-compat alias for older Nx consumers. ## Related Issue(s) N/A |
||
|
|
294344300f |
cleanup(core): use calculateHashesForCreateNodes for batch hashing in plugins (#35561)
## Current Behavior 15 inferred plugins call `calculateHashForCreateNodes` once per project inside their `createNodesInternal` callback. The single-project helper triggers a workspace-context glob per project, even when many projects are being processed in the same `createNodesV2` invocation that all share `context.workspaceRoot`. There is already a batch helper, `calculateHashesForCreateNodes`, that runs a single multi-glob hash across all project roots — only `vite`, `vitest`, `docker`, and `maven` use it today. `nuxt` even has a `TODO(@nrwl/nx-vue-reviewers): This should batch hashing like our other plugins` comment to that effect. In the same per-project path, several plugins also re-invoke `getLockFileName(detectPackageManager(context.workspaceRoot))` once per project, which probes the lockfile redundantly. ## Expected Behavior Each affected plugin's `createNodes` callback now does the per-workspace work upfront: - Pre-filter `configFiles` into `validConfigFiles` + `projectRoots` (parallel arrays) using the existing sibling-file / metro / expo / remix-compiler checks. - Detect the package manager once and derive `pmc` and `lockFileName` from it. - Call `calculateHashesForCreateNodes(projectRoots, options, context, additionalGlobsByProject)` once. - Pass the pre-computed hash by index into `createNodesInternal` (4th `idx` arg from `createNodesFromFiles`). Plugins migrated: - `angular`, `cypress`, `detox`, `expo`, `gradle` (v1 + v2 nodes), `next`, `nuxt`, `playwright`, `react-native`, `remix`, `rollup`, `rsbuild`, `storybook`, `webpack`. `gradle`'s exported `makeCreateNodesForGradleConfigFile` factory gains an optional `hashes?: string[]` parameter so external callers retain the previous single-shot behavior; when a `hashes` array is supplied, each callback invocation picks `hashes[idx]`. `nuxt`'s TODO comment is removed since the batching is now in place. `rspack` was not migrated — it does its own hashing with `hashFile` + `hashArray` + `hashObject` and never used `calculateHashForCreateNodes`. For `playwright`, the `additionalGlobs` array is per-project (each project's `externalTsconfigInputs`), so the call passes `projectRoots.map((_, idx) => [lockFileName, ...externalTsconfigInputsByIdx[idx]])`. All affected plugin specs (cypress, next, storybook, rsbuild, angular, playwright, webpack, rollup, nuxt, remix, gradle v1 + v2) pass locally. ## Related Issue(s) N/A — this is an internal refactor / performance cleanup, not a fix for a reported issue. --------- Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com> |
||
|
|
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>
|
||
|
|
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> |
||
|
|
7bdb937ea0 |
chore(repo): align direct-declaration version drift via catalog (#35639)
## Current Behavior Workspace `package.json` files declare common dependencies with mixed literals and ranges (e.g. `webpack: 5.101.3` in root vs `^5.101.3` in plugin consumers). pnpm materializes the resulting permutations as separate peer-resolution variants in the lockfile, even when every consumer resolves to the same version. ## Expected Behavior Declarations route through `pnpm-workspace.yaml` catalogs. Redundant peer-resolution variants collapse and the catalog becomes the single source of truth for cross-package version alignment. Lockfile shrinks by ~1,980 lines. ## Implementation Details ### Catalog changes - **New named catalogs:** `catalogs.css` (postcss family + `less-loader` / `sass-loader`), `catalogs.tailwind` (`@tailwindcss/postcss`, `tailwind-merge`), `catalogs.vite` (`vitest`). - **Default `catalog:` additions:** `@babel/core`, `@heroicons/react`, `@module-federation/enhanced`, `@playwright/test`, `@svgr/webpack`, `ajv`, `gpt3-tokenizer`, `http-proxy-middleware`, `http-server`, `jsonc-parser`, `next-seo`, `ora`, `react-textarea-autosize`, `rxjs`, `tmp`, `tree-kill`, `verdaccio`, `webpack`, `webpack-dev-server`. - **`catalogs.eslint` additions:** `@typescript-eslint/type-utils`, `@typescript-eslint/utils`, `eslint-config-prettier`. - **Existing-catalog routings:** `semver`, `tslib`, `typescript` routed at additional consumers. ### Per-dep notes - `ora` cataloged at `^5.3.0` — caret preserves `<6.0.0` since `ora` 6+ is ESM-only and Nx packages are CommonJS. - `@module-federation/enhanced` (`^2.3.3`) and `verdaccio` (`^6.3.2`) cataloged at security floors (2.3.1 had a compromised `axios`; `verdaccio` <6.3.2 carried a vulnerable `handlebars`). ### Out of scope - **Transitive-only drift** in `yaml`, `less`, `esbuild` — would require `pnpm.overrides`, not catalog routing. - **`packages/vitest` peer range** `^1 || ^2 || ^3 || ^4` — left alone (tightening is breaking for downstream consumers). - **Peer ranges spanning multiple majors** (`next`, `@nuxt/*`, `@rsbuild/core`, `metro-*`, `nx`, `@typescript-eslint/parser`, etc.) — intentional cross-major support contracts. - **Cross-major direct declarations** (`storybook`, `loader-utils`, `memfs`, `cypress`, `eslint`, `vite`, etc.) — each is its own migration. - **`@nx/devkit` / `@nx/js` literal `23.0.0-beta.4`** in `tools/workspace-plugin` — stale workspace reference; not catalog-fixable. - **`verdaccio` peerDep `^6.0.5`** in `packages/js` — not tightened to security floor to avoid changing the peer constraint for downstream consumers. ### Note on `pnpm dedupe` Evaluated separately and abandoned — `pnpm dedupe` actively bumps minor versions across declared ranges, which cascaded into 21 plugin `:test` snapshot regressions when attempted (#35628). The targeted catalog routing here avoids that class of change. --------- Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com> |
||
|
|
06bffdb044 |
feat(misc)!: remove deprecated js option from component generators (#35616)
## Current Behavior The `@nx/react`, `@nx/react-native`, and `@nx/expo` component generators expose a `--js` option to generate JavaScript files instead of TypeScript. The option was deprecated in #29111 (targeted for removal in Nx v21) in favor of including the file extension directly in the `path` argument: ```sh nx g @nx/react:component mylib/src/lib/foo.jsx ``` We are now on Nx v23 — past the deprecation window — but the option still exists. ## Expected Behavior The `--js` option is removed from the component generators in `@nx/react`, `@nx/react-native`, and `@nx/expo`. Users include the desired file extension directly in the `path` argument; the path's extension drives whether `.tsx`, `.jsx`, `.ts`, or `.js` files are generated. When no extension is provided, the generators default to `.tsx`, matching prior behavior. The library generators in those three packages still keep their own `--js` option (out of scope for this change). Internally they now encode `.js` into the `path` they pass to the component generator instead of forwarding the now-removed `js` flag. ### BREAKING CHANGE The `--js` option has been removed from: - `@nx/react:component` - `@nx/react-native:component` - `@nx/expo:component` Migration: include the file extension in the `path` argument, e.g. `nx g @nx/react:component mylib/src/lib/foo.jsx`. ## Related Issue(s) Linear: [NXC-3665](https://linear.app/nxdev/issue/NXC-3665/common-remove-js-option-from-component-generators) |
||
|
|
a2dfac6bfa |
feat(misc)!: deprecate executors with inferred-plugin replacements (#35576)
## Current Behavior Most Nx executors that have an inferred-plugin alternative (`@nx/<pkg>/plugin`) and a `convert-to-inferred` generator are still wired up like first-class citizens. There is no signal — at scaffold time, schema browsing, or task execution — that they are on a path to removal, and the existing `cypress`/`detox` deprecation messages are inconsistent with the canonical pattern shipped most recently. ## Expected Behavior Every executor that has an inferred-plugin migration target is now deprecated through three surfaces, matching the canonical pattern: - **Runtime warning.** The executor logs that it is deprecated, will be removed in Nx v24, and points at `nx g @nx/<pkg>:convert-to-inferred`. - **Schema-root `x-deprecated`.** Surfaces in editor / Nx Console / `nx show project` views. - **Generation-time warning.** When a generator is about to scaffold a target that uses one of these executors because the corresponding inferred plugin isn't registered, it warns at generation time and points at the same migration path. All warnings link to <https://nx.dev/docs/guides/tasks--caching/convert-to-inferred>. ### Executors deprecated in this PR | Package | Executors | |---|---| | `@nx/webpack` | `webpack`, `dev-server` | | `@nx/vite` | `build`, `dev-server`, `preview-server` | | `@nx/rollup` | `rollup` | | `@nx/next` | `build`, `server` | | `@nx/remix` | `build`, `serve` | | `@nx/jest` | `jest` | | `@nx/playwright` | `playwright` | | `@nx/eslint` | `lint` | | `@nx/storybook` | `storybook`, `build` | | `@nx/rspack` | `rspack`, `dev-server` | | `@nx/expo` | `build`, `export`, `install`, `prebuild`, `run`, `serve`, `start`, `submit` | | `@nx/react-native` | `build-android`, `build-ios`, `bundle`, `pod-install`, `run-android`, `run-ios`, `start`, `upgrade` | | `@nx/vitest` | `test` | ### Generation-time warnings wired in - `@nx/<pkg>:configuration` for webpack, vite, rollup, jest, playwright, storybook, rspack, vitest - `@nx/<pkg>:application` for next, expo, react-native - `@nx/eslint:lint-project` legacy fallback path - `@nx/react:application` (webpack, rspack branches) and `@nx/react:library` (rollup legacy fallback) — warning text is inlined rather than imported. Two distinct reasons: - **rspack:** `@nx/react` does not declare a tsconfig project reference to `@nx/rspack`, so the deep import would not even type-check. - **webpack and rollup:** the project reference exists, so the deep import compiles, but `@nx/webpack` and `@nx/rollup` only expose `./index.js` in their package `exports` field. The import would resolve in source but throw `Cannot find module '@nx/<pkg>/src/utils/deprecation'` at runtime in published packages. - `@nx/react-native:web-configuration` (webpack) — inline for the same reason as the rspack case (no project reference). ### Scope notes - `@nx/vite:test` is intentionally **not** included — it is being removed entirely by PR #35517 (deprecation messaging would ship as dead code). `@nx/vitest:test` is now in scope: the `convert-to-inferred` generator for `@nx/vitest` is in place, so the deprecation has a real migration target. - `@nx/cypress`/`@nx/detox` already shipped earlier; this PR retitles their generation-time messages to the new wording (drop the redundant "register the plugin first" guidance, swap "Scaffolding" for "Generating") and points the detox URL at the general convert-to-inferred guide. - `@nx/react-native:storybook` is intentionally **not** included. The companion `@nx/react-native:storybook-configuration` generator was already removed in v21 and no generator has emitted this target since Jan 2024 (RN 0.73 upgrade). It will be removed outright in a follow-up PR rather than going through the deprecate-now / remove-in-v24 cycle. - `@nx/webpack:ssr-dev-server`, `@nx/expo:build-list`, `@nx/expo:sync-deps`, `@nx/expo:update`, `@nx/expo:ensure-symlink`, `@nx/react-native:sync-deps`, and `@nx/react-native:ensure-symlink` are intentionally left as-is — none are covered by `convert-to-inferred` and they have no clean replacement (Nx-specific glue or non-migrating utilities). - `@nx/esbuild:esbuild` and `@nx/nuxt:*` ship neither an inferred plugin nor a `convert-to-inferred` generator yet, so they're out of scope. - Per-package READMEs, introduction-doc banners, per-package "migration recipe" pages, and `migrations.json` entries are intentionally skipped per the canonical pattern (NXC-4422). The runtime warning carries the migration story. ## Related Issue(s) Fixes NXC-4423. Fixes NXC-4296. Fixes NXC-4294. Fixes NXC-4290. Fixes NXC-4285. Fixes NXC-4283. Fixes NXC-4286. Fixes NXC-4297. Fixes NXC-4293. Fixes NXC-4292. Fixes NXC-4282. Fixes NXC-4287. Fixes NXC-4295. Fixes NXC-4447. --------- Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com> |
||
|
|
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 |
||
|
|
ff0eec2641 |
chore(repo): update nx to 23.0.0-beta.7 (#35565)
Updating Nx from 23.0.0-beta.4 to 23.0.0-beta.7 |
||
|
|
ed44fb3426 |
fix(core): use workspace root for package manager detection in script targets (#35550)
## Current Behavior
`readTargetsFromPackageJson` (in
`packages/nx/src/utils/package-json.ts`) receives a `workspaceRoot`
argument but never passes it to package manager detection:
```ts
for (const script of includedScripts) {
packageManagerCommand ??= getPackageManagerCommand(); // ← no workspaceRoot
res[script] = buildTargetFromScript(script, scripts, packageManagerCommand);
}
```
Two consequences:
1. **Wrong package manager** — `detectPackageManager()` defaults `dir =
''`, so the lockfile probe runs in the CWD, not the workspace. When that
finds nothing it falls back to `npm_config_user_agent`, so the inferred
`runCommand` (`npm run X` vs `pnpm run X` vs `yarn X`) on script targets
ends up depending on whoever invoked the nx process rather than on the
workspace's actual lockfile.
2. **Module-level cache** — `let packageManagerCommand` (cleared with
`??=`) memoizes the first detection result across all subsequent calls
in the process. So even if the first call had the right `workspaceRoot`,
every later call inherits that detection regardless of *its*
`workspaceRoot`. This is also why
`packages/nx/src/plugins/package-json/create-nodes.spec.ts` had four
pre-existing snapshot failures locally (`pnpm run …` instead of the
expected `npm run …`) — the first test in any process locked detection
to the host's PM.
This is a follow-up to #35116, which moved package manager detection
into the `createNodes` callback for the inferred plugins but missed this
code path.
## Expected Behavior
- Drop the module-level cache.
- Thread `workspaceRoot` into both `detectPackageManager` and
`getPackageManagerCommand`, so the lockfile probe runs in the right
directory.
```ts
if (includedScripts.length > 0) {
const packageManagerCommand = getPackageManagerCommand(
detectPackageManager(workspaceRoot),
workspaceRoot
);
for (const script of includedScripts) {
res[script] = buildTargetFromScript(script, scripts, packageManagerCommand);
}
}
```
The `packages/nx/src/plugins/package-json/create-nodes.spec.ts` fixture
now seeds `package-lock.json` into memfs in a `beforeEach`, matching the
pattern #35116 established for plugin specs. Without the lockfile the
detector still falls back to the env var; with it, every test
deterministically picks `npm`, matching the existing snapshots.
## Verification
Before this PR: 4 failures in
`packages/nx/src/plugins/package-json/create-nodes.spec.ts`:
```
✕ should build projects from package.json files
✕ should store js package metadata
✕ should add a script target if the sibling project.json file does not exist
✕ should add a script target if the sibling project.json exists but does not have a conflicting target
Tests: 4 failed, 7 passed, 11 total
```
After this PR:
```
Tests: 11 passed, 11 total
```
## Related Issue(s)
Follow-up to #35116.
|
||
|
|
803453de29 | cleanup(misc): reuse PluginCache constructor cachePath in writeToDisk (#35546) | ||
|
|
6808588a14 |
fix(misc): adopt PluginCache across createNodes plugins to prevent flaky cache parse errors (#35544)
## Current Behavior
Unit tests intermittently fail with `ValueExpected` JSON parse errors
against `.nx/workspace-data/<plugin>-<hash>.hash` files, e.g.:
```
Error: ValueExpected in /home/workflows/workspace/.nx/workspace-data/jest-7930610538513362720.hash at 1:1
> 1 |
| ^
```
Each createNodes plugin (jest, next, vite, vitest, storybook, docker,
rsbuild, rspack, webpack, rollup, react-native, expo, detox, eslint,
remix, react/router, nuxt, angular) maintains its own targets cache via
a roughly identical `readTargetsCache` / `writeTargetsToCache` pair
built on `readJsonFile` / `writeJsonFile`. The write is non-atomic
(`writeFileSync`); when two callers race on the same cache path, a
reader can observe the empty truncated file mid-write, and
`readJsonFile` throws `ValueExpected`.
A few drive-by issues uncovered along the way:
- `detox` was reading from `expo-${hash}.hash` (wrong filename
copy/paste).
- `detox`, `expo`, `react-native` writes were `{ ...oldCache,
targetsCache }` without spread — storing the cache object literally
under the key `targetsCache` instead of merging entries, so on-disk
cache was effectively useless across runs.
## Expected Behavior
All createNodes plugins now use the shared `PluginCache` utility from
`@nx/devkit/internal` (already in use by Gradle and the .NET analyzer).
`PluginCache`:
- Wraps the cache read in `try/catch`, treating empty/corrupt files as a
cache miss instead of throwing — eliminates the `ValueExpected` flake.
- Wipes the cache file on write error so corruption can't persist across
runs.
- Tracks LRU access order and evicts entries when serialization hits
`RangeError`.
Per-plugin pattern change:
```ts
// before
const targetsCache = readTargetsCache(cachePath);
targetsCache[hash] ??= await buildTargets(...);
const result = targetsCache[hash];
// later:
writeTargetsToCache(cachePath, targetsCache);
```
```ts
// after
const targetsCache = new PluginCache<TargetsType>(cachePath);
if (!targetsCache.has(hash)) {
targetsCache.set(hash, await buildTargets(...));
}
const result = targetsCache.get(hash);
// later:
targetsCache.writeToDisk(cachePath);
```
Plugins migrated: `angular`, `detox`, `docker`, `eslint`, `expo`,
`jest`, `next`, `nuxt`, `react-native`, `react` (router-plugin),
`remix`, `rollup`, `rsbuild`, `rspack`, `storybook`, `vite`, `vitest`,
`webpack`.
Skipped:
- `packages/dotnet/src/utils/cache.ts` — orphaned dead code with no
callers; `dotnet/src/analyzer/analyzer-client.ts` already uses
`PluginCache`.
- `packages/js/src/plugins/typescript/plugin.ts` — already has its own
resilient read (try/catch) and atomic temp+rename write; migrating would
regress on the write side.
### On-disk cache format change
`PluginCache` stores `{ entries, accessOrder }` instead of a flat
record. Stale caches written by older versions are simply discarded on
read — perf-only impact, no correctness implication.
## Validation
`pnpm nx run-many -t test --parallel=8 --skip-nx-cache` produces the
same set of failed tasks on this branch as on `master` (24 tasks, all
pre-existing snapshot/env issues unrelated to this change). Net new
failures introduced: 0.
## Related Issue(s)
N/A — addresses observed flake during unit tests; no specific issue
tracked.
|
||
|
|
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> |
||
|
|
c401052abe |
chore(linter): bump ignore from ^5.0.4 to ^7.0.5 (#35508)
## Current Behavior `@nx/js`, `@nx/next`, and `@nx/react-native` declare `ignore: ^5.0.4`. The companion packages `@nx/nx` and `@nx/dotnet` are already on `^7.0.5` (queue file flagged not to re-bump those). Per the bulk-dependency-update sweep (NXC-4329), the remaining three packages should align. ## Expected Behavior Bumped to `ignore@^7.0.5` in all three. ignore@6/7 are API-compatible with v5 — only changes are dropping Node ≤12 support and internal perf improvements. ## Validation The 5 source-level call sites all use the default-import default-export shape: ```ts // packages/js/src/utils/generate-globs.ts // packages/js/src/utils/assets/copy-assets-handler.ts // packages/js/src/generators/typescript-sync/typescript-sync.ts // packages/react-native/src/generators/init/lib/add-git-ignore-entry.ts // packages/next/src/utils/add-gitignore-entry.ts import ignore from 'ignore'; ``` Stable across v5/v6/v7. The `ignore()` constructor + `.add()` + `.filter()` + `.ignores()` chain is unchanged. `pnpm nx run-many -t build -p js,react-native,next` — passes. ## Related Issue(s) Part of [NXC-4329](https://linear.app/nxdev/issue/NXC-4329) bulk-dependency-update sweep. |
||
|
|
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>
|
||
|
|
d1e9a4349a |
feat(misc)!: remove deprecated stylesheet options from generators (#35103)
This PR removes Less, Tailwind, and CSS-in-JS style options from React and Vue generators. Moving forward, it is preferable to use either CSS or SCSS, and set up other options manually or with AI. The CSS-in-JS solutions have fallen out of favor. Tailwind and shadcn are more likely to be used by AI, and neither needs our generator support. For anyone who depends on these removed options, they can wrap their own generators on top of ours to customize further. ## Current Behavior All non-Angular generators (React, Next.js, Vue, Nuxt, Web, Workspace) prompt users with a long list of stylesheet options including. These make the decision much more complicated, where a basic CSS/SCSS setup opens up for more customization if desired without our official support. ## Expected Behavior **Simplified style prompt** — the interactive prompt now shows only: - CSS (default) - SCSS - None Note: Code will continue to compile for now, we're removing the option from generators. For LESS, users will receive a deprecation warning, the same way we deprecated Stylus previously. For styled-jsx, styled-components, emotion we will do a follow-up when we decide what to do with built-in configs for webpack/rspack/rollup. ## Related Issue(s) Fixes NXC-4178 |
||
|
|
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. |
||
|
|
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 |
||
|
|
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`.
|
||
|
|
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> |
||
|
|
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> |
||
|
|
d5f51d6d33 |
fix(linter): prepend framework configs before baseConfig in flat config generation (#34898)
## Current Behavior Framework generators append predefined configs (`flat/react`, `flat/angular`, etc.) after `baseConfig` in the flat config array. In flat config, later entries override earlier ones, so framework rules override user root rules. Additionally, the parser/plugins config entries in `flat/typescript`, `flat/javascript`, and `flat/angular` have no `files` restriction, applying the TypeScript parser to all files globally — including `.html`, `.json`, and other non-TS/JS files. This was partially fixed in PR #28381 (which scoped rules/extends) but the parser entries were missed. ## Expected Behavior Framework configs are inserted before `baseConfig`, giving user root config higher priority. Root standalone projects (no `baseConfig`) are unaffected. Parser/plugins entries are scoped to their respective file types, so `.html` files get the correct parser (Angular template parser, or ESLint default) instead of the TypeScript parser. ## Changes - Implement `checkBaseConfig` option in `addBlockToFlatConfigExport` to insert before `...baseConfig` when present - Framework generators (`@nx/react`, `@nx/angular`, `@nx/next`, etc.) pass `checkBaseConfig: true` - Scope parser entries in `flat/typescript` to `**/*.ts, **/*.tsx, **/*.cts, **/*.mts` - Scope parser entries in `flat/javascript` to `**/*.js, **/*.jsx, **/*.cjs, **/*.mjs` - Scope processor/plugins entry in `flat/angular` to `**/*.ts` - Refactor `addPredefinedConfigToFlatLintConfig` to use an options object for optional params - Fix regex patterns in react-native and expo jest config templates to use `[.]` instead of `\.` — avoids `no-useless-escape` lint errors and fixes a latent bug where `\.` in a string literal matched any character instead of a literal dot ## Related Issue(s) Fixes #32923 |
||
|
|
c1a93cb061 |
fix(core): set windowsHide: true on all child process spawns (#34894)
## Current Behavior
On Windows, the Nx daemon runs as a detached background process with no
console. When child processes are spawned without `windowsHide: true`
(Node.js) or `CREATE_NO_WINDOW` (Rust/Win32), Windows allocates a new
visible console window for each subprocess. This causes command prompt
windows to flash on screen during:
- Project graph creation (daemon spawn, plugin workers)
- Task hashing (runtime hashers)
- NX Console extension detection (`code.cmd --list-extensions`, etc.)
- AI agent configuration checks (`git ls-remote`, npm install)
- Machine ID retrieval, package manager version detection, git
operations, and more
The issue is especially noticeable "after a little bit" following daemon
startup, because background operations like the NX Console status check
and AI agents configuration check kick off after the initial project
graph is computed.
## Expected Behavior
No console windows should flash on Windows. All child process spawns use
`windowsHide: true` (Node.js) or `CREATE_NO_WINDOW` (Rust) to suppress
console windows.
## Root Causes Found
Investigation using a child_process interceptor in the daemon revealed
multiple sources:
1. **Rust native `ide/install.rs`** — `Command::new("code.cmd")` calls
for NX Console extension detection/installation were missing
`CREATE_NO_WINDOW`
2. **Rust native `hash_runtime.rs`** — Had its own `CREATE_NO_WINDOW`
handling but was duplicated
3. **`nx@latest` temp install** — The daemon downloads `nx@latest` to a
temp directory for NX Console and AI agent checks. The install process
(`pnpm add -D nx@latest`) and the downloaded code's `git ls-remote`
calls run without `windowsHide: true`
4. **~120 Node.js `child_process` calls** — Various
`spawn`/`exec`/`execSync` calls across the codebase were missing
`windowsHide: true`
## Changes
### Node.js child_process fixes
- Set `windowsHide: true` on all `spawn`/`exec`/`execSync`/`spawnSync`
calls across the codebase (~120 files)
- Added custom ESLint rule `@nx/workspace/require-windows-hide` that
errors when any spawn/exec call is missing `windowsHide: true`
### Rust native fixes
- **New shared util `native/utils/command.rs`** with `create_command()`
and `create_shell_command()` that set `CREATE_NO_WINDOW` on Windows —
centralizes the pattern so future Rust code gets it right by default
- **`ide/install.rs`** — Use `create_command()` for `code.cmd` calls
(list-extensions, install-extension, version check)
- **`hash_runtime.rs`** — Replaced local `create_command_builder()` with
shared `create_shell_command()`
- **`machine_id/mod.rs`** — Updated to use shared
`create_shell_command()`
### Daemon background operation fixes
- **`handle-configure-ai-agents.ts`** — Now respects `NX_USE_LOCAL` env
var to skip downloading `nx@latest`, avoiding the pnpm install that
opens windows. Once these fixes ship in a release, the downloaded
`nx@latest` will also have the fixes.
### Inlined node-machine-id
- Replaced the `node-machine-id` npm package with an inlined
implementation in `machine-id-cache.ts` that uses `windowsHide: true`
- The original package used `exec`/`execSync` without `windowsHide`
## Related Issue(s)
Supersedes #34455
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: FrozenPandaz <FrozenPandaz@users.noreply.github.com>
|
||
|
|
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 # |
||
|
|
dd7ec3016b |
feat(core): support cwd specific hashes (#33879)
## Current Behavior
There is no straight-forward way to use the cwd as part of a tasks hash
## Expected Behavior
You can use `{workingDirectory: 'absolute'}` to factor the working
directory into the hash
## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->
Fixes #33684
|
||
|
|
6f85b83fd3 |
cleanup(repo): format all files (#33902)
Format all files after Prettier v3 was merged. |
||
|
|
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 |
||
|
|
2917b3e545 |
fix(vite): update worker configuration in generator to follow Vite's … (#30465)
…new convention
## Current Behavior
Currently, the @nx/vite plugin generates a `vite.config.ts` file where
the worker configuration is commented out, but uses the old format:
```ts
// worker: {
// plugins: [ nxViteTsPaths() ],
// }
```
If uncomment, this format triggers a warning from Vite, as the worker
configuration should now be a function that returns an array of plugins.
While Vite automatically converts the old format for compatibility, it
is not ideal to rely on this behavior.
## Expected Behavior
With the changes in this PR, the @nx/vite plugin will generate a Vite
configuration where the worker configuration follows the new convention,
avoiding warnings and ensuring compatibility with future versions of
Vite. The updated configuration will look like this:
```ts
// worker: {
// plugins: () => [ nxViteTsPaths() ],
// }
```
This change ensures that the generated configuration aligns with Vite's
recommended practices and eliminates unnecessary warnings.
---------
Co-authored-by: Colum Ferry <cferry09@gmail.com>
|
||
|
|
1c8796a4d7 |
docs(misc): update migration docs to use supported markdown syntax (#33563)
This PR cleans up the markdown files under `packages/`. We previously had to support Next.js docs and translate it for astro docs with proper markdown syntax. This applies to generators, executors, and migrations. Also removes the function to do the translation in astro app since it's no longer needed. ## Code block (migrations) <img width="1086" height="800" alt="Screenshot 2025-11-20 at 1 23 53 PM" src="https://github.com/user-attachments/assets/bd9acb9b-7960-4e41-9d26-22d29da6658e" /> ## Aside (generators) <img width="802" height="443" alt="Screenshot 2025-11-20 at 1 43 17 PM" src="https://github.com/user-attachments/assets/e2999821-8783-46ed-a984-2193f6f8eafa" /> |
||
|
|
618c3344af |
fix(vite): generate .mts config files to force ESM (#33518)
Using .mts extension forces files to always be treated as ESM modules, ensuring consistent behavior regardless of package.json or tsconfig settings. This matters for Node 24 because by default Node will strip types from `.ts` files and then they are resolved through normal Node resolution. In the past we can control CJS/ESM through tsconfig options, but now only extension or `type` in `package.json` matters. Changes: - Updated all createOrEditViteConfig calls to pass useEsmExtension: true - Updated normalizeViteConfigFilePathWithTree to check for .mts files first - Updated test files to expect .mts config files - Updated snapshots to reflect new .mts extension Closes NXC-3446 |