Skip to content

Process files sequentially in flow-upgrade runCodemods - #9485

Draft
rootkiller6788 wants to merge 1 commit into
facebook:mainfrom
rootkiller6788:fix-run-codemods-sequential
Draft

Process files sequentially in flow-upgrade runCodemods#9485
rootkiller6788 wants to merge 1 commit into
facebook:mainfrom
rootkiller6788:fix-run-codemods-sequential

Conversation

@rootkiller6788

Copy link
Copy Markdown

Summary

Fixes #9407. When running a codemod over multiple files (e.g. flow-codemod convertLegacyUtilityTypes), every file was rewritten with the first file's transformed output.

Root cause

runCodemods used Promise.allSettled(filePaths.map(...)) so all files were transformed concurrently. hermes-transform is not safe to use this way:

  • Prettier memoizes loaded parser plugins with cacheKey: JSON.stringify of the plugin options.
  • hermes-transform's printer passes a plugin whose only per-call difference is the parse closure returning the current file's AST. JSON.stringify drops functions, so the cache key is identical for every file.
  • The second file's prettier.format therefore reuses the first file's parser, which returns the first file's AST, and every file gets printed with the first file's content.

Clearing hermes-transform/prettier from require.cache (the existing workaround) only helps when files are processed one at a time; under concurrent execution the reset races between files.

Fix

Process files sequentially. The per-file failure tolerance of Promise.allSettled is preserved with a try/catch around each file.

Added two regression tests:

  • runCodemods-test.js — verifies each file is transformed independently (the reported bug's exact scenario).
  • runCodemods-sequential-test.js — mocks hermes-transform and asserts that transform invocations never overlap.

Test plan

  • npx jest src/__tests__/runCodemods-test.js src/__tests__/runCodemods-sequential-test.js — both pass.
  • npx jest src/ — all 14 suites / 91 tests pass.
  • Reproduced the bug with the reporter's A/B/C fixture before the change (all files got A.js's output) and confirmed the fix yields each file's own correct output.

Fixes flow-upgrade codemods writing another file's output when run over
multiple files (issue facebook#9407).

hermes-transform is not safe to run concurrently: prettier memoizes the
loaded parser plugin keyed by a JSON serialization of the plugin options,
and hermes-transform's printer wires the current file's AST up through
that parser. Because the only per-file difference is the parse closure
(which JSON.stringify drops), the cache key collides across files and a
file can end up being printed with a different file's AST. The require
cache clearing already present only works when files are processed one at
a time; under Promise.allSettled the cache reset races between files.

Process files sequentially instead, preserving the previous behavior of
not aborting the whole run when a single file fails.
@meta-cla meta-cla Bot added the CLA Signed label Aug 21, 2026
@meta-codesync

meta-codesync Bot commented Aug 21, 2026

Copy link
Copy Markdown

This pull request has been imported. If you are a Meta employee, you can view this in D116917614. (Because this pull request was imported automatically, there will not be any future comments.)

},
}));

describe('runCodemods', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

delete this test. it's only asserting that things are running sequentially which is an impl detail. it just adds blockers once the cache issue is properly fixed

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.

convertLegacyUtilityTypes codemod replaces types with others from a random file

2 participants