Skip to content

feat(misc): multi-version support compliance for detox, expo, react-native, and remix#35885

Merged
FrozenPandaz merged 25 commits into
masterfrom
feature/nxc-4385-4389-4400-4402-multi-version-rn-ecosystem
Jun 9, 2026
Merged

feat(misc): multi-version support compliance for detox, expo, react-native, and remix#35885
FrozenPandaz merged 25 commits into
masterfrom
feature/nxc-4385-4389-4400-4402-multi-version-rn-ecosystem

Conversation

@FrozenPandaz

Copy link
Copy Markdown
Contributor

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.

@netlify

netlify Bot commented Jun 4, 2026

Copy link
Copy Markdown

Deploy Preview for nx-docs ready!

Name Link
🔨 Latest commit 1ff37bd
🔍 Latest deploy log https://app.netlify.com/projects/nx-docs/deploys/6a2847c120ee2600087e2093
😎 Deploy Preview https://deploy-preview-35885--nx-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Jun 4, 2026

Copy link
Copy Markdown

Deploy Preview for nx-dev ready!

Name Link
🔨 Latest commit 1ff37bd
🔍 Latest deploy log https://app.netlify.com/projects/nx-dev/deploys/6a2847c190c9b10008c474bf
😎 Deploy Preview https://deploy-preview-35885--nx-dev.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@nx-cloud

nx-cloud Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit 1ff37bd

Command Status Duration Result
nx affected --targets=lint,test,build,e2e,e2e-c... ✅ Succeeded 49m 50s View ↗
nx run-many -t check-imports check-lock-files c... ✅ Succeeded 3s View ↗
nx-cloud record -- pnpm nx-cloud conformance:check ✅ Succeeded 56s View ↗
nx build workspace-plugin ✅ Succeeded <1s View ↗
nx-cloud record -- nx sync:check ✅ Succeeded 18s View ↗
nx-cloud record -- nx format:check ✅ Succeeded 7s View ↗
nx affected -t e2e-macos-local --parallel=1 --b... ✅ Succeeded 31m View ↗

☁️ Nx Cloud last updated this comment at 2026-06-09 17:59:28 UTC

@socket-security

socket-security Bot commented Jun 4, 2026

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: npm @expo/cli is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: pnpm-lock.yamlnpm/@expo/cli@56.1.13

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@expo/cli@56.1.13. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@FrozenPandaz FrozenPandaz force-pushed the feature/nxc-4385-4389-4400-4402-multi-version-rn-ecosystem branch from 1a7caf4 to 43c37bb Compare June 4, 2026 20:56
nx-cloud[bot]

This comment was marked as outdated.

@FrozenPandaz FrozenPandaz force-pushed the feature/nxc-4385-4389-4400-4402-multi-version-rn-ecosystem branch 3 times, most recently from fb78eb1 to d116009 Compare June 5, 2026 19:40
@FrozenPandaz FrozenPandaz marked this pull request as ready for review June 5, 2026 20:01
@FrozenPandaz FrozenPandaz requested a review from a team as a code owner June 5, 2026 20:01
@FrozenPandaz FrozenPandaz requested a review from AgentEnder June 5, 2026 20:01
nx-cloud[bot]

This comment was marked as outdated.

@FrozenPandaz FrozenPandaz force-pushed the feature/nxc-4385-4389-4400-4402-multi-version-rn-ecosystem branch from 130c1cf to d2f0b7d Compare June 8, 2026 13:45
@FrozenPandaz FrozenPandaz enabled auto-merge (squash) June 8, 2026 15:08
@FrozenPandaz FrozenPandaz force-pushed the feature/nxc-4385-4389-4400-4402-multi-version-rn-ecosystem branch from 7978edb to 9e45fc5 Compare June 9, 2026 15:50

@nx-cloud nx-cloud Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Important

At least one additional CI pipeline execution has run since the conclusion below was written and it may no longer be applicable.

Nx Cloud has identified a possible root cause for your failed CI:

We investigated the e2e-gradle:e2e-ci--src/gradle-plugin-v1.test.ts failure and determined it is unrelated to the changes in this PR. The failure is caused by a Gradle daemon lock contention error ("Timeout waiting to lock build logic queue") triggered by stale CI infrastructure state — no Gradle plugin code was modified here. We recommend re-running the task to allow the Gradle daemon state to clear.

No code changes were suggested for this issue.

Trigger a rerun:

Rerun CI

Nx Cloud View detailed reasoning on Nx Cloud ↗


🎓 Learn more about Self-Healing CI on nx.dev

assertGeneratorsEnforceVersionFloor now resolves a generator's factory from
either `factory` or `implementation` in generators.json, so plugins that use
`implementation` (e.g. @nx/remix) can adopt the shared floor spec.
- Add assertSupportedDetoxVersion (floor 20.0.0; v19 is unmaintained upstream)
  to the init, application, and convert-to-inferred generators.
- Preserve user-pinned versions: keepExistingVersions now defaults to true.
- Split migration 22.0.0 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.
- Add the parameterized all-generators-enforce-floor spec.

NXC-4385
- Add the SDK 55 install lane (RN 0.83.6, React 19.2) and make it the default;
  add isExpoV55 detection. The v53 and v54 lanes are retained.
- Declare `expo` as a peer dependency; add assertSupportedExpoVersion
  (floor SDK 53) to every generator; keepExistingVersions defaults to true.
- Route SDK 55 jest setup through the v54 winter-runtime path.
- Gate SDK-specific migrations with `requires` and tighten 22.2.0 to a
  bilateral expo range.
- Add the floor spec; update the supported-versions docs.

NXC-4389
- Replace the single 0.79.3 literal with a per-minor version map
  (0.83/0.84/0.85, default 0.85) routed through versions(tree).
- Declare `react-native` as a peer dependency; add
  assertSupportedReactNativeVersion (floor 0.83.0) to every generator;
  keepExistingVersions defaults to true.
- Gate the remove-deprecated-deps migration on `react-native >=0.76 <0.79`.
- Add the floor spec; update the supported-versions docs.

NXC-4400
- @nx/remix stays Remix v2 (React Router v7 lives in @nx/react); document the
  split. Add assertSupportedRemixVersion (floor 2.0.0) to every generator.
- Stop generators from overwriting installed react/vite/@remix-run versions via
  keepExistingVersions, fixing conflicting pins with @nx/react.
- Add the parameterized all-generators-enforce-floor spec.

NXC-4402
The react-native compliance change added `semver` (catalog:) to the plugin's
dependencies; record its resolved entry in the lockfile so frozen installs stay
in sync.
… them in the lockfile

Marks the `expo` and `react-native` peerDependencies optional (their inferred
plugins are gated on app-config presence) and records the resolved peers in the
lockfile so `pnpm install --frozen-lockfile` (CI) stays in sync.
The plugins detect support by major version (expo) / minor version (react-native),
so they work with any patch within each supported version, not just the .0.x line.
Caret reflects that; for the 0.x react-native versions caret and tilde are equivalent.
Moves each per-plugin assertSupported*Version wrapper into the plugin's
versions.ts (next to the minSupported*Version floor it already uses) and removes
the standalone assert-supported-*-version.ts files. No behavior change; the
all-generators-enforce-floor specs still exercise every generator's assert.
Defers RN 0.85 to a follow-up: 0.85 relocated its Jest preset to the new
@react-native/jest-preset package, which needs a separate generator change to
@nx/react-native's jest config. 0.83 and 0.84 still ship the old self-contained
jest, so they work with the current generator. Default install is now 0.84
(Active line); a 0.85 workspace falls through to latest constants.
…s pass

The expo and react-native peers are optional (peerDependenciesMeta), so they
need no lockfile entries. Earlier full re-resolves churned ~2000 lines and
engaged @vitejs/plugin-react's optional babel-plugin-react-compiler peer, which
made @nx/react fail @nx/dependency-checks (green on master). The lockfile is now
master plus the one real addition — react-native's semver dependency.
Gated requires expo >=54.0.0 <55.0.0 to continue the existing SDK ladder. Versions mirror the expoV55* install constants.
…0.84

Gated requires react-native >=0.83.0 <0.84.0 (within the supported window). Versions mirror the 0.84 install constants.
…edExpoMajorVersion

Collapse the three duplicated isExpoV5x detection blocks into one getInstalledExpoMajor helper (graph-first detection unchanged), route via a per-major lookup table so the project graph is traversed once, and remove the unused getInstalledExpoMajorVersion. Also reword two stale "For Expo v54" jest comments. Behavior unchanged.
Remove the as-CompatMinors cast and widen versionMap to Partial<Record<number, ReactNativeVersions>> so indexing by minor() is type-safe without the cast. Behavior unchanged.
… detox peer optional

Pass keepExistingVersions to addDependenciesToPackageJson in the application and add-linting generators so re-runs no longer overwrite a user's pinned versions. Mark the detox peer as optional, matching the gated executor/inferred-plugin surfaces (consistent with the expo/react-native peers).
…unused version helpers

Pass keepExistingVersions to addDependenciesToPackageJson in the ensure-dependencies and add-linting helpers so the application/library generators stop overwriting a user's pinned Expo ecosystem versions. Remove the unused isExpoV54/isExpoV55 helpers (no src callers; the only question the code asks is isExpoV53) and their specs.
…or and simplify versions()

Pass keepExistingVersions to addDependenciesToPackageJson in add-linting so it no longer overwrites a user's pins. Drop the dead tree-less branch from versions()/getInstalledReactNativeVersion (every caller passes a tree) and inline the one-line helper.
The generator floor asserts detox >=20.0.0, but the peer advertised ^20.9.0, leaving a [20.0.0, 20.9.0) gap that passes the assert but fails the peer. Widen to ^20.0.0 so the two agree.
…rsion floor

The generator floor asserts @remix-run/dev >=2.0.0, but the peer advertised ^2.17.3. Widen to ^2.0.0 so the advertised peer agrees with the asserted floor.
@remix-run/dev is consumed only through opt-in surfaces — the build/serve executors (require.resolve at runtime) and the createNodesV2 inferred plugin gated on a remix.config file. The eager imports are type-only. Declare it optional via peerDependenciesMeta, matching the detox/expo/react-native primaries in this PR.
…indow

The peer was open-ended (>=0.83.0), advertising 0.85+ which the plugin explicitly defers (0.85 relocated its Jest preset to @react-native/jest-preset). Cap to >=0.83.0 <0.85.0 so the advertised window matches the install lanes. This also pulls the repo's own react-native dev resolution from 0.85.3 down to the supported 0.84.1. versions() still silently falls through to the latest lane above the window (no throw).
@FrozenPandaz FrozenPandaz force-pushed the feature/nxc-4385-4389-4400-4402-multi-version-rn-ecosystem branch from 9e45fc5 to 1ff37bd Compare June 9, 2026 17:05
@FrozenPandaz FrozenPandaz merged commit b9a0582 into master Jun 9, 2026
27 checks passed
@FrozenPandaz FrozenPandaz deleted the feature/nxc-4385-4389-4400-4402-multi-version-rn-ecosystem branch June 9, 2026 18:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants