Skip to content

Handle optional compat patch failures for TypeScript 7#7190

Open
hamidrezahanafi wants to merge 2 commits into
yarnpkg:masterfrom
hamidrezahanafi:fix-typescript-7-compat-patch
Open

Handle optional compat patch failures for TypeScript 7#7190
hamidrezahanafi wants to merge 2 commits into
yarnpkg:masterfrom
hamidrezahanafi:fix-typescript-7-compat-patch

Conversation

@hamidrezahanafi

@hamidrezahanafi hamidrezahanafi commented Jun 18, 2026

Copy link
Copy Markdown

Summary

  • Bound the generated TypeScript compatibility patch to TypeScript versions before 7.0.0, and committed the matching generated patch artifact so the compat patch generator stays reproducible.
  • Fixed optional patch handling so optional! patches fall back when a package no longer ships a target file (for example lib/_tsc.js in TypeScript 7 / TypeScript compatibility shims), instead of failing the install with ENOENT.
  • Added regression coverage for both direct typescript@7.0.1-rc installs and the TypeScript 7 side-by-side recommendation where the typescript ident aliases to npm:@typescript/typescript6@^6.0.0.
  • Stabilized the scoped plugin-typescript acceptance test by moving it from @babel/traverse (now reported by npm search metadata as having included types) to a fixture package that still exercises DefinitelyTyped scoped package insertion.
  • Added deferred version metadata for the changed workspaces.

Test plan

  • yarn workspace @yarnpkg/plugin-compat test:plugin-compat
  • yarn test:unit packages/plugin-patch
  • node ./scripts/run-yarn.js test:integration packages/acceptance-tests/pkg-tests-specs/sources/plugins/plugin-typescript.test.ts
  • yarn version check

Notes

TypeScript 7 ships a native compiler package layout and no longer has the legacy JS compiler files patched by the existing PnP compatibility diff, such as lib/_tsc.js. Bounding the TypeScript compat patch makes this intent explicit, while the optional patch fallback fixes the broader bug that optional! patch failures caused by missing target files were still fatal.

TypeScript 7 support should ship with microsoft/typescript-go#1966.

Made with Cursor

@maik-bol

Copy link
Copy Markdown

Thanks for this. Bounding the patch to < 7.0.0 fixes the plain typescript@7.x install, but I think it leaves a closely related case still broken, and it might be worth addressing the root cause while we're here.

The TypeScript 7.0 RC announcement recommends a side-by-side install that aliases typescript to the TS6 compatibility package:

{
  "devDependencies": {
    "typescript": "npm:@typescript/typescript6@^6.0.0",
    "typescript-7": "npm:typescript@rc"
  }
}

@typescript/typescript6 is a thin re-export shim (version 6.0.1) that does not ship lib/_tsc.js / lib/_tsserver.js. Because the compat plugin keys on the dependency ident (typescript) before resolution, Yarn applies the builtin patch to this shim. Its version is 6.0.1, so a < 7 gate still selects it, and the install dies with the same YN0001 ENOENT on lib/_tsc.js. So this PR fixes installing TS7 directly, but not the recommended TS6/TS7 coexistence setup.

Repro on node-modules (no PnP):

# .yarnrc.yml
nodeLinker: node-modules
{ "devDependencies": { "typescript": "npm:@typescript/typescript6@^6.0.0" } }

yarn install -> YN0001: ... ENOENT ... lstat '.../typescript/lib/_tsc.js'.

Two more robust options, either of which also covers the alias case:

  1. Gate on resolved package identity, not (only) version. Apply the patch only when the typescript-ident descriptor actually resolves to the typescript package, and skip it when it resolves to something else via an npm: alias. That fixes both the TS7 case and the compat-alias case without relying on a version cutoff.
  2. Make optional! tolerate a missing target file. Today the apply step only swallows hunk-application failures; a raw lstat ENOENT on a file the package doesn't ship is rethrown as a hard YN0001, even though the descriptor is marked optional!. Treating a missing target file like a failed hunk (warn and fall back to the original source) would make this and any future layout drift non-fatal.

(1) is the precise fix; (2) is good defense-in-depth and matches what the docs say optional! already does.

Happy to help if you want it!

@arcanis

arcanis commented Jun 18, 2026

Copy link
Copy Markdown
Member

I think the optional qualifier being ignored is a proper bug to fix, yeah - also something curious in the current PR, it seems to be breaking a test 🤔

@hamidrezahanafi

Copy link
Copy Markdown
Author

Thanks @maik-bol and @arcanis , that makes sense.

I updated the PR to address this in two layers:

  • Kept the explicit TypeScript compat patch bound to <7.0.0-0, so it’s clear that the legacy PnP patch is only intended for the old JS-based TypeScript package layout.
  • Fixed the broader optional! behavior in plugin-patch: if an optional patch targets a file that the package no longer ships, Yarn now treats that like an optional patch miss, warns, discards the patch attempt, and falls back to the original package instead of failing with ENOENT.

I also added coverage for the alias case from the TS 7 recommendation:

{
  "typescript": "npm:@typescript/typescript6@^6.0.0"
}

So this should cover both direct typescript@7.x installs and the TS6/TS7 side-by-side setup.

On the CI failure: plugin-typescript.test.ts was failing because the test used @babel/traverse as a scoped package that should get @types/babel__traverse auto-added. That depends on live npm-search / Algolia metadata. That metadata now reports @babel/traverse as having included types, so Yarn correctly skipped adding the @types package and the assertion failed. I changed the test to use a fixture package whose metadata still exercises the DefinitelyTyped scoped-package path, so the test remains focused on Yarn’s scoped @types/foo__bar behavior instead of depending on stale metadata for @babel/traverse.

@hamidrezahanafi hamidrezahanafi changed the title Limit TypeScript compat patch to pre-7 releases Handle optional compat patch failures for TypeScript 7 Jun 19, 2026
Co-authored-by: Cursor <cursoragent@cursor.com>
@hamidrezahanafi hamidrezahanafi force-pushed the fix-typescript-7-compat-patch branch from 1c09061 to ce3dafd Compare June 19, 2026 04:29
@hamidrezahanafi hamidrezahanafi requested a review from arcanis as a code owner June 19, 2026 04:29
Co-authored-by: Cursor <cursoragent@cursor.com>
@maik-bol

Copy link
Copy Markdown

Thanks @maik-bol and @arcanis , that makes sense.

I updated the PR to address this in two layers:

  • Kept the explicit TypeScript compat patch bound to <7.0.0-0, so it’s clear that the legacy PnP patch is only intended for the old JS-based TypeScript package layout.
  • Fixed the broader optional! behavior in plugin-patch: if an optional patch targets a file that the package no longer ships, Yarn now treats that like an optional patch miss, warns, discards the patch attempt, and falls back to the original package instead of failing with ENOENT.

I also added coverage for the alias case from the TS 7 recommendation:

{
  "typescript": "npm:@typescript/typescript6@^6.0.0"
}

So this should cover both direct typescript@7.x installs and the TS6/TS7 side-by-side setup.

On the CI failure: plugin-typescript.test.ts was failing because the test used @babel/traverse as a scoped package that should get @types/babel__traverse auto-added. That depends on live npm-search / Algolia metadata. That metadata now reports @babel/traverse as having included types, so Yarn correctly skipped adding the @types package and the assertion failed. I changed the test to use a fixture package whose metadata still exercises the DefinitelyTyped scoped-package path, so the test remains focused on Yarn’s scoped @types/foo__bar behavior instead of depending on stale metadata for @babel/traverse.

Amazing work, thanks for the effort!

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