chore: version packages - #827
Merged
Merged
Conversation
github-actions
Bot
force-pushed
the
changeset-release/main
branch
27 times, most recently
from
June 19, 2026 03:21
ceb9887 to
d727155
Compare
github-actions
Bot
force-pushed
the
changeset-release/main
branch
2 times, most recently
from
June 19, 2026 03:30
16bf0cb to
70c9624
Compare
commit: |
github-actions
Bot
force-pushed
the
changeset-release/main
branch
2 times, most recently
from
June 19, 2026 04:16
e136294 to
3edb630
Compare
github-actions
Bot
force-pushed
the
changeset-release/main
branch
from
June 19, 2026 04:19
3edb630 to
cfbdcdb
Compare
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.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
eslint-plugin-react-doctor@0.5.7
Patch Changes
424d8f9,81bbfcc,937a7ca,b8170f8,3f7d0e7,6b8e756,03301fc,44db3e0,5b742fa,8908f98,451beeb]:oxlint-plugin-react-doctor@0.5.7
Patch Changes
#847
424d8f9Thanks @rayhanadev! - Fixagent-tool-capability-risk(and its siblingmcp-tool-capability-risk) false positives when a capability keyword appears only in prose (#838).The rules already blanked comments before their keyword scan but still matched the dangerous-capability pattern inside string literals. A tool whose
descriptionhappened to contain a capability word as prose — e.g.description: "...ALWAYS fetch the underlying numbers first"— fired even though no shell/fs/network primitive was wired to the handler. The keyword scan now blanks string-literal interiors (preserving offsets, so reported lines/columns stay correct), via a new opt-inignoreStringLiteralsflag on the sharedscanByPatternhelper.Genuine signals still fire: a real call site outside the quotes (
exec(command),fetch(url)), a capability inside a template interpolation (`${fetch(url)}`—${…}is treated as code, not blanked), and a dangerous module specifier (import { execFile } from "node:child_process",require("axios")) are all preserved.#845
81bbfccThanks @rayhanadev! - Fixartifact-baas-authority-surfacefalse positives onnext-sanity/@sanity/clientstudio bundles (#840).The rule's "BaaS client config present" gate paired the generic
createClienttoken with Firebase'sprojectIdfield. But that pairing is the Sanity client signature —createClient({ projectId, dataset, apiVersion })— not a Firebase or Supabase one, so every Sanity Studio browser chunk tripped the gate and then matched the second factor on a shippedroles/administratorstring.createClientnow only counts as a BaaS signal next to a Supabase marker (supabase/SUPABASE_URL); Firebase is still detected by its own verbs (initializeApp,firebase,firestore), so genuine Firebase/Supabase authority maps keep firing.#861
937a7caThanks @rayhanadev! - Stopno-inline-exhaustive-stylefrom flagging Satori (next/og, @vercel/og) OG-image components.OG components style everything inline because Satori rasterizes the JSX to a static image and supports no other styling channel — so the rule's "rebuilds every render" premise never applies, and an exhaustive
style={{…}}is the only way to lay them out. The rule now shares the sameisGeneratedImageRenderContextguard the sibling image rules already use (alt-text,nextjs-no-img-element,no-unknown-property): it short-circuits in Next.js metadata image routes (opengraph-image.tsx,twitter-image.tsx,icon.tsx, …) and skips JSX that flows into anImageResponse(...)/satori(...)call, including a helper component resolved to that call. The expensive per-node generated-image lookup runs only once a style is large enough to report, so ordinary files pay nothing. Exhaustive inline styles in regular components are still flagged.#862
b8170f8Thanks @rayhanadev! - Stopjsx-keyfrom flagging element collections handed to a non-childrenprop (e.g.<Tabs items={[<Tab />, <Tab />]} />).The rule decided whether an element needed a
keypurely from its structural position — "is this JSX inside an array literal or a.map/.flatMap/Array.fromcallback?" — and never looked at where the resulting collection was consumed. React's dev-mode key validation only iteratesprops.children(jsxWithValidation→validateChildKeys(props.children, type)), so an element array passed to any other prop is never key-validated at the call site; the receiving component owns keying (thecloneElement/Children.map/Children.toArrayidiom). Flagging the producer site was a false positive — the same "data handoff, not a sibling render" reasoning the rule already applies to object-Propertyvalues.The fix exempts collections that are the value of a non-
childrenJSX attribute, for both array literals and iterator callbacks — including when the value is wrapped in optional chaining,&&/||/??, a ternary branch, or a TSas/satisfies/!assertion (items={ready && xs.map(...)}), since none of those change whether React validates it.Genuine missing keys still fire: array literals and
.mapresults in children position (<Menu>{data.map(...)}</Menu>,<ul>{[<li/>, <li/>]}</ul>), and the explicitchildren={[...]}attribute — which isprops.childrenand which React does validate.#865
3f7d0e7Thanks @rayhanadev! - Shipno-dangerdefault-off so it no longer blanket-flags safedangerouslySetInnerHTML.no-dangeris the absolutist oxc port — it flags everydangerouslySetInnerHTMLwith zero content awareness, so it fired Security warnings on the canonical-safe idioms that React Doctor's own content-aware detectors deliberately exempt: escaped JSON-LD, theme-init<script>templates, CSS-variable<style>injection, and sanitized /safe-named values. Two default-on Security rules judged the same prop and disagreed.The content-aware rules are now the canonical default-on detectors for
dangerouslySetInnerHTML:dangerous-html-sink(dynamic/tainted markup, with the style-tag / static-template / sanitizer exemptions) andunsafe-json-in-html(the unescaped-JSON.stringifybreakout case).no-dangerremains available opt-in ("react-doctor/no-danger": "warn") for teams that want the stricter "never usedangerouslySetInnerHTMLat all" policy (oxc /eslint-plugin-reactparity).Score impact: repos using these safe idioms will see fewer Security findings and a correspondingly higher score. A CI gate pinned to a fixed threshold may pass where it previously failed. Re-enable
no-dangerin config to restore the old behavior.#846
6b8e756Thanks @rayhanadev! - Fixserver-sequential-independent-awaitfalse positive on awaits whose dependency flows through nested destructuring (#839).The rule's binding collector only saw top-level
Identifierbindings and shallow object/array pattern elements, so names bound through a nested pattern — e.g.const [{ slug }, { isEnabled }] = await Promise.all([...])— were invisible. A follow-upawait client.fetch(BlogPostQuery, { slug }, isEnabled ? ... : ...)that genuinely depended on those names was wrongly flagged as an independent waterfall. The collector now reuses the recursivecollectPatternNamesutility, so nested array/object patterns, defaulted bindings, and rest elements all count as a real dependency.#831
03301fcThanks @aidenybai! - Fixserver-auth-actionsfalse positives on custom auth guards (#829).The rule only recognized a fixed list of auth function names, so a server action protected by a project's own guard — e.g.
await requireAdmin()orawait getAdminSession()— was wrongly flagged as callable by anyone. It now recognizes auth checks by naming convention as well: an assertive verb plus an auth noun (requireAdmin,ensureSignedIn,checkPermission,assertUser,isAdmin,hasRole), a getter plus a strong auth noun (getServerAuthSession,getAdminSession), andcurrent/my/ownqualifiers (getCurrentUser). Genuinely ambiguous names likegetUserandgetTokenstill require an auth-related receiver, soanalytics.getUser()keeps firing the rule.#859
44db3e0Thanks @rayhanadev! - Fixserver-fetch-without-revalidatefalse positive on mutating fetches. Next.js only caches GET requests, so afetch(url, { method: "POST" | "PUT" | "PATCH" | "DELETE" })in a Server Component or route handler can never serve stale cached data — the rule no longer flags it.#843
5b742faThanks @rayhanadev! - Fixurl-prefilled-privileged-actionfalse positive when a validating helperwraps a read behind a receiver chain. The validator-suppression lookbehind only
recognized
validator(searchParams.get(...))orvalidator(new URLSearchParams(...))directly — real code reads through a receiver (
sanitizeNext(url.searchParams.get(...)),validateNext(request.nextUrl.searchParams.get(...))), and that interveningurl.broke the match so validated reads kept firing. The lookbehind now allows an optional
receiver member-chain between the helper's
(and the read.#826
8908f98Thanks @aidenybai! - Add 7 new rules mined from React, web-platform, security, and accessibility best practices:no-call-component-as-function(Bugs): calling a component likeFoo(props)instead of<Foo />runs it outside React and breaks hooks, state, and memoization. Shadow-safe via scope resolution.no-create-ref-in-function-component(Bugs):createRef()in a function component or hook allocates a fresh ref every render; useuseRef().no-async-effect-callback(Bugs): anasyncuseEffect/useLayoutEffectcallback returns a Promise that React treats as cleanup, causing unmount races.no-json-parse-stringify-clone(Performance):JSON.parse(JSON.stringify(x))is a slow, lossy deep clone; usestructuredClone(x).no-img-lazy-with-high-fetchpriority(Performance):loading="lazy"andfetchPriority="high"are contradictory directives on the same image.dialog-has-accessible-name(Accessibility): a<dialog>/role="dialog"with noaria-label/aria-labelledbyis announced only as "dialog".auth-token-in-web-storage(Security): persisting auth tokens inlocalStorage/sessionStorageexposes them to XSS exfiltration.#828
451beebThanks @aidenybai! - Add 3 new rules (mining batch 2), each validated with an OSS noise sweep (0 false positives across ~2,800 diagnostics in react-use, radix-ui/primitives, excalidraw, mantine):no-document-write(Performance):document.write()/document.writeln()blocks parsing and is ignored or wipes the page after load.no-sync-xhr(Performance): a synchronousXMLHttpRequest(.open(method, url, false)) freezes the main thread until the request finishes.no-string-false-on-boolean-attribute(Bugs):disabled="false"and friends pass the string"false", which is truthy, so the boolean attribute is applied even when you wrote "false". Targets a curated set of true HTML boolean attributes on intrinsic elements; excludes enumerated attrs (aria-*,contentEditable,draggable,spellCheck) and custom components.react-doctor@0.5.7
Patch Changes
#881
50999f4Thanks @rayhanadev! - Add a--debugflag that prints the run's Sentry trace id at the end of a scan.When something looks wrong, run
react-doctor --debug: it forces a Sentry performance trace for that run (even ifSENTRY_TRACES_SAMPLE_RATEwas turned down) and printsSentry trace (mention this when reporting): <id>as the last line so the id can be pasted into a bug report for maintainers to pull the full trace. It prints on both outcomes — a clean run and a crash (the crash's trace is surfaced even when it happens before the scan span starts). The line goes to stderr, so--json/--scorestdout stays machine-clean. Combining--debugwith--no-score/--no-telemetryis rejected up front, since those flags disable the Sentry reporting--debugdepends on. Telemetry also gains a low-cardinalitydebugrun tag so adoption of the flag is visible.#864
b317164Thanks @rayhanadev! - Makefile:linediagnostic locations clickable in the terminal, and record which terminal each run uses.Diagnostic locations are now wrapped in OSC 8 hyperlinks pointing at each file's absolute path, so supporting terminals (iTerm2, WezTerm, Kitty, Windows Terminal, VS Code, and other VTE-based emulators) turn them into click-to-open links — even in monorepo scans where the displayed path is relative to a sub-project root rather than the terminal's cwd. The visible text is unchanged (
src/App.tsx:12), the link rides in escape sequences, and terminals without OSC 8 support print it exactly as before. Hyperlinks are auto-detected per terminal and can be forced on/off with the standardFORCE_HYPERLINKenv var; they are off for non-TTYs, CI, and coding agents (whose output parsers shouldn't see the escapes).Telemetry also gains a
terminalKindrun tag (neovim, vscode, iterm, wezterm, kitty, windows-terminal, …) so we can see where React Doctor is actually run. It is a low-cardinality enum with no path, username, or secret.#863
740211cThanks @rayhanadev! - Add a per-projectscannedFileCountto the JSON report'sprojects[]entries —the number of source files the scan's linter examined (the changed React-eligible
files in diff mode, the whole source tree in a full scan). It's additive and
optional, so the
schemaVersionis unchanged and existing consumers are unaffected.This lets the GitHub Action tell "a PR that changed no React-eligible files" (the
linter examined nothing —
scannedFileCount: 0for every project) apart from "aclean scan of real React changes" (
scannedFileCount >= 1), which previouslyproduced identical reports. The Action now treats the former as a no-op: it skips
the sticky PR comment entirely and the commit status reads "Skipped — no React
files changed" instead of a zero-filled score line. A clean scan of real React
changes still posts its "no issues 🎉" comment.
#844
eafac9dThanks @rayhanadev! - Stop recommending the deprecated--diffflag in agent-facing guidance (#834).The CLI "Agent guidance" section, the installed agent hooks, and the
--helpexamples all advised runningreact-doctor --verbose --diff, which now prints a deprecation warning on every run. They now recommend the supported--scope changed(pass--base <ref>to pin the base). The websitellms.txtand thereact-doctorskill reference were updated to match.#832
f45cb29Thanks @devin-ai-integration! - Fix a false-positivedeslop/unused-filefor a file imported only by a file inignore.files. Ignored files are now kept in the dead-code dependency graph (only their reporting is suppressed), so a module reachable solely through an ignored file is no longer flagged as unused.#851
1e260c5Thanks @rayhanadev! - Show the "Add React Doctor to CI" and "install React Doctor" pitches once per repo instead of on every scan.The post-scan handoff re-asked the CI question on every run, and the agent install hint re-printed every run because its opt-out store was built but never written. Both now record a per-repo answer (reusing the existing once-per-repo
Confpattern) and stay quiet afterward — the first-run experience is unchanged, only the repetition stops.The agent copy-prompt no longer carries the CI marketing preamble at all. The interactive handoff prompt is now the single once-per-repo pitch, so the agent is never instructed to re-ask what the user was just asked — capable agents were flagging that preamble as social-engineering and it was eroding trust in the actual diagnostics.
#848
431e515Thanks @rayhanadev! - Stop a brokeneslint-plugin-react-hooksinstall from sinking the whole lint pass, and fix the misleading error it produced (issue #833).When the optional
react-hooks-js(React Compiler) plugin can't be imported in the user's environment, oxlint fails the entire config load — which previously dropped every curated react-doctor diagnostic too and left the scan withskippedChecks: ["lint"]and zero results. The oxlint error is also multi-line, and the 200-char error preview truncated its plugin path mid-string (often right at…/node_modules/), so it read as react-doctor passing an invalid directory rather than a plugin that failed to load.react-hooks-jsplugin-load failure and retries once with that plugin (and its compiler rules) dropped — mirroring the existing adopted-extendsfallback. The curated react-doctor rules, dead-code, and environment checks all still run; only the React Compiler rules are skipped, surfaced as a clearlint:partialnote that includes oxlint's real underlying reason.Error:line survive instead of being cut at…/node_modules/.#857
17389baThanks @rayhanadev! - Show a syntax-highlighted source snippet inreact-doctor why <file>:<line>.The
buildCodeFrameutil already powers the source frames in the scan summary, but thewhycommand (the single-location explain path) never called it — so explaining a diagnostic printed the rule, category, help, and suppression hint with no view of the offending code. It now renders the same code frame directly under the headline, with the caret on the offending column (or the whole line span for a multi-site diagnostic). When the file can't be read or the line is minified, it falls back to the existing text-only output.#882
a9d2713Thanks @rayhanadev! - Group findings that a single fix resolves into one root-cause task.Several findings can share one fix — e.g. four
useEffects that reset state on the same prop change all clear with a singlekeyprop. Those findings now carry a sharedfixGroupIdin the JSON report and the on-diskdiagnostics.jsondump, so a tool that turns findings into work items counts one fix as one task instead of N. The terminal labels such a group "One fix clears all N findings", and the agent handoff frames it as a single task ("one fix · N sites") and tells the agent to group byfixGroupId.Grouping is presentation-only and keyed on identical (file, rule, message) for an allowlist of rules where the same message means the same fix — the state-on-prop-change family today (
no-derived-state-effect,no-adjust-state-on-prop-change,no-reset-all-state-on-prop-change, and theno-derived-state/no-derived-useStaterules). The score is unchanged — it already de-weights repeated same-rule findings and never reads the new field.fixGroupIdis an additive optional field, so existing JSON consumers are unaffected.#859
44db3e0Thanks @rayhanadev! - Improve disable-directive handling for react-doctor rules:// react-doctor-disable-line/-next-line(andignore.rules/ rule lookups) now accept a rule's bare short id, e.g.no-evalforreact-doctor/no-eval— the unqualified form people reach for first.eslint-disable/oxlint-disabledirective names a react-doctor rule by an id oxlint can't bind to a plugin rule — a bare short id (no-eval) or a legacy plugin prefix (react/jsx-key), whether inline or as a file-level block disable — the diagnostic now carries a hint to use the fullreact-doctor/<id>key.#884
869f220Thanks @rayhanadev! - Warn before mass-fixing a migration-scale bucket. When a single rule spans dozens of files (≥MIGRATION_SCALE_RULE_FILE_COUNT, default 40), the report now prints a "Migration-scale change: sample before you sweep" advisory. It names the rule(s), explains the review risk, and points atnpx react-doctor@latest <path>to scope the work down one area at a time.The same guidance reaches coding agents. A new "Agent guidance" line and an inline note on any migration-scale bucket in the agent handoff prompt tell the agent to fix a representative sample, confirm the recipe holds, and get the code owner's sign-off before changing the rest, instead of mass-fixing a broad pattern in one unreviewed pass.
A new wide-event attribute (
migration.largestRuleBucketFiles, plusmigration.largestRuleBucketSitesandmigration.largestRuleBucketRule) records the widest-blast-radius rule per scan, so the threshold can be calibrated against real runs. No change to the score, exit code, or JSON report.Updated dependencies [
424d8f9,81bbfcc,937a7ca,b8170f8,3f7d0e7,6b8e756,03301fc,44db3e0,5b742fa,8908f98,451beeb]:@react-doctor/api@0.5.7
Patch Changes
431e515]:@react-doctor/core@0.5.7
Patch Changes
#848
431e515Thanks @rayhanadev! - Stop a brokeneslint-plugin-react-hooksinstall from sinking the whole lint pass, and fix the misleading error it produced (issue #833).When the optional
react-hooks-js(React Compiler) plugin can't be imported in the user's environment, oxlint fails the entire config load — which previously dropped every curated react-doctor diagnostic too and left the scan withskippedChecks: ["lint"]and zero results. The oxlint error is also multi-line, and the 200-char error preview truncated its plugin path mid-string (often right at…/node_modules/), so it read as react-doctor passing an invalid directory rather than a plugin that failed to load.react-hooks-jsplugin-load failure and retries once with that plugin (and its compiler rules) dropped — mirroring the existing adopted-extendsfallback. The curated react-doctor rules, dead-code, and environment checks all still run; only the React Compiler rules are skipped, surfaced as a clearlint:partialnote that includes oxlint's real underlying reason.Error:line survive instead of being cut at…/node_modules/.Updated dependencies [
424d8f9,81bbfcc,937a7ca,b8170f8,3f7d0e7,6b8e756,03301fc,44db3e0,5b742fa,8908f98,451beeb]:@react-doctor/language-server@0.5.7
Patch Changes
431e515]:Note
Low Risk
Release-metadata-only diff; behavioral risk is inherited from already-reviewed feature PRs, with notable user-facing deltas being new default-off
no-danger(higher scores) and stricter CI semantics viascannedFileCount.Overview
Changesets release PR that bumps the monorepo from 0.5.6 → 0.5.7 and removes the consumed
.changeset/*.mdentries. The diff is almost entirelypackage.jsonversion fields andCHANGELOG.mdprose—no application logic in this merge.react-doctor@0.5.7(CLI):--debug(Sentry trace id on stderr), OSC 8 clickablefile:linelocations +terminalKindtelemetry, per-projectscannedFileCountfor GitHub Action no-React-files skip,fixGroupIdgrouping for one-fix-many-findings, migration-scale advisory,whycode frames, once-per-repo CI/agent pitches,--scope changedin agent docs (drops deprecated--diffguidance), bare-rule disable hints, and oxlint react-hooks-js load fallback via@react-doctor/core.oxlint-plugin-react-doctor@0.5.7: ten new rules (two batches),no-dangerdefault-off (scores may rise; CI thresholds), plus FP fixes (jsx-key, Satori OG styles, Sanity BaaS gate, agent/MCP tool prose in strings, server auth guards, nested await deps, mutating fetch revalidate, URL validator chains, etc.).eslint-plugin-react-doctorand internal@react-doctor/*packages only pick up dependency bumps and changelog mirrors.Reviewed by Cursor Bugbot for commit cfbdcdb. Bugbot is set up for automated code reviews on this repo. Configure here.