Repository navigation
Move the pure components and the hooks out of index.jsx into files of their own - #567
Conversation
… their own Fifth step of the redesign's groundwork (#542, #552). index.jsx held seven components that take props and nothing else, and five hooks, between its imports and the two components that are most of the file. They move, verbatim, to src/renderer/components/ and src/renderer/hooks/, so the redesign can change one at a time and so what is left in index.jsx is the part that still has to be taken apart. Nothing is rewritten. Each function gains `export` and its file gains the imports it needs; three comments that said "above" or "below" now name the file they mean. `build.files` excluded the bundle's one source, index.jsx. It excludes every `.jsx` under src/renderer/ now: in that directory `.jsx` means "bundled by esbuild", and the new files would otherwise ship beside the bundle that already contains them. The hooks are `.jsx` for that reason, although they hold no JSX. The packaged smoke test reads the same rule. The review standard and TESTING.md said a decision made in index.jsx is one the suite cannot reach. That is as true of a component or a hook beside it, and they say so. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…heck plainly Notes from the review of the previous commit; it had no findings. Two eslint-disable comments in index.jsx said "see the note on the first console.error above". That note went to the hooks directory with useDetectedEditors, and nothing above them holds one now. The packaged smoke test asserted that `components` and `hooks` are not in the archive through arrayContaining; toContain says the same thing. The README named index.jsx as what the bundle is rebuilt from. It is rebuilt from the components and hooks too. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: WordPress/contributor-toolkit/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (17)
Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe renderer’s seven UI components and five hooks are defined in dedicated JSX modules. The renderer entry point imports those modules. Electron packaging excludes all renderer JSX files, and packaged smoke checks verify that exclusion. README, TESTING.md, and review instructions now cover renderer components and hooks. Assessment against linked issues
Priority: ⬇️ Low Merge Risk: ⚪ Minimal · up to No actionable merge-blocking issue is established. The renderer modularization and packaging changes appear consistent with the behavior-preserving objective and are mergeable subject to normal checks. Security Architecture ReviewSecurity architecture risk: ⚪ Minimal · up to The new files remain internal to the existing renderer bundle. The reviewed changes preserve action controls and shared-state ownership, while packaging excludes the uncompiled sources. No material security risk was found to be introduced or worsened. Retained concerns Security review detailsSecurity Blast Radius
Trust Boundaries and Controls
Resilience and Maintainability Implications
🚥 Pre-merge checks | ✅ 3 | ❓ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (3 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
## Why `SiteRow` is about 5,000 lines and holds the state of everything a site's view can do. The redesign in #542 needs its dialogs as components it can restyle one at a time, and #553 asks for them to leave `index.jsx` one per pull request. This is the first: the rename dialog, chosen because nothing else in `SiteRow` reads its state. ## What changes `src/renderer/components/rename-site-modal.jsx` is new. It holds what was inline in `SiteRow`: the value being typed, the refusal, the busy flag, two effects, the handlers and the markup. `SiteRow` keeps the one thing that is its business, whether the dialog is open. One thing is different in structure. **The component is mounted while the dialog is open and not otherwise.** Opening used to reset the value to the site's name and clear the last refusal by hand; a fresh mount does both, so those two lines are gone rather than moved. Everything else is the code that was there, with `onClose()` where it set `SiteRow`'s flag. **No journey drove this dialog**, so one does now. `rename-site.spec.js` was written against the inline version first and passes on both: the dialog opens on the site's name with it selected, refuses an empty one, writes nothing when cancelled, and puts the saved name in the sidebar and the heading. `siteHeading` joins the shared locators in `ui.cjs`, now that two files use it. Deliberately not in this PR: the other four dialogs, and the two things listed under Risks that would change what the dialog does. ## How to test this Platforms: any. Use the Buildkite build for the current head of this branch. The journey carries this: `npx playwright test --project=journeys -g "rename dialog"`, from the repository root. It passes on `trunk`'s inline dialog and on the component, and it is red when Save does not rename, when an empty name is accepted, when Cancel saves what was typed, and when the name is not selected on opening. What it does not reach is the dialog while a rename is pending, which is too brief to stage against a local store: **Starting state:** the app open on any site. 1. Click the pencil beside the site's name. The "Rename site" dialog opens with the name selected. 2. Type a new name and press Enter. The dialog closes and the name changes in the sidebar and in the heading. 3. Open it again, clear the field, and click "Save". "Site name cannot be empty." appears under the field and the dialog stays open. 4. Press Escape. The dialog closes and the name is unchanged. **What must not have happened:** - The name typed and then abandoned in step 4's dialog is not in the field the next time it opens. - No other site's name changed. ## Risks and limitations - **A failed rename still closes the dialog.** `App`'s rename handler catches every error, shows it in a native alert and resolves, so the dialog's own place for a refusal can never be reached and what was typed is lost. This is how `trunk` behaves; the move keeps it, and fixing it changes behaviour. - **The empty-name check is still in a `.jsx` file**, where the unit suite cannot reach it. It was moved, not added. The rule in the review standard says it belongs in a `.cjs` module with a test; the journey covers it at layer 4 until then. - **The journey costs one app launch** on every pull request, on both platforms: about a second and a half locally on macOS. - **Two of its assertions could not be shown to be load-bearing on their own.** The saved name's surrounding spaces are removed by the main process as well as the dialog, and what was typed is forgotten both by the fresh mount and by an effect; breaking one of each pair leaves the test green. - **Checked on macOS only.** Windows is covered by CI. - Review: 1 [fix here], fixed; 2 [follow-up], the first two items above. ## Related Part of #553, which has four more dialogs. Part of #542. Follows #567. --- <details> <summary>Design decisions and alternatives considered</summary> **Mounted while open, rather than always mounted with an `isOpen` prop.** An always-mounted component would have to reset its own state each time it opens, from an effect, which is the code this removes. Mounting it fresh is the reset. **`SiteRow` still owns the open flag.** The button that opens the dialog is in the site's header, which `SiteRow` renders, so the flag has to live where both can reach it. **The state keeps its names**, `renameValue`, `renameError`, `renaming`, although inside a component called `RenameSiteModal` the prefix says nothing. Keeping them makes the diff readable as a move. **A journey of its own rather than a step in `engine.spec.js`.** That file's restart test renames through the handler directly, deliberately, because what it proves is the write reaching disk. The dialog is a different claim. </details> <details> <summary>Review outcome (required — see AGENTS.md)</summary> **1 [fix here] · 2 [follow-up]** — the one fixed, the two recorded under Risks. - Tests, low, [fix here], fixed: TESTING.md and the helper's header listed what `ui.cjs` holds as the shared controls and four locators, and `siteHeading` is neither. Both say five. - Architecture, low, [follow-up], deferred: the empty-name check and its sentence are in JSX. Moved, not added; extracting it to a module changes nothing a contributor sees but is a change to the code's shape beyond a move. - Architecture, low, [follow-up], deferred: `App.onRename` swallows a failed write, so the dialog closes on failure. It predates this change, and fixing it is a behaviour change. The review went through every piece of state, effect, handler and prop against `trunk` and found the behaviour the same, including a state update after unmount on success, closing while a rename is pending, and focus and selection on opening. - **Review:** completed — fresh agent context, local, before the PR existed, read-only; head b6c996d / base 2b6a69a; committed work only, working tree clean; outcome above. CodeRabbit: not run yet. - **Since review:** `b6c996d → 655a5ed` checked. The difference is the fix and one comment in the component. It was not reviewed again by a fresh context. Checks, macOS, local: | Check | On | Result | |---|---|---| | `eslint . --max-warnings=0` | 655a5ed | clean | | unit suite on Node | b6c996d | 1691 pass, 2 skipped, 0 fail | | unit suite on Electron's Node | b6c996d | 1693 pass, 0 fail | | `npm run test:e2e` | b6c996d | 35 of 35 pass | | the two journeys that use what the second commit touched | 655a5ed | 2 of 2 pass | | `npm run test:e2e:packaged` | b6c996d | 15 of 15 pass | Lint and the unit suites were run on this checkout's files only: a worktree left by another local session sits under `.claude/worktrees/`, and the bare commands collect it. </details> 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Why
src/renderer/index.jsxis 6,420 lines, and the redesign in #542 has to change almost all of it. Between its imports and the two components that are most of the file sat seven components that take props and nothing else, and five hooks. They can be changed one at a time once they are files of their own, and what is left inindex.jsxafterwards is the part that still has to be taken apart.What changes
The twelve move, verbatim, and
index.jsximports them back:src/renderer/components/:ReasonedButton,DiscardChangesLink,DiffText,LogText,DestinationandDestinationGroup(one file),TerminalCommandLink.src/renderer/hooks/:useDetectedEditors,useContributorProvenance,useNextActionCue,useSites, anduseConfirmationwithConfirmationContext.Nothing is rewritten. Each function gains
export, each file gains the imports it needs, and five comments that pointed "above" or "below" now name the file they mean.index.jsxis 6,085 lines.Two things follow from there being more than one renderer source:
build.filesexcludes every.jsxundersrc/renderer/, where it excludedindex.jsxalone. In that directory.jsxnow means "bundled by esbuild", and without this the new files would ship uncompiled beside the bundle that already contains them. The hooks are named.jsxfor that reason, although they hold no JSX. The packaged smoke test reads the same rule.index.jsxis one the suite cannot reach. That is as true of a component or a hook beside it, and they say so.Deliberately not in this PR: any change to what a component renders or a hook does, the five modals (#553), and
SiteRow's state (#554).How to test this
Platforms: any. Use the Buildkite build for the current head of this branch.
The journeys carry this:
npm run test:e2e, from the repository root, drives every flow the moved components and hooks take part in. The packaged smoke test, afternpm run build:once && CSC_IDENTITY_AUTO_DISCOVERY=false npm run pack:dir, proves the app boots from an archive without the sources:npm run test:e2e:packaged. It is red ifbuild.filesexcludes onlyindex.jsx; that was checked by putting the old pattern back.Nothing on screen should differ, so a manual pass is a look for what the journeys do not reach:
Starting state: the app open on a site that has finished setup, with an edited file.
What must not have happened:
Risks and limitations
npm testwas not rerun on the last commit. It changes two comments, the README and two smoke-test assertions. The unit suites ran on the commit before it.Checked on macOS only. Windows is covered by CI's journeys and packaged smoke.Checked on Windows alsoindex.jsx's two-space indentation, where.editorconfigasks for tabs in new code, so that the diff reads as a move..editorconfigsteers editors and fails nothing.Related
Closes #552
Part of #542. Follows #566.
Design decisions and alternatives considered
hooks/besidecomponents/, where the issue's title namescomponents/for both. The step that splitsSiteRow's state (#554) will add a hook per domain, and they are not components.Kebab-case file names, like every other file under
src/, rather than thePascalCase.tsxof the prototype.One pattern,
!src/renderer/**/*.jsx, rather than a line per directory. A directory list has to be remembered the next time one is added; the extension is a rule a new file follows by being what it is. The.cjsmodules beside them are not covered, deliberately: the main process requires some of them.No rewrite on the way.
formatEmailDate, the style constants and the labels at the top ofindex.jsxstay where they are; they are not components, and moving them is not what the issue asks.Review outcome (required — see AGENTS.md)
0 [fix here] · 0 [follow-up] — no findings across the five dimensions.
The review checked the move mechanically: it stripped each new file's imports and
export, found the rest as an exact substring oftrunk'sindex.jsx, and diffed what remained against the branch's. All eleven files are byte-identical totrunkapart from the comment edits. It also confirmed no circular import, one sharedConfirmationContext, an unchanged order of style registration, that the packaging pattern matches all twelve.jsxand nothing that must ship, and that the translation extraction script still sees the new files.Style notes, applied in the second commit: two comments in
index.jsxstill pointed at a note that had moved; the smoke test's directory checks were written througharrayContainingwheretoContainsays it; the README namedindex.jsxalone as the bundle's source.Style notes, left alone as unrelated to this change:
.editorconfigandeslint.config.mjssayindex.jsxuses four spaces, and the review standard still says "4000-line component".1ab1520 → 4c98b4cchecked. The difference is the three style notes. It was not reviewed again by a fresh context.Checks, macOS, local:
eslint . --max-warnings=0.claude/worktrees/ignored; it is not part of the repository)npm testnpm run test:electronnpm run test:e2enpm run test:e2e:packagedImplementation notes
How the move was checked before the review: every line that left
index.jsxwas looked up in the new files. Of 335, all but eight are there unchanged; the eight are five import lines and three comments, each rewritten.@wordpress/i18n's extraction (scripts/generate-pot.cjs, from #541) walkssrc/and picks up.jsx, so a string wrapped in a component later is found without a change there.Screenshots or recording
Nothing on screen changed, and no images are attached. The twelve shots
npm run shotscaptures were taken ontrunkand on this branch with a two-second settle before each capture. Eleven are pixel-identical. The twelfth,gutenberg-site-view, differed once in its terminal, which is an intermittent rendering problem ontrunkitself: eight captures of that shot on unchangedtrunkcode showed it twice, and eight on this branch not at all. It is being looked at separately.With the harness's own 300ms settle, one shot differs from
trunkby 14 corner pixels of a sidebar entry: a transition caught part-way, which is why the comparison was repeated with a longer settle.🤖 Generated with Claude Code