[No QA] Rebuild lint seatbelt pipeline outside ESLint - #99259
[No QA] Rebuild lint seatbelt pipeline outside ESLint#99259roryabraham wants to merge 15 commits into
Conversation
Move react-compiler, no-deprecated, and seatbelt post-process out of ESLint processors into scripts/lint so one JSON reduce replaces ~8400 per-file TSV locks. Drop eslint-seatbelt and its three patches. Replay dumps via --from-raw for benchmarks.
|
@MelvinBot review |
Code reviewNicely structured rewrite — the pipeline stages are cleanly separated, the seatbelt ratchet logic is faithfully reproduced, and I confirmed the committed 🔴 Blocker — a JSON parse failure exits 0 and passes CI silentlyWhen ESLint's output can't be parsed and ESLint exited 0 or 1, the whole lint run reports zero errors, prints nothing, and exits 0.
So the author's own "force a failure" safety net is dead code, and since CI runs Fix: make parse failure fatal — return 🟡 Worth addressing
🔵 Minor / nits
Things I verified as correct
This is lint-tooling only (no runtime/network behavior), so no App testing was run. |
JSON parse failures now exit >1 so CI cannot go green on truncated ESLint output. Arg parsing uses expensify-common/CLI. Compiler workers share a WorkerPool that falls back per file instead of aborting the run.
Move wall-clock stage timing out of the lint package so other scripts can reuse it.
expensify-common/CLI lives in the runner; seatbelt env parsing belongs next to the ratchet.
Benchmark replay is not worth the extra CLI and pipeline paths.
Pipeline now composes abstract Linter, Processor, and Formatter ports. ESLint, seatbelt, compiler filter, and stylish output live in named folders so a new linter or stage is a new class.
|
@MelvinBot review |
Re-review (head
|
|
@codex review |
|
@MelvinBot review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c3cd1aaeee
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Re-review (updated at
|
A silent worker exit could hang lint, and the new crash fixture was missing from the root tsconfig exclude list.
|
@codex review |
Re-review (head
|
There was a problem hiding this comment.
💡 Codex Review
App/scripts/checkOnyxConnectBypass.ts
Line 28 in 69c36f9
When a caller writes the equivalent Onyx . connect(...) or splits the member access across lines, ESLint still reports the banned call and an adjacent disable can suppress it, but this fixed-string git grep does not select the file. The downstream ONYX_CONNECT_CALL_REGEX already accepts whitespace, yet it is never invoked, allowing npm run lint to pass a forbidden direct Onyx read; candidate selection should not require the contiguous text Onyx.connect. CLAUDE.mdL36-L38
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
git grep is line-oriented, so requiring contiguous Onyx.connect skipped split member access.
|
Addressed the latest Codex P1: candidate scan no longer requires contiguous |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8636ec2a22
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Comments and grouping parens are valid member-access syntax and slipped past the source regex.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b416214e3e
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Block comments can list the banned rule after the first line; the old regex stopped at the newline.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: adcbc92eb4
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Scan TypeScript comment trivia so string contents cannot re-enable a disabled lint rule.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cc71b6b6e3
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const sourceFile = ts.createSourceFile(file, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX); | ||
| const offsets: number[] = []; | ||
| const visit = (node: ts.Node) => { | ||
| if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && !node.expression.questionDotToken && node.expression.name.text === 'connect') { |
There was a problem hiding this comment.
Detect optional Onyx.connect calls
A blanket directive can still bypass the ban when the call uses optional member access, for example /* eslint-disable */ Onyx?.connect(...): TypeScript represents this as a property-access call with questionDotToken, but this condition explicitly excludes it, so blanketDirectiveCoversCall sees no covered call while ESLint's disabled rule remains suppressed. Fresh evidence beyond the addressed AST fix is this explicit optional-chain exclusion; treat Onyx?.connect() like the other valid member-access spellings.
Useful? React with 👍 / 👎.
# Conflicts: # config/eslint/eslint.config.mjs # package-lock.json # package.json # scripts/tsconfig.json # tests/tooling/tsconfig.json # tsconfig.json
Explanation of Change
Rebuild the lint post-process pipeline (React Compiler filter, no-deprecated stratification, seatbelt ratchet) as linter-agnostic transforms in
scripts/lint/, still driven byeslint --format json. This is Phase 3 / §4.1 of the Oxlint migration: dropeslint-seatbeltand its three patches, do one TSV read/write per run instead of ~8,400 locked per-file updates, and only run the React Compilers on files that actually have a suppressible message.Measured on a full-repo dump (8,489 files, 3,479 messages): post-process is ~9s cold / ~230ms warm. Oxlint is out of scope.
Full-Repo Lint Benchmark
Method: each repository ran npm run lint sequentially. Before each cold run, node_modules/.cache/eslint was removed; the warm result is the immediately following run using the populated cache. Timings are wall-clock seconds from /usr/bin/time -p; each invocation had a 30-minute timeout. The PR worktree failed both runs consistently at src/libs/Navigation/helpers/swapBackgroundTabForRHPTarget.ts:41:74 with @typescript-eslint/no-unnecessary-type-assertion.
Editor integrations and bare
npx eslintstill run the rule set but no longer apply seatbelt / compiler filter / stratify — those live only innpm run lint. Documented inLINTING.md.Fixed Issues
$
PROPOSAL:
Tests
TZ=utc bun test --preload ./scripts/stubReactNative.js --preload ./tests/tooling/setup.ts ./tests/tooling/lintSeatbelt.test.ts ./tests/tooling/lintPipeline.test.tsand confirm all tests pass.TZ=utc NODE_OPTIONS="--experimental-vm-modules --max_old_space_size=8192" bunx jest tests/unit/OnyxConnectBypassTest.ts --no-coverageand confirm all tests pass.npm run lint -- scripts/lint scripts/checkOnyxConnectBypass.ts scripts/onyxConnectBypass.tsand confirm it exits 0.npm run lint -- --timings scripts/lintand confirm per-stage timings are printed.SEATBELT_READ_ONLYunset (local default), runnpm run lint -- scripts/lint/seatbelt.tsand confirmconfig/eslint/eslint.seatbelt.tsvis unchanged.package.jsonno longer listseslint-seatbeltandpatches/eslint-seatbelt/is gone.Offline tests
This change is lint tooling only and has no runtime / network behavior.
QA Steps
Same as tests. Lint tooling only; no product QA.
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari