Skip to content

Conversation

@clemyan
Copy link
Member

@clemyan clemyan commented Dec 10, 2025

What's the problem this PR addresses?

I was planning to update the built-in TypeScript patch when I encountered some inconveniences working with the built-in patches:

  • The patch generations scripts are not cross-platform
    • The typescript script uses POSIX paths, and git diff generates patches with quoted Windows paths which the script can't work with
    • The other scripts are bash scripts
  • There is no clear way to use a local clone of yarnpkg/TypeScript for generating patches
  • Building the TypeScript patch requires installing Volta
  • Other than the TypeScript patch, the built-in patches are not readily available (requires running a script to dump)
  • The patches for each package are generated with different git diff options, which may cause inconsistencies and the generated patch may be affected by the contributor's global git config.
  • The output of the scripts is not clear on what is happening

How did you fix it?

Reimplemented all 3 patch generation scripts in TypeScript, using @yarnpkg/core and @yarnpkg/fslib utilities to make them cross-platform.

The common parts of the generation procedure are extracted into a shared module, ensuring the patches are generated with the same options and ignoring the contributor's global git config as much as possible. Also, git diff is executed in a way that does not incorporate Windows paths into the patch.

This new patch generator includes documentation on usage and maintenance via JSDoc comments and the README.

In case someone wants to review how the patches are changed during this rewrite, the first 3 commits in this PR provides a step-by-step view:

  1. The existing patches are dumped into the filesystem to provide the "before" state in the git history
  2. The scripts are reimplemented in TypeScript, where minor issue with the patches are fixed
  • The fsevents patches have an extraenous /copy/ in the source path
  • The patched resolve sources are updated in Upgrades Eslint to v9 #6694 but the patch was not
  • There is a patch for typescript that applies to no versions of TypeScript
  • The differing git diff options are preserved, to minimize the change to the patches in this commit
  1. The git diff options are unified

Also made the CI process check for consistency of the patches when anything inside packages/plugin-compat/extra changes, not just when the bundled patches themselves change.

For the TypeScript patch, I added a fallback to using Corepack if Volta is not installed. A warning is printed in this case. While Corepack only switches npm versions, not node versions, I tested it on node 22.21.1 and all patches are built without changes. And the CI checks the patch using Volta so there is little risk of publishing a bad patch.

On the output, I tried to find a async task runner library to manage that. tasuku seemed promising at first but output that is not managed via it will be moved to above the task list, even after all tasks are complete. In the end, I created a very rudimentary logger for the scripts.

Checklist

  • I have set the packages that need to be released for my changes to be effective.
  • I will check that all automated PR checks pass before the PR gets reviewed.

@clemyan clemyan requested a review from arcanis as a code owner December 10, 2025 18:14
@clemyan clemyan force-pushed the clemyan/patch-generator branch from 7377690 to 7c4e326 Compare December 11, 2025 05:07
synchronizeProgram();
diff --git a/lib/tsserver.js b/lib/tsserver.js
index 20cb517f8..eeac46d44 100644
index 20cb..eeac 100644
Copy link
Member

Choose a reason for hiding this comment

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

Is it expected that those files changed?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, it's part of unifying git diff options. The 3 packages were using different hash lengths for the index header. fsevents was using 40, typescript was using 9. resolve was using the default which I think is 8 but the docs does not say and I don't know if something can affect that or what.

So I wanted to explicitly pass the --abbrev option. But git apply doesn't check the hashes, and I don't think we do either. So if we are explicit anyway, why not choose the minimum (which is 4)?

Comment on lines 174 to 175
for (const slice of slices) {
this.getValidateVersions(slice)
Copy link
Member

Choose a reason for hiding this comment

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

nit for readability: I'd prefer a Promise.all(slices.map(...)), and to separate it in two passes (first call getValidateVersions on all slices, aggregate that in an array, and then make another Promise.all on all the versions you got this way)

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure

Theoretically, it does have a perf impact as the fastest getValidateVesrions will be blocked by the slowest. But practically, the only truly async getValidateVesrions is the TypeScipt one and they are all waiting on the TypeScipt version fetch anyway.

@clemyan
Copy link
Member Author

clemyan commented Dec 12, 2025

I was thinking adding a fallback to building TypeScript with Corepack is safe because the CI process would build the patch with Volta and check. Turns out the job doesn't have Volta and has been checking only the aggregation of patches to bundle.

I want the CI process to check by fully regenerating the patch bundles. But building the TypeScript patch takes quite a long time that other checks shouldn't have to wait for it.

To that end, I think we should move the check into the plugin-compat workflow as a new job where we install Volta and run the check. I'll implement that later.

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