Skip to content

feat: support TypeScript 7 for spec preprocessing#34277

Merged
mschile merged 7 commits into
developfrom
mschile/focused-germain-deffe6
Jul 15, 2026
Merged

feat: support TypeScript 7 for spec preprocessing#34277
mschile merged 7 commits into
developfrom
mschile/focused-germain-deffe6

Conversation

@mschile

@mschile mschile commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Additional details

TypeScript 7 (the native/Go compiler, GA'd 2026-07-08) ships an npm package with no JavaScript compiler APIts.sys, createProgram, transpileModule, etc. no longer exist. @cypress/webpack-batteries-included-preprocessor resolves the project's typescript and hands it to ts-loader as the compiler, so on a typescript@7 project every spec crashes before a test runs:

TypeError: Cannot read properties of undefined (reading 'fileExists')

What this changes:

  • @cypress/webpack-batteries-included-preprocessor — When the resolved TypeScript version is >= 7.0.0, .ts/.tsx spec and support files are transpiled with babel-loader + @babel/preset-typescript instead of ts-loader. This matches the transpile-only behavior we already had (ts-loader ran with transpileOnly: true, so no type-checking happened at bundle time before or after). TypeScript <= 6 is completely unchanged and still uses ts-loader with the project's compiler.
  • emitDecoratorMetadata parityts.transpileModule (what transpileOnly calls) emits decorator metadata, so the babel path adds @babel/plugin-proposal-decorators (legacy) + babel-plugin-transform-typescript-metadata to preserve it. tsconfig paths aliases continue to work (resolved by tsconfig-paths-webpack-plugin, which reads the tsconfig JSON and needs no compiler API).
  • Setup wizardWIZARD_DEPENDENCY_TYPESCRIPT.minVersion widened to ^5.0.0 || ^6.0.0 || ^7.0.0 so component-testing onboarding no longer blocks typescript@7 projects.

Known behavioral differences under TypeScript 7 (all scoped to TS 7+; none affect TS ≤ 6):

  • tsconfig compilerOptions no longer drive spec output — babel uses a fixed config (preset-env targeting Chrome 64, CommonJS) plus classic preset-react. The observable case is a .tsx e2e spec using the automatic JSX runtime ("jsx": "react-jsx"): babel emits React.createElement, so React must be in scope. This already matches how .jsx e2e specs behave in this preprocessor; TSX in e2e specs is rare (component specs go through the framework dev server, not this path).
  • Decorators are transpiled with legacy semantics (matching experimentalDecorators + emitDecoratorMetadata); standard TC39 decorators are not supported on this path. Because babel emits an unguarded Reflect.metadata(...) call, decorator-metadata code requires reflect-metadata to be loaded at runtime (projects using metadata already do this).
  • @babel/preset-typescript must support new TypeScript syntax as it ships. TS 7.0 adds no new syntax, so there's no gap today.

Implementation note: the new babel deps use caret ranges, which pulls a monorepo-wide @babel/* 7.28 → 7.29 bump in yarn.lock (within existing semver ranges).


Note

Medium Risk
Swapping the transpiler for TS 7+ can change JSX/decorator output vs tsconfig-driven ts-loader; risk is mitigated by version gating and broad tests but affects all TS7 e2e preprocessing.

Overview
Adds TypeScript 7 support for Cypress e2e spec/support preprocessing and widens the component-testing wizard to accept typescript@7.

Because TypeScript 7’s npm package no longer exposes the compiler API that ts-loader needs, @cypress/webpack-batteries-included-preprocessor now detects resolved TypeScript >= 7 and transpiles .ts/.tsx with babel-loader + @babel/preset-typescript instead of ts-loader, while keeping the prior transpile-only behavior for TS ≤ 6. Babel options are centralized in getBabelLoaderOptions, with legacy decorators and babel-plugin-transform-typescript-metadata on the TS path to preserve emitDecoratorMetadata parity; tsconfig-paths-webpack-plugin behavior is unchanged.

WIZARD_DEPENDENCY_TYPESCRIPT.minVersion now includes ^7.0.0. Coverage adds unit/e2e tests for the TS 7 branch, a ts-proj-7 system fixture (including decorator metadata), and bumps react-vite-ts-configured to TypeScript 7 for component testing.

Reviewed by Cursor Bugbot for commit 570e364. Bugbot is set up for automated code reviews on this repo. Configure here.

Steps to test

The AtofStryker/typescript-7-cypress minimal repro (an untouched cypress open scaffold + a typescript@7 tsconfig) reproduces the original crash on develop. To validate:

  1. In a project with typescript@7 installed, run an e2e spec written in TypeScript: npx cypress run --spec cypress/e2e/spec.cy.ts. It should compile and run instead of failing with the fileExists crash.
  2. Automated coverage in this PR:
    • Unit + e2e: yarn workspace @cypress/webpack-batteries-included-preprocessor test (exercises the TS 7 branch against a real typescript@7 install via a typescript-v7 alias, including decorator metadata).
    • Wizard range: yarn workspace @packages/scaffold-config test -- dependencies.spec.ts
    • System test (new ts-proj-7 fixture): cd system-tests && node ./scripts/run.js --glob-in-dir="typescript_spec_support" -- --browser electron

How has the user experience changed?

Projects that upgrade to TypeScript 7 can now compile and run their TypeScript specs. Previously, every TypeScript spec failed to compile (0 tests executed) with a Cannot read properties of undefined (reading 'fileExists') error. No change for projects on TypeScript 6 or earlier.
Before:
ts7-before

After:
ts7-after

Screenshot 2026-07-10 at 3 48 28 PM

PR Tasks

TypeScript 7 (the native compiler) ships without the JavaScript compiler
API, so ts-loader crashes when transpiling TypeScript spec and support
files. Detect TypeScript 7+ in the webpack batteries-included preprocessor
and transpile those files with Babel (@babel/preset-typescript, plus legacy
decorators + babel-plugin-transform-typescript-metadata to preserve
emitDecoratorMetadata) instead of ts-loader. TypeScript <= 6 is unchanged.

Also widen the component testing setup wizard's accepted TypeScript range
to include 7.x.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mschile mschile self-assigned this Jul 10, 2026
mschile and others added 3 commits July 10, 2026 14:08
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mschile mschile marked this pull request as ready for review July 10, 2026 21:50
@cypress

cypress Bot commented Jul 10, 2026

Copy link
Copy Markdown

cypress    Run #72391

Run Properties:  status check passed Passed #72391  •  git commit 570e364d19: chore: address review wording on TypeScript 7 changes
Project cypress
Branch Review mschile/focused-germain-deffe6
Run status status check passed Passed #72391
Run duration 13m 31s
Commit git commit 570e364d19: chore: address review wording on TypeScript 7 changes
Committer Matthew Schile
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 52
View all changes introduced in this branch ↗︎

Warning

No Report: Something went wrong and we could not generate a report for the Application Quality products.

@mschile mschile requested a review from AtofStryker July 11, 2026 22:03

@AtofStryker AtofStryker left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The preprocessor change looks good to me — babel transpile-only is the right call given TS 7 shipped without a compiler API, and the test coverage on that path is solid (including the decorator-metadata ordering assertions).

Approving with one ask, which is the only thing I'm concerned about here: a vite + typescript@7 component testing system test added to this PR. Vite CT is the one CT path that works with TS 7 today — @cypress/vite-dev-server never touches the TS compiler API, and config-file loading uses our bundled tsx/esbuild — but we have zero coverage proving it, and the wizard change in this PR is now telling users TS 7 is good for CT. A fixture modeled on react-vite-ts-configured (bumped to typescript@7.x, with a .tsx spec) plus a block in component_testing_spec.ts alongside the existing one would lock it in.

For context, where the other CT paths stand with typescript@7 — none of these need anything in this PR:

  • Webpack (react/vue/svelte): nothing for us to do — we source the user's own webpack config and ship no TS loaders ourselves. If their config uses ts-loader it breaks with typescript@7, but it breaks identically outside Cypress, and whatever fix they adopt flows through to us since we consume their config.
  • Angular: broken upstream — @ngtools/webpack needs ts.createProgram/ts.sys, and Angular 22 pins typescript >=6.0 <6.1 (angular/angular#69704), so no Angular project builds with TS 7 anywhere today and there's nothing we could test against.
  • Next: next build aborts with typescript@7 installed (vercel/next.js#95490); fixed only behind experimental.useTypeScriptCli in the 16.3 preview. Same story.

On the wizard widening: I'm good keeping it. We have no way to know when Angular/Next will ship TS 7 support, and their breakage is upstream of us — we can deal with it if/when it surfaces. Allowing TS 7 for the paths that work beats holding the whole range back on frameworks we can't control.

One small test suggestion on the preprocessor itself: the decorator tests cover a class decorator + constructor parameter property, but not a decorated class field (@Input() foo: string / @Column() name: string style), which is the pattern most decorator users actually hit and the quirkiest combo with plugin-transform-class-properties. Worth adding while the fixtures are fresh.

mschile and others added 3 commits July 14, 2026 17:11
Address review feedback on #34277:
- Add a decorated class field case (property decorator + class-properties) to
  the preprocessor decorator-metadata fixture, the quirkiest combo for babel.
- Add a react + vite + typescript@7 component testing system test to lock in
  the one CT bundler path that works with TypeScript 7.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Consolidate the vite + typescript@7 component testing coverage into the
existing react-vite-ts-configured project (bumped to typescript@7) rather
than a near-duplicate fixture.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Split the changelog entry so the setup wizard note is its own sentence.
- Drop the ts-loader-specific rationale from a shared comment (may change).
- Simplify the TypeScript 7 e2e describe title.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mschile mschile merged commit 4904db4 into develop Jul 15, 2026
96 of 98 checks passed
@mschile mschile deleted the mschile/focused-germain-deffe6 branch July 15, 2026 18:40
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.

Support TypeScript 7 (native compiler) for spec preprocessing

2 participants