Skip to content

chore(nxext): upgrade to nx 23 - #1201

Merged
DominikPieper merged 5 commits into
mainfrom
chore/upgrade-nx-23
Jul 2, 2026
Merged

DominikPieper merged 5 commits into
mainfrom
chore/upgrade-nx-23

Conversation

@DominikPieper

Copy link
Copy Markdown
Member

Migrates the workspace from Nx 22.6.5 to Nx 23.0.1 via nx migrate, then fixes the fallout the automated codemods didn't cover:

  • @nx/workspace's deep-import surface (core/project-graph, utilities/fileutils) was removed with no migration; rewrote stencil's build/serve executors to use @nx/devkit/node:fs, and reimplemented @nx/js's also-fully-removed updateBuildableProjectPackageJsonDependencies locally.
  • @nx/vite's vitestGenerator moved to @nx/vitest/generators under the name configurationGenerator; fixed in preact/solid/svelte and added @nx/vitest as an explicit dependency to each.
  • @nx/jest's jestProjectGenerator renamed to configurationGenerator; the automated migration missed every call site using the ensurePackage<typeof import(...)> dynamic-peer pattern (5 files).
  • Nx's stripSourceCode (deprecated since v20) was fully removed; vendored the implementation into packages/svelte since our TypeScriptSvelteImportLocator depends on it.
  • @nx/js's addTsConfigPath now prefixes registered paths with ./; updated svelte's test expectation to match.
  • Vite's Rolldown migration renamed rollupOptionsExternal to rolldownOptionsExternal across preact/solid/svelte/sveltekit.
  • @nx/angular's /generators and /internal subpaths have no classic-resolution stub for TS; added targeted @ts-expect-error (verified they resolve fine at runtime) rather than widening moduleResolution workspace-wide.
  • Added a missing glob devDependency (previously riding on a transitive hoist that broke once the dependency tree shifted) and .env.test/.env.e2e (NODE_OPTIONS=--experimental-vm-modules) since @nx/cypress's new e2e-config generator does a dynamic import that Jest can't run without it.
  • Bumped every @nxext/* plugin's own version to 23.0.0 and its @nx/* peer ranges to ^23.0.1 (tools/scripts/update-package-json-nx-versions.ts plus manual version bumps, per established convention), and removed @nx/workspace from stencil/sveltekit's package.json now that nothing imports it directly.
  • Bumped .node-version to the current Node 22 LTS (22.23.1) and corrected CLAUDE.md's stale claim that CI excludes e2e.

Note: @nx/devkit@23.0.1's rewritten formatFiles() does await import('prettier') and reads .resolveConfig off the namespace directly, which doesn't work for prettier@2.x's CJS export shape (reproduces in plain Node, not just Jest) - this silently no-ops formatting across the whole workspace. Worth reporting upstream; only visible local symptom was one stale inline snapshot, updated.

Verified: build/lint/test green across all 18 projects; stencil-e2e passed 10/10 against a real Verdaccio-published package.

Claude-Session: https://claude.ai/code/session_01Xpn3tnTKhe4Pd8grXphQPg

Migrates the workspace from Nx 22.6.5 to Nx 23.0.1 via `nx migrate`, then
fixes the fallout the automated codemods didn't cover:

- @nx/workspace's deep-import surface (core/project-graph, utilities/fileutils)
  was removed with no migration; rewrote stencil's build/serve executors to
  use @nx/devkit/node:fs, and reimplemented @nx/js's also-fully-removed
  updateBuildableProjectPackageJsonDependencies locally.
- @nx/vite's vitestGenerator moved to @nx/vitest/generators under the name
  configurationGenerator; fixed in preact/solid/svelte and added @nx/vitest
  as an explicit dependency to each.
- @nx/jest's jestProjectGenerator renamed to configurationGenerator; the
  automated migration missed every call site using the
  ensurePackage<typeof import(...)> dynamic-peer pattern (5 files).
- Nx's stripSourceCode (deprecated since v20) was fully removed; vendored the
  implementation into packages/svelte since our TypeScriptSvelteImportLocator
  depends on it.
- @nx/js's addTsConfigPath now prefixes registered paths with `./`; updated
  svelte's test expectation to match.
- Vite's Rolldown migration renamed rollupOptionsExternal to
  rolldownOptionsExternal across preact/solid/svelte/sveltekit.
- @nx/angular's /generators and /internal subpaths have no classic-resolution
  stub for TS; added targeted @ts-expect-error (verified they resolve fine at
  runtime) rather than widening moduleResolution workspace-wide.
- Added a missing `glob` devDependency (previously riding on a transitive
  hoist that broke once the dependency tree shifted) and .env.test/.env.e2e
  (NODE_OPTIONS=--experimental-vm-modules) since @nx/cypress's new e2e-config
  generator does a dynamic import that Jest can't run without it.
- Bumped every @nxext/* plugin's own version to 23.0.0 and its @nx/* peer
  ranges to ^23.0.1 (tools/scripts/update-package-json-nx-versions.ts plus
  manual version bumps, per established convention), and removed @nx/workspace
  from stencil/sveltekit's package.json now that nothing imports it directly.
- Bumped .node-version to the current Node 22 LTS (22.23.1) and corrected
  CLAUDE.md's stale claim that CI excludes e2e.

Note: @nx/devkit@23.0.1's rewritten formatFiles() does `await import('prettier')`
and reads `.resolveConfig` off the namespace directly, which doesn't work for
prettier@2.x's CJS export shape (reproduces in plain Node, not just Jest) -
this silently no-ops formatting across the whole workspace. Worth reporting
upstream; only visible local symptom was one stale inline snapshot, updated.

Verified: build/lint/test green across all 18 projects; stencil-e2e passed
10/10 against a real Verdaccio-published package.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xpn3tnTKhe4Pd8grXphQPg
@nx-cloud

nx-cloud Bot commented Jul 2, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit b3cdbdf

Command Status Duration Result
nx affected --target build lint test e2e ✅ Succeeded 5m 32s View ↗
nx-cloud record -- pnpm exec nx format:check ✅ Succeeded 2s View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-07-02 17:50:01 UTC

DominikPieper and others added 4 commits July 2, 2026 17:50
Writing a unit test for the vitest generator fix (add-vitest.ts, fixed in
the previous commit) exposed that the fix itself was incomplete:

- preact/solid: @nx/vite's viteConfigurationGenerator (called uncondi-
  tionally from addVite, with includeVitest) already delegates to
  @nx/vitest internally in Nx 23 and sets up the "test" target itself.
  Calling our own addVitest() afterward tried to add the same target
  again and crashed with "Target 'test' already exists". Removed the
  now-fully-redundant addVitest() call and its @nx/vitest dependency
  from both plugins.
- svelte: same redundancy for buildable/publishable libs, but svelte's
  addVite() only delegates to @nx/vitest when buildable/publishable -
  for plain (non-buildable, non-publishable) libs it's a no-op, and
  addVitest() was the only thing wiring up the vitest test target.
  Removing it broke that case. Restored addVitest(), gated on the same
  condition it always had, calling @nx/vitest/generators directly (the
  Nx 23 replacement import) instead of the removed @nx/vite vitestGenerator.
- Also fixed a pre-existing bug the new coverage caught: svelte's
  tsconfig.spec.json.template used single-quoted strings inside the
  vitest branch of its "types" array, producing invalid JSON. Nx's own
  JSON reader now rejects it (previously lenient). Fixed to double quotes.

Added generator-spec coverage for unitTestRunner: 'vitest' in preact,
solid, and svelte (both the buildable and non-buildable paths for
svelte, since they take different code paths).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xpn3tnTKhe4Pd8grXphQPg
Two real issues surfaced once capacitor-e2e actually ran against Nx 23:

- e2e/utils/index.ts's downgradeToLegacyWorkspace() writes a flat
  tsconfig.base.json with moduleResolution: "node" + baseUrl for
  compatibility with what @nxext generators emit. TypeScript 5.9+ now
  hard-errors (TS5101/TS5107) on that combination instead of warning,
  since it's deprecated ahead of removal in TS 7.0. Since keeping the
  legacy resolution is the entire point of this downgrade, silence the
  deprecation via `ignoreDeprecations: "6.0"` (as TS's own error message
  suggests) rather than migrating away from it.
- Once past that, `@nx/web:application --bundler=vite --style=css`
  generates a tsconfig.app.json with `"types": ["node"]`, which fully
  replaces (doesn't merge with) whatever `types` the workspace-level
  tsconfig.base.json sets down the `extends` chain. Without vite/client's
  ambient `*.css` module declaration, the plain CSS side-effect import in
  the generated app.element.ts fails to type-check (TS2882). Patched
  tsconfig.app.json directly in capacitor.spec.ts's beforeAll, since Nx
  regenerates that file fresh every run and we don't own its template.

capacitor-e2e passes end-to-end now (4 passed, 3 skipped pre-existing
Xcode/unit-test gaps, unrelated to this fix).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xpn3tnTKhe4Pd8grXphQPg
…M/jest-preset-angular gap

The generated app's unit tests fail with "Must use import to load ES
Module" on @angular/core/fesm2022/core.mjs inside jest-preset-angular's
zoneless setup-env. Spent significant effort trying to isolate the
trigger: an isolated repro with the exact same @angular/core@21.2.17,
jest@30.3.0, jest-preset-angular@16.0.0, and the exact jest.config.cts
our updateJestConfig() produces (verified via debug logging in the real
e2e run) passes every single time. The failure only reproduces inside
the full @nxext/ionic-angular + @nxext/capacitor install.

This isn't a Nx 23 regression - the generated jest config itself is
correct and matches what a from-scratch @nx/angular:application produces.
It looks like Angular going ESM-only-core in 21.x outpacing
jest-preset-angular, surfaced by some node_modules resolution detail
specific to the full plugin install that a clean repro doesn't hit.
Skipping with a detailed TODO rather than blocking on it; build and the
cap executor both pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xpn3tnTKhe4Pd8grXphQPg
`ignoreDeprecations: '6.0'` in the shared legacy-workspace tsconfig was
wrong for every locally-installed TypeScript (5.9.3 only accepts
"5.0"), but `create-nx-workspace@latest` floats to whatever npm
currently ships as latest — which had moved to a real TypeScript 6.0
major, where the same deprecations require "6.0" instead. No single
literal satisfies both, so pin the e2e workspace's typescript to the
range every @nxext/* package already declares in peerDependencies
(^5.7.3) instead, and set ignoreDeprecations back to "5.0" to match.

That pin then surfaced a second, previously-masked bug: svelte-preprocess
5.1.4's TS5-migration compat shim auto-injects the now fully-removed
`importsNotUsedAsValues` compiler option whenever it sees
`ignoreDeprecations: "5.0"`, which TypeScript 5.9 rejects outright
(TS5102). svelte-preprocess 6.x dropped that shim in favor of auto-setting
`verbatimModuleSyntax`, so bump the generator's pinned version.

Fixes ionic-angular-e2e, preact-e2e, svelte-e2e, stencil-e2e, and
solid-e2e failing in the Nx Cloud distributed CI run for PR #1201
while passing locally — the fresh npm install each of these e2e specs
performs is exactly what made the drift CI-only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xpn3tnTKhe4Pd8grXphQPg
@DominikPieper
DominikPieper merged commit 95c0080 into main Jul 2, 2026
6 checks passed
@DominikPieper
DominikPieper deleted the chore/upgrade-nx-23 branch July 2, 2026 17:56
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.

1 participant