Skip to content

fix(testing): install typescript in npm e2e workspaces to keep tsquery off TypeScript 7 - #36478

Merged
FrozenPandaz merged 3 commits into
masterfrom
fix/e2e-npm-typescript-peer
Aug 11, 2026
Merged

fix(testing): install typescript in npm e2e workspaces to keep tsquery off TypeScript 7#36478
FrozenPandaz merged 3 commits into
masterfrom
fix/e2e-npm-typescript-peer

Conversation

@FrozenPandaz

Copy link
Copy Markdown
Contributor

Current Behavior

@phenomnomnominal/tsquery@6.2.0 declares an unbounded peer:

"peerDependencies": { "typescript": ">3.0.0" }

npm auto-installs missing peers and resolves that range to latest, which has been TypeScript 7 since 7.0.2 was published on 2026-07-08. TypeScript 7 dropped the top-level CommonJS SyntaxKind export — its CJS entry now exports only { version, versionMajorMinor } — and tsquery reads ts.SyntaxKind at module load time:

 NX   Cannot convert undefined or null to object

TypeError: Cannot convert undefined or null to object
    at Object.keys (<anonymous>)
    at .../@phenomnomnominal/tsquery/dist/src/syntax-kind.js:8:27
    at .../@phenomnomnominal/tsquery/dist/src/traverse.js:5:23

npm hoists that copy into the single root node_modules/typescript slot and demotes the compatible version into a nested node_modules, so tsquery resolves TypeScript 7 and any generator that loads it fails.

This is what turns e2e-vite and e2e-web red on the npm legs of the nightly matrix — e.g. nx generate @nx/react:lib --unitTestRunner=vitest, which reaches tsquery through @nx/vitest's configuration generator and @nx/vite's vite-config-edit-utils.

The timeline matches exactly: the 2026-07-08 nightly (06:00 UTC) was green on Linux/npm; TypeScript 7.0.2 hit latest at 15:55 UTC that day; every nightly since has been red on npm.

Expected Behavior

After installing the requested @nx/* packages, the e2e harness installs typescript directly. A direct dependency wins the hoisted root slot, so tsquery resolves the pinned version and loads normally.

Installing it after the plugin install means it repairs whatever tree npm produced rather than relying on a particular ordering. Verified against a deliberately broken tree:

BEFORE: node_modules/typescript -> 7.0.2
        npm add -D typescript@~6.0.3
AFTER : node_modules/typescript -> 6.0.3 (only copy)
        tsquery resolves 6.0.3 -> loads OK

Why npm only

Same scenario, per package manager:

package manager behavior
npm installs a second typescript at latest (7.0.2) and hoists it to the single root slot, demoting the compatible 6.0.3
pnpm reuses the version already in the graph; the peer is encoded in the path (@phenomnomnominal+tsquery@6.2.0_typescript@6.0.3) — TypeScript 7 is never downloaded
yarn does not auto-install peers at all (warns only)

That matches the nightly matrix, where the npm legs are red while yarn stays green. The change is gated to npm accordingly.

Notes

Related Issue(s)

N/A — surfaced from the nightly golden-test matrix rather than a filed issue.


View session information ↗

@netlify

netlify Bot commented Jul 27, 2026

Copy link
Copy Markdown

Deploy Preview for nx-dev ready!

Name Link
🔨 Latest commit a225da4
🔍 Latest deploy log https://app.netlify.com/projects/nx-dev/deploys/6a7b51281b16200007b184ec
😎 Deploy Preview https://deploy-preview-36478--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.

@netlify

netlify Bot commented Jul 27, 2026

Copy link
Copy Markdown

Deploy Preview for nx-docs ready!

Name Link
🔨 Latest commit a225da4
🔍 Latest deploy log https://app.netlify.com/projects/nx-docs/deploys/6a7b5128f80d7600074d8a85
😎 Deploy Preview https://deploy-preview-36478--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.

@nx-cloud

nx-cloud Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit a225da4

Command Status Duration Result
nx affected --targets=lint,test,build,e2e,e2e-c... ✅ Succeeded 44m 24s View ↗
nx run-many -t check-imports check-lock-files c... ✅ Succeeded 6s View ↗
nx-cloud record -- pnpm nx-cloud conformance:check ✅ Succeeded 54s View ↗
nx build workspace-plugin ✅ Succeeded <1s View ↗
nx-cloud record -- nx sync:check ✅ Succeeded 17s View ↗
nx-cloud record -- nx format:check ✅ Succeeded 5s View ↗

☁️ Nx Cloud last updated this comment at 2026-08-11 17:32:02 UTC

…y off TypeScript 7

`@phenomnomnominal/tsquery@6.2.0` declares an unbounded
`typescript: ">3.0.0"` peer. npm auto-installs missing peers and resolves
that to `latest`, which has been TypeScript 7 since 7.0.2 was published on
2026-07-08. TypeScript 7 dropped the top-level CommonJS `SyntaxKind`
export that tsquery reads at module load, so it throws:

     NX   Cannot convert undefined or null to object
    at Object.keys (<anonymous>)
    at .../@phenomnomnominal/tsquery/dist/src/syntax-kind.js:8:27

npm hoists that copy into the single root `node_modules/typescript` slot,
demoting the compatible version, so tsquery resolves TypeScript 7 and any
generator touching it fails. This is what turns `e2e-vite` and `e2e-web`
red on the npm legs of the nightly matrix.

Installing `typescript` directly after the plugin install reclaims the
hoisted slot, which repairs the tree regardless of how it got there.

Only npm is affected. pnpm resolves peers per dependent and reuses the
version already in the graph; yarn does not auto-install peers at all.
Both were verified green in this scenario, so the fix is gated to npm.
The npm typescript install added in the previous commit forced `~6.0.3` on
every npm e2e workspace. `@nx/remix` pins TypeScript 5.x and hard-errors on
6.x, so `e2e-remix`'s `--integrated (npm)` suite failed at app generation:

     NX   Remix does not support TypeScript 6 (detected `typescript@~6.0.3`).

Add a `typescriptVersion` option to `newProject()`, defaulting to the
version `@nx/js` installs, and have the Remix npm suite pass `~5.9.2`.
Pinning 5.x still claims the hoisted root slot, so tsquery keeps resolving
a TypeScript that exposes `SyntaxKind`.

Only the npm suite needs this; the yarn suite never receives the install.
@FrozenPandaz
FrozenPandaz force-pushed the fix/e2e-npm-typescript-peer branch from 95b997b to bbbbfc4 Compare July 27, 2026 20:51
@FrozenPandaz
FrozenPandaz marked this pull request as ready for review August 11, 2026 16:43
@FrozenPandaz
FrozenPandaz requested a review from a team as a code owner August 11, 2026 16:43
@FrozenPandaz
FrozenPandaz merged commit 4bff954 into master Aug 11, 2026
25 checks passed
@FrozenPandaz
FrozenPandaz deleted the fix/e2e-npm-typescript-peer branch August 11, 2026 17:38
FrozenPandaz added a commit that referenced this pull request Aug 11, 2026
## Current Behavior

`e2e-expo` fails on its MacOS/npm combo. Jest's `NODE_ENV=test` leaves
@expo/cli's fusebox debugger shell on, and
`@react-native/dev-middleware` 0.85+ throws `DefaultToolLauncher must be
mocked or overridden in tests` while eagerly preparing it.

Behind that, `@nx/expo:serve` only resolves on a `Bundling
complete`/`Bundled` stdout line. Expo builds the web bundle on first
request, and `@nx/cypress` will not request anything until the dev
server target reports ready, so the two deadlock until the 5 minute
`runCLI` timeout.

## Expected Behavior

Expo e2e sets `EXPO_UNSTABLE_HEADLESS=1` through a shared `setupExpoEnv`
helper.

`@nx/expo:serve` resolves once the packager accepts connections.

## Nightly suite fixed

Failing run used for reproduction:
https://github.com/nrwl/nx/actions/runs/31358783952 (commit `3298fd8`).

| Suite | Combo | Failure | Local result |
| --- | --- | --- | --- |
| e2e-expo | MacOS/npm | `DefaultToolLauncher must be mocked or
overridden in tests`, `expo-legacy.test.ts` "should run e2e for cypress"
| 22 passed, 0 `DefaultToolLauncher`, 0 `ETIMEDOUT` |

Latent since the Expo SDK 56 lane (#35904) landed on 2026-06-24:
`@expo/cli` 56.1.x pins dev-middleware 0.85.3, and 0.84.0 has neither
the assert nor the eager `prepareDebuggerShell()` call.

The dev-server deadlock is only reachable once the `DefaultToolLauncher`
error is gone, so it never appeared in a nightly report - it is the next
blocker behind it.

The npm/TypeScript 7 failures in the same nightly (e2e-vite, e2e-web,
e2e-rspack) are handled by #36478, not here.

## Related Issue(s)

NXC-4612

<!-- polygraph-session-start -->
---
[View session information
↗](https://snapshot.app.trypolygraph.com/orgs/69cdc268b6aa527e4129c2b4/sessions/nightly-golden-test-fixes-19ad82e5)
<!-- polygraph-session-end -->

---------

Co-authored-by: Jason Jean <jason@nrwl.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants