Repository navigation
Commit 5d69133
perf(console): load the Studio builder and the chart engine with their routes, not on every page (objectui#11798) (#11852)
Refs #11798 (item: Studio and the chart engine)
Clause-②: no
The console's first authenticated load no longer fetches the Studio
builder or the chart engine. Measured below on `main` `455c6466` and on
this branch by the same method: 336 → 333 JS chunks, 12,316,677 →
11,572,609 decoded bytes (−6.0%), 3,610,637 → 3,418,259 gzipped
(−192,378). The flow designer's canvas could not be moved from inside
`apps/console` (why, and the decision it needs, under "Not in this PR"),
so this PR is `Part of`, not a closing reference: that half stays open
on the card.
## What changed
- **Studio loads with its route.** `StudioRoute.tsx` and
`registerStudioComponents.tsx` reach `StudioDesignSurface` and
`BuilderLanding` through `import('./studioBuilder')` behind `lazy()`.
`apps/console/src/components/studioBuilder.ts` is a console-local module
that re-exports the two from `@object-ui/app-shell`, and nothing imports
it statically. No package entry or `exports` subpath is added: once no
static importer names the two components, the bundler leaves them out of
the eager set even though the barrel re-exports them. The entry gate
(`StudioRoute` then `RequireStudioAccess`) is unchanged and still
decides before the chunk is requested. Fallbacks: the full-screen pillar
builder shows `LoadingFallback`, the same splash as the gate's pending
state; the `/studio` landing and the `studio:builder` entry show a
one-line `common.loading` status inside their frame (`role="status"`).
- **Charts load with the first chart.** The chart engine was eager
because of a chunk rule, not an import. `vendor-charts` captured
`use-sync-external-store/shim` (a recharts dependency through
`react-redux`) by rolldown's default recursive capture,
`react-i18next`'s `useTranslation` imports that shim on every page, and
so the eager `vendor-i18n` chunk imported `vendor-charts`.
`vendor-react` now claims `use-sync-external-store`, and `vendor-charts`
keeps its default capture.
- **The eager-closure ledger moves with the bytes.** In
`scripts/check-eager-closure-budget.mjs`, `MAX_EAGER_CLOSURE_GZIP_BYTES`
goes from 3,637,000 to 3,444,622, down by exactly the 192,378 bytes that
left, which keeps the 26,363 bytes (0.29x) of headroom `main` had.
`BASELINE` is re-pinned to `c1d378ec` (3,418,259 across 333 of 2456
chunks) in the same commit; the record is the new objectui#11798 entry
on that constant. Without the re-pin, `pnpm check:eager-closure` exits 2
on this branch (the aggregate ceiling stood 2.40x the 89 KB regression
above the payload). The unit test's rendered-baseline literal moves from
3506.9 to 3338.1. No per-chunk row moved.
- The branch's first commit used `includeDependenciesRecursively: false`
on `vendor-charts` instead. A browser control caught it: recharts' other
dependencies fell into `plugin-charts`, the two chunks imported each
other, and a `chart` node rendered "Failed to load plugin: A is not a
function" (build of `8cc10b9`). It is replaced, and the reason is
recorded beside the `vendor-react` group.
## Acceptance notes
### First authenticated load, before and after
Method, the same on both sides: Playwright Chromium
(`/opt/pw-browsers/chromium`) at 1440x900, a cold context with a
signed-in session token, the built console served statically, the API
answered by a scratch mock (one admin, one organization, one default app
whose home is an object list), landing on `/` and redirected to
`/apps/demo_app/account`. Every `.js` response is counted and its
decoded body summed; each reading was repeated, identically. `main` was
built in this worktree with this branch's three bundler inputs checked
out from `455c6466` (blob hashes compared) and `studioBuilder.ts`
removed.
| | `main` `455c6466` | this branch | delta |
|---|---:|---:|---:|
| JS chunks fetched | 336 | 333 | −3 |
| decoded bytes | 12,316,677 | 11,572,609 | −744,068 (−6.0%) |
| eager closure, gzipped | 3,610,637 | 3,418,259 | −192,378 |
| eager chunks of all chunks | 336 of 2452 | 333 of 2456 | |
The fetched set equals the build's eager closure on both sides, in count
and in raw bytes. The chunk count barely moves because 280 (`main`) and
276 (branch) of those chunks are single-icon `vendor-icon-*` chunks;
bytes are the measure. The filer's 336 chunks and ~11.7 MB reproduce on
`main`.
Per half, each reverted alone on the final tree and built the same way:
the chart half is +158,535 gzipped / +583,407 decoded, the Studio half
+33,928 / +160,671. Most of `index`'s Studio loss (−72,913 gzipped) is
offset by a new `studioScope` chunk (+40,375): 49 modules `index` held
on `main` (the app header and the AI chat page among them), split out
because the lazy builder imports them too. That is re-attribution, not
new payload.
### `pnpm check:eager-closure`
- `main`, with its own constants: exit 0, 3526.0 KB of 3551.8 KB,
headroom 25.7 KB.
- This branch before the re-pin: exit 2, aggregate ceiling DRIFTED 2.40x
the regression above the payload.
- This branch after the re-pin, on the ordinary console build of
`f95872a`: exit 0, 3338.1 KB of 3363.9 KB, headroom 25.7 KB = 0.29x.
### H1 to H3, the dispatch's mechanism hypotheses
- **H1, corrected.** Not the `App.tsx` barrel import as such: the NAMED
static imports of `StudioDesignSurface` and `BuilderLanding` from the
barrel, in `StudioRoute.tsx` and `registerStudioComponents.tsx`, kept
them in the entry chunk. The flow canvas is eager through a different
barrel edge: app-shell's package entry imports
`views/metadata-admin/register-builtins.ts` for its side effects, which
reaches `previews/FlowPreview.tsx` and `previews/FlowCanvas.tsx` (chunk
`src`, eager on both sides). Of the "Monaco wiring", only
`@monaco-editor/react` and `@monaco-editor/loader` are eager (11
modules, 18,560 rendered bytes, through app-shell's
`useMonacoFallback.ts`); `@xyflow` is not in the bundle.
- **H2, falsified for the heavy plugins.** `register-plugins.ts` imports
grid, form, view, list and detail eagerly; charts, dashboard, map,
gantt, markdown, calendar, timeline and tree are `registerLazy` already,
and `plugin-charts` and `plugin-dashboard` source have 0 eager modules
on both sides.
- **H3, confirmed for the chart engines.** The `vendor-charts` rule
alone made 578 modules (1,305,476 rendered bytes) eager while none of
them was statically reachable. One more instance of the class remains,
`vendor-markdown`, under "Not in this PR".
### Controls, in the browser, on the ordinary build of `f95872a` with
the same mock plus fixtures
- A chart page (`/apps/demo_app/page/chart_page`, one `chart` bar node
with inline data): an SVG `.recharts-surface` with its axis labels,
`vendor-charts` and `plugin-charts` fetched on demand, no page error.
Same on `main`.
- A flow (`/apps/demo_app/metadata/flow/f1`): the flow canvas renders
both node cards. Same on `main`.
- Studio: `/studio` renders the landing under its header and fetches
`studioBuilder`; `/studio/~org/automations` renders the pillar builder
and fetches `studioBuilder`; no page error.
### Pins and reverse checks
- `apps/console/src/components/StudioRoute.lazyBuilder-11798.test.tsx`:
a refused principal never evaluates the builder module; the pending
window holds it back too; a holder gets it once; the landing renders in
its frame from the same module.
-
`apps/console/src/__tests__/registerStudioComponents.builderLazy-11798.test.tsx`,
renamed from `registerStudioComponents.builderDirect.test.tsx`, whose
objectui#5486 assertion (the builder on the first commit) is now the
wrong property: `studio:builder` is registered at boot with nothing of
the builder evaluated, and its first commit shows the loading status,
then the landing.
- The bundle half is pinned by `check:eager-closure`'s re-pinned
ceiling. No new gate.
- Reverse, unit: with the Studio source reverted to `main`, predicted 3
failed and 2 passed; observed `Tests 3 failed | 2 passed (5)`. The
restore is proven by an empty `git diff HEAD` and matching blob hashes.
- Reverse, gate: the re-pinned `check:eager-closure` on the reverted
builds, predicted exit 1 each; observed chart half reverted 3493.0 KB
(129.1 KB over), Studio half reverted 3371.3 KB (7.4 KB over), both
reverted 3526.0 KB (162.1 KB over). The final tree exits 0.
### Gates run on `f95872a`
- `pnpm exec turbo run build --filter=@object-ui/console...
--concurrency=2`: 35 of 35 tasks, exit 0 (the console's own `tsc`, `vite
build`, manifest and plugin build).
- `pnpm check:eager-closure`: exit 0, above. `pnpm
check:eager-locale-catalogues`: exit 0.
- `pnpm exec vitest run` over 52 files that name the touched files (the
console's App, AppContent, StudioRoute, registration, register-plugins
and loadLocales tests, the two new pins, and every `scripts/__tests__`
file that reads `check-eager-closure-budget.mjs` or a vite config): 52
files, 1723 tests passed.
- `pnpm --filter @object-ui/console type-check`: exit 0; its program
includes both new test files.
- `check:control-bytes`, `check:new-line-citations`,
`check:changeset-claims`, `check:pending-changeset-literals`,
`check:vi-mock-specifiers`, `check:vi-mock-inherit`,
`check:vi-mock-override-shape`, `check:test-path-roots`,
`check:unreferenced-sources`, `check:shell-escape-residue`,
`check:entry-guard`, `scripts/check-changeset-presence.mjs` and
`scripts/check-changeset-no-major.mjs`: exit 0 each.
- The repo-wide `pnpm lint` and the full test farm are CI's.
## Not in this PR
- **The flow canvas, and with it every metadata-admin designer.**
`@object-ui/app-shell`'s package entry imports
`views/metadata-admin/register-builtins.ts` for its side effects, and
that leaf statically reaches the previews and inspectors: on this branch
162 metadata-admin modules (1,807,259 rendered bytes) are eager, the
flow designer's 32 among them (292,151). A console-side `import()`
cannot move a side-effect import inside a package entry; only an
app-shell change (lazy registration there) or a new subpath export
could, and this claim allows neither. Reported on the card as a
decision.
- **`vendor-markdown`**, the same chunk-rule class as the charts: 53 of
its modules sit in the eager closure without being statically reachable.
Reported on the card for the seat.
- `plugin-chatbot` is eager through `SharedRecordPage` and app-shell's
AI hooks: a static import, not a chunk rule. Noted only.
Changeset: `.changeset/11798-console-eager-closure.md`,
`'@object-ui/console': patch` (the console is in the fixed release group
and ships `dist`).
Implemented by Claude Code in session
`https://claude.ai/code/session_01DrKzdPdyLLBW3qpZ4vtk7z`.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01DrKzdPdyLLBW3qpZ4vtk7z)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 4e7dd56 commit 5d69133
11 files changed
Lines changed: 602 additions & 203 deletions
File tree
- .changeset
- apps/console
- src
- __tests__
- components
- scripts
- __tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
This file was deleted.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
0 commit comments