feat: Extends performance domain with React Compiler error triage and coverage antipatterns - #170
Merged
Merged
Conversation
`mm-react-compiler-error-triage` — sorting compiler errors into `Todo` and unsupported versus actionable, ratcheting `panicThreshold`, and the `useMemo`/`useCallback` exception for effect dependencies, whose output the compiler's memoization does not preserve. Split out of #43: compiler adoption is a different subject from the render antipattern scans, and nothing in either scan cites it.
`babel-plugin-react-compiler` 1.0.0 logs `CompileSuccess` for every function in a file whose top-level directive then discards the transform. Only a directive inside one function's body logs `CompileSkip`. The compiled count therefore rises with opt-outs and is read against the opt-out count.
…s it metamask-mobile#31171 (enable the React Compiler across the Metro bundle) replaced the two-path allowlist. The plugin now applies to every file except under Jest, with no `target` override, so its default of React 19 applies. The triage reference also named two internal planning tickets.
…ng it Removing the directive and re-running the compiler settles it: zero new errors means the directive was masking nothing.
MajorLift
marked this pull request as ready for review
September 18, 2026 14:25
MajorLift
marked this pull request as draft
September 18, 2026 14:52
…d drop a stale-closure example
…ing a value the effect uses
It was stated only as an effects rule, and the cost it names is a compiler cost: an entry the effect never reads makes the compiler skip the whole function, so the hook ships unmemoized. Someone asking what loses them compiler coverage opens the compiler overlay, where the rule was absent. Zero hits for it across all four react-compiler surfaces before this.
MajorLift
marked this pull request as ready for review
September 18, 2026 14:53
performance domain with React Compiler error triageperformance domain with React Compiler error triage and coverage antipatterns
rvelaz
approved these changes
Sep 18, 2026
MajorLift
enabled auto-merge (squash)
September 18, 2026 16:37
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
The React Compiler fails open. A component it cannot compile ships unoptimized with a green build and no warning, so the coverage you believe you have and the coverage you have are different numbers and nothing reports the gap.
That makes two questions urgent, in that order. Which components is it skipping, and which patterns are quietly removing components from the set it compiles.
Finding out what the compiler is skipping
Raising
panicThresholdto'all_errors'is how you see the skipped set, and it returns an error list that mixes unsupported-syntax noise with real bugs, so the list is only useful once it is sorted.references/mm-react-compiler-error-triage.mdsorts compiler errors into unsupported, which is the compiler's ownTodocategory, and actionable. It ratchetspanicThresholdin non-production builds rather than setting it once, so the actionable set shrinks under a threshold that tightens instead of being read once and abandoned.references/mm-react-compiler.mdcounts a file carrying a module-scope'use no memo'as compiled rather than skipped, which is the measurement error that makes a coverage figure read better than the build.target: '19', as set up in metamask-mobile#31171 (fully enable React Compiler), replacing the path allowlist and thetarget: '18'rule. That is the denominator the skipped set is measured against.Coverage Anti-Patterns
Each of these removes a whole function from the compiled set, and each looks locally harmless at the call site.
validateExhaustiveEffectDependencies) reports it as an error and skips the entire function. A comment beside the array does not help, because the check reads the array rather than the prose.perf-hooks-effectsgains the rule with the four ways to make the array describe the code: pass the value into the work the effect starts, put akeyon a component where the value marks a scope that must reset as a whole, handle it where the action occurs, or treat it as a finding. It names what does not work, which is deleting the dependency outright, explaining it in a comment, reaching for'use no memo', and adding a parameter the callee ignores.'use no memo'left in place after the reason for it is gone.mm-react-compiler.mdnow tests whether the directive is doing any work before keeping it, since React documents it as a temporary debugging tool and it opts the whole function out.useMemothe compiler appears to make redundant. Its memoization is not guaranteed to preserve the identity auseEffectdependency needs, somm-react-compiler.mdkeeps anyuseMemooruseCallbackwhose output feeds an effect's dependency array.perf-react-compilerstates the unread-dependency rule as a compiler-coverage anti-pattern, because someone asking what costs them coverage opens that file rather than the effects one.Two effects rules from the same review
These do not affect compiler coverage and are here because they came out of the same review rather than because they belong to the argument above.
perf-hooks-effectscorrects its dependency-minimization guidance to depend on the primitive the effect reads rather than on an object the parent rebuilds each render. The previous worked pair movedrefreshIntervalto a default parameter and dropped it from the array, which does not change whether it is a dependency.perf-hooks-effectsmoves a multi-step async acquire with a timeout and several end paths into a plain object owned outside React, so those end paths can be unit-tested without rendering.Where this lands
The triage material goes to
skills/performance/, which reaches mobile alone because that skill'srepos/holds onlymetamask-mobile.mdandtools/installskips a skill entirely for any repo with no overlay file. The coverage rules go toskills/perf-*/repos/metamask-extension.md, four skills whoserepos/holds only that file.Of the 85 lines added on the first path, 6 are mobile-specific. Of the 140 on the second, 0 are extension-specific. So most of this is platform-independent React guidance landing where the existing rules already sit. Widening either path's reach needs
repos/files in skills this pull request does not touch and is tracked separately.The anti-patterns came out of review on metamask-extension#46240.