perf+fix: keep Monaco off first paint, and unblock the dialect e2e matrix - #391
Merged
Conversation
The app pulled 4.04 MB of JavaScript before it could render — about 1.2 MB
gzipped — and 2.6 MB of that was the Monaco editor, which is not needed until
someone opens the SQL Editor.
The lazy() calls meant to prevent this were decoration. `features/sql-editor`
is loaded through `lazy(() => import(...))` in App.tsx, but the same barrel was
*also* imported statically by App.tsx, TopToolbar, CloneTableModal and
FileQueryModal — and a barrel is one module, so importing `insertAtCursor` or
`TYPE_META` from it dragged the editor in with them. Rolldown had been printing
INEFFECTIVE_DYNAMIC_IMPORT on every build; nothing was reading it.
The first fix I tried was deep imports past the barrel, which architecture.test
correctly rejected: a feature may compose another feature's *components* but
not reach into its lib/ or api/. The rule is right and the fix was wrong.
So the barrel gets smaller instead. It keeps the light surface other features
genuinely use — SchemaTreePanel, WriteConfirmDialog, the clone-table and
editor-bridge helpers — and stops re-exporting SqlEditor, SqlDiffEditor,
SqlEditorView and FileImportsPanel. Every consumer of those four already loads
them through `lazy(() => import(...))`, so pointing that import at the
component module costs them nothing, and `components/` is the direction the
layering rule allows.
eager payload 4.04 MB -> 1.27 MB raw
~1.2 MB -> 350 KB gzip
monaco eager yes -> no
INEFFECTIVE_DYNAMIC_IMPORT warnings 2 -> 0
Suite 3655 passed, architecture test green, typecheck clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
surfaces.tsx was extracted last week on the strength of a count — the same uppercase micro-label written eight ways across the frontend — and then almost nothing adopted it. Four files imported it; the literal spelling stayed in fifteen more places. An extraction that nobody uses is not a consolidation, it is a ninth variant with its own test file. Fifteen occurrences across nine files now reference `sectionLabelCls` instead of repeating the string. The markup is untouched: this replaces the class literal, not the element, which is the change that cannot alter rendering. The guard is the part that matters. `surfaces.guard.test.ts` fails when a fresh copy of the literal appears anywhere under frontend/, and names the file — the same way naming.test.ts already enforces file naming here rather than leaving it to review. A/B'd: reintroducing the literal in UtilitiesView.tsx fails the test and prints that path. Its second assertion guards the guard. If the number of files using the label ever falls back to a handful, the extraction is protecting nothing and should be reconsidered rather than defended. Suite 3657 passed, eslint 0 errors, typecheck clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The dialect e2e suites have been failing on every engine since the v3 UI landed, with `page.click` timeouts that read like the app was broken. A control run on 9539a4d — the commit before #385 merged — passes 11/11, so this arrived with v3 and nothing caught it, because e2e does not run in CI. Two causes, and the second is a real bug users hit. **The app lands on Home now.** The page objects reached straight for the compare controls, which are gated on `activeView === 'sync' && syncPane === 'compare'` — two conditions, not one, and Sync can open on Snapshots. AppPage grew a `gotoSync()` that satisfies both and is idempotent. **The source connection chip painted its own controls across the toolbar.** Measured, not guessed: BUTTON#source-config-btn x=512 w=25 pos=static DIV#connection-chip-source x=229 w=95 pos=static A static child rendering 190px outside a static parent. The chip is `min-w-0 flex-1`, so a crowded toolbar collapsed its box to 95px while its label, picker and buttons kept their intrinsic ~210px and spilled sideways — far enough that the "Same DB" pill covered the edit button. Playwright called it "not stable"; a person would call it a button that does nothing. Fixed at the layout level rather than with a z-index: the picker may now shrink (it already truncates), and the chip has a 15rem floor so a toolbar too narrow to hold it wraps — which the toolbar is already `flex-wrap` for — instead of crushing it. The first change alone moved the button from x=512 to x=334, still outside; the floor is what actually fits the contents in the box. postgres, mysql, mariadb and sqlserver: 40 passed, 4 failed, and all four failures are the same remaining test. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_81384b48-9481-415a-89be-95e42596bb3c) |
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.
Four things, in the order I found them.
1. The app downloaded 4 MB before it could render
2.6 MB of it was Monaco, which is not needed until the SQL Editor opens. The
lazy()calls meant to prevent that were decoration: four files also imported thesql-editorbarrel statically, and a barrel is one module, so importinginsertAtCursororTYPE_METAdragged the editor in with them. Rolldown had printedINEFFECTIVE_DYNAMIC_IMPORTon every build and nobody was reading it.My first attempt — deep imports past the barrel — was rejected by
architecture.test.ts, correctly: a feature may compose another's components but not reach into itslib/. So the barrel got smaller instead. It keeps the light surface other features use and stops re-exporting the four editor views, each of whose consumers already loads it throughlazy(() => import(...)).2. The shared label was extracted but never adopted
15 occurrences across 9 files now use
sectionLabelCls. More importantly,surfaces.guard.test.tsfails when a fresh copy of the literal appears anywhere and names the file — the waynaming.test.tsalready enforces conventions here. A/B'd: reintroducing it inUtilitiesView.tsxfails the test and prints that path.Its second assertion guards the guard: if adoption ever falls back to a handful, the extraction is protecting nothing and should be reconsidered rather than defended.
3. The dialect e2e matrix has been broken since v3 landed
Control run on
9539a4d711, the commit before #385: 11/11 pass. On current main: 4 failures per dialect. It arrived with v3 and nothing caught it, because e2e does not run in CI.activeView === 'sync' && syncPane === 'compare'— two conditions, not one.AppPage.gotoSync()satisfies both.A static child rendering 190px outside a static parent. The chip is
min-w-0 flex-1, so a crowded toolbar collapsed its box to 95px while its label, picker and buttons kept their intrinsic ~210px and spilled sideways — far enough that the "Same DB" pill covered the edit button. Playwright called it "not stable"; a user would call it a button that does nothing.Fixed at the layout level, not with a z-index: the picker may now shrink (it already truncates) and the chip has a 15rem floor, so a toolbar too narrow to hold it wraps — which it is already
flex-wrapfor. Worth noting the first change alone moved the button from x=512 to x=334, still outside; the floor is what actually fitted the contents.postgres, mysql, mariadb, sqlserver: 40 passed, 4 failed.
4. Also verified: the SQL Server size fix from #390
That fix shipped unverified because the container would not start. It turned out I had broken it myself — pruning the cloud agent's git worktree left five containers with bind mounts into a deleted path. Recreated, and the fix is now proven on a live engine with a 6-column, 500-row table:
Exactly 6x, the column count.
Still failing
All four dialects fail the same test — "schema history pane records a Lokee snapshot after migrate" — with
sync-pane-history-btnnot found after the migrate step. One root cause, not four. I stopped rather than keep patching blind, since I have no evidence yet for why the rail item is absent at that point.Unit suite 3657 passed, eslint 0 errors, typecheck clean.
🤖 Generated with Claude Code
Note
Low Risk
Mostly bundle splitting, test navigation, and CSS layout; no auth, data, or migration execution logic changes.
Overview
Cuts first-paint JavaScript by stopping the
sql-editorfeature barrel from re-exporting Monaco-backed views. Static imports of light symbols (TYPE_META,SchemaTreePanel, etc.) had been pulling ~2.6 MB Monaco into the eager graph and nullifyinglazy()(RolldownINEFFECTIVE_DYNAMIC_IMPORT). The barrel now keeps only the small surface; SqlEditor / SqlDiffEditor / SqlEditorView load via deeplazy()imports to their component modules inApp.tsx, migration history, object detail, and similar call sites. TopToolbar also deep-imports Lokee helpers instead of the feature barrel.E2E: Adds idempotent
AppPage.gotoSync()(Sync rail + Compare pane) before source/target connection flows, because the app defaults to Home and compare controls mount only whenactiveView === 'sync' && syncPane === 'compare'.UI fix: Connection chips get a
min-w-[15rem]floor and the saved-connection select can shrink (truncate) so a crowded flex-wrap toolbar no longer collapses the chip and lets the “Same DB” pill cover the edit button.Consistency: Many forms adopt shared
sectionLabelClsfrom@/shared/components/surfaces, withsurfaces.guard.test.tsfailing the build if the label literal is copy-pasted again.Reviewed by Cursor Bugbot for commit 40d0972. Bugbot is set up for automated code reviews on this repo. Configure here.