Skip to content

perf(admin): defer icon and chart bundles - #2516

Open
richardjoo wants to merge 4 commits into
emdash-cms:mainfrom
richardjoo:perf/defer-admin-icon-chart-bundles
Open

perf(admin): defer icon and chart bundles#2516
richardjoo wants to merge 4 commits into
emdash-cms:mainfrom
richardjoo:perf/defer-admin-icon-chart-bundles

Conversation

@richardjoo

@richardjoo richardjoo commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Defers uncommon plugin navigation icons into 32 deterministic Phosphor buckets and lazy-loads Block Kit chart rendering. Existing icon names and aliases remain supported; unknown or failed icon chunks fall back to Plug, while failed chart chunks retain the configured-height placeholder instead of unmounting the route.

Adds a production-fixture bundle guard that checks the emitted chunk graph, rejects eager/full-catalog regressions and stale CI builds, and runs between the SQLite and D1 query-count builds.

Approved performance Discussion: #2486

Type of change

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: OpenCode with GPT-5.6 Sol

Screenshots / test output

No persistent visual change after lazy chunks load. The chart fallback reserves the configured height while loading and on chunk failure.

Matched production-site build measurements:

Metric Baseline Candidate Delta
Entry gzip 796,756 B 485,414 B -39.1%
Entry Brotli 633,278 B 381,927 B -39.7%
Initial closure gzip 953,375 B 757,531 B -20.5%
Initial closure Brotli 766,262 B 610,568 B -20.3%

Current-upstream fixture guard:

PluginRegistry: 1,869,922 / 2,250,000 bytes
Initial static closure: 2,753,894 / 3,150,000 bytes across 4 chunks
Initial Phosphor definitions: 153 / 350
Non-bucket lazy Phosphor definitions: 0 / 96 total; largest chunk 0 / 32
Lazy icon buckets: 32; largest 217,820 / 275,000 bytes
Lazy chart chunks: 1

Verification:

  • pnpm typecheck
  • Admin targeted tests: 23 passed
  • Blocks targeted tests: 29 passed
  • Core Vite integration tests: 10 passed
  • pnpm bundle:check
  • pnpm format and pnpm format:check
  • git diff --check

Copilot AI lite review requested due to automatic review settings August 16, 2026 18:27
@changeset-bot

changeset-bot Bot commented Aug 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 08002e5

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 17 packages
Name Type
@emdash-cms/admin Patch
@emdash-cms/blocks Patch
emdash Patch
@emdash-cms/plugin-embeds Patch
@emdash-cms/cloudflare Patch
@emdash-cms/sandbox-workerd Patch
@emdash-cms/plugin-mcp-smoke Patch
@emdash-cms/fixture-perf-site Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/do-demo-site Patch
@emdash-cms/do-solo-demo-site Patch
@emdash-cms/auth Patch
@emdash-cms/gutenberg-to-portable-text Patch
@emdash-cms/x402 Patch
create-emdash Patch
@emdash-cms/auth-atproto Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the right change for the right reason. Deferring the full Phosphor catalog and the chart renderer out of the admin entry chunk matches the approved Discussion and follows EmDash’s existing code-splitting patterns. The deterministic icon buckets, alias-preserving loader, and production-fixture bundle guard are well-scoped and the snapshot reuse in the query-counts workflow avoids a redundant build.

I reviewed the diff, the generator/loader logic, the bundle-guard script, the Vite test/dev resolution plugins, the chart lazy-loading path, and the new tests against AGENTS.md. No security issues, data-integrity regressions, or broken contracts were found. The only issues are test/assertion quality and comment discipline:

  • The new chart-failure test and the new chart-height test assert exact Tailwind class strings back at the implementation. Per AGENTS.md these are tautological tests that cannot fail on a real regression.
  • The bundle-guard script opens with several paragraphs justifying the threshold constants, which AGENTS.md treats as reviewer-facing justification rather than durable code context.

Once the class-string assertions are removed/rewritten and the rationale comments are trimmed, this is good to land.

});

const placeholder = container.querySelector<HTMLElement>('[aria-hidden="true"]');
expect(placeholder?.className).toContain("rounded-lg border border-kumo-line p-4");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] This assertion repeats the exact Tailwind class string from ChartPlaceholder:

expect(placeholder?.className).toContain("rounded-lg border border-kumo-line p-4");

It is a tautological test: the only way it fails is if someone intentionally changes those classes. Per AGENTS.md, that makes it worse than no test. The useful behavior here is that a placeholder is rendered and the configured height is preserved. Drop the class assertion and keep the height check.

Suggested change
expect(placeholder?.className).toContain("rounded-lg border border-kumo-line p-4");
expect(placeholder).toBeTruthy();

Comment thread packages/blocks/tests/renderer.test.tsx Outdated
Comment on lines +454 to +457
expect(fallback?.classList).toContain("rounded-lg");
expect(fallback?.classList).toContain("border");
expect(fallback?.classList).toContain("border-kumo-line");
expect(fallback?.classList).toContain("p-4");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] These four assertions pin the implementation detail (the exact Tailwind classes added in this PR) straight back:

expect(fallback?.classList).toContain("rounded-lg");
expect(fallback?.classList).toContain("border");
expect(fallback?.classList).toContain("border-kumo-line");
expect(fallback?.classList).toContain("p-4");

Like the chart-failure test, this restates the diff and can only fail on an intentional style change. Remove them and exercise the observable behavior instead: the placeholder exists before the chart resolves and the configured height is applied. The later assertions that the chart appears and the placeholder disappears already cover the real behavior.

Comment thread scripts/check-admin-client-bundle.mjs Outdated
Comment on lines +8 to +17
// The entry and closure ceilings retain roughly 400 KB of normal bundle drift,
// but less than one deferred chart payload. The definition cap is over twice
// the optimized fixture count while remaining far below the full icon catalog.
const MAX_PLUGIN_REGISTRY_BYTES = 2_250_000;
const MAX_INITIAL_CLOSURE_BYTES = 3_150_000;
const MAX_INITIAL_PHOSPHOR_DEFS = 350;
// Feature chunks may carry a focused icon set; namespace imports exceed both caps by an order of magnitude.
const MAX_NON_BUCKET_LAZY_PHOSPHOR_DEFS = 32;
const MAX_TOTAL_NON_BUCKET_LAZY_PHOSPHOR_DEFS = 96;
// A deferred bucket has about 25% growth room before one icon request becomes too large.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] The constant block opens with reviewer-facing justification rather than durable context for future readers:

// The entry and closure ceilings retain roughly 400 KB of normal bundle drift,
// but less than one deferred chart payload. The definition cap is over twice
// the optimized fixture count while remaining far below the full icon catalog.
// Feature chunks may carry a focused icon set; namespace imports exceed both caps by an order of magnitude.
// A deferred bucket has about 25% growth room before one icon request becomes too large.

AGENTS.md says comments should not justify decisions, narrate alternatives, or address reviewers. These thresholds are either self-explanatory (the constants are right above them) or belong in the changeset/PR description. Delete this comment block; if the history really matters, capture it in the changeset instead.

plugins: [linguiMacroPlugin()],
// @tiptap/suggestion is intentionally bundled (devDependency)
inlineOnly: false,
// Generated icon buckets are package-owned chunks; normal root imports stay consumer-owned.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] This comment justifies the noExternal regex rather than explaining a non-obvious invariant to future readers:

// Generated icon buckets are package-owned chunks; normal root imports stay consumer-owned.
noExternal: /^@phosphor-icons\/react\//,

AGENTS.md discourages decision-justification comments. The regex itself already encodes the rule (deep @phosphor-icons/react/* imports are bundled; the bare import is listed in external). Either delete the comment or replace it with a short note about why a bare root import must remain external (consumer-side deduplication / initial icon set).

@github-actions

Copy link
Copy Markdown
Contributor

Scope check

This PR changes 2,550 lines across 54 files. Large PRs are harder to review and more likely to be closed without review.

If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs.

See CONTRIBUTING.md for contribution guidelines.

@pkg-pr-new

pkg-pr-new Bot commented Aug 16, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@2516

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@2516

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@2516

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@2516

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@2516

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@2516

emdash

npm i https://pkg.pr.new/emdash@2516

create-emdash

npm i https://pkg.pr.new/create-emdash@2516

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@2516

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@2516

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@2516

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@2516

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@2516

@emdash-cms/registry-verification

npm i https://pkg.pr.new/@emdash-cms/registry-verification@2516

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@2516

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@2516

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@2516

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@2516

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@2516

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@2516

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@2516

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@2516

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@2516

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@2516

commit: 08002e5

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves admin/perf-site bundle performance by code-splitting uncommon Phosphor navigation icons into deterministic lazy buckets and lazy-loading the Block Kit chart renderer, plus adding a CI guard to detect bundle-regression chunk graphs during the query-counts workflow.

Changes:

  • Introduces generated Phosphor icon bucket modules + a runtime loader/fallback, and updates admin navigation icon resolution to use lazy buckets (with an explicit Phosphor version pin).
  • Lazily loads the Block Kit chart renderer with a height-preserving placeholder and a failure fallback that keeps routes mounted; adds targeted tests.
  • Adds a production-fixture bundle metadata emitter and a bundle:check guard script, wired into the Query Counts workflow between SQLite and D1 builds.

Reviewed changes

Copilot reviewed 20 out of 54 changed files in this pull request and generated no comments.

Show a summary per file
File Description
scripts/check-admin-client-bundle.mjs Emits/validates fixture client chunk graph and enforces bundle ceilings (icons/charts).
pnpm-workspace.yaml Pins @phosphor-icons/react exactly to keep generated buckets deterministic.
pnpm-lock.yaml Updates catalog specifier to match the exact Phosphor pin.
packages/core/tests/unit/astro/vite-config.test.ts Verifies dev-time Phosphor deep-import resolver behavior under source aliasing.
packages/core/src/astro/integration/vite-config.ts Adds a Vite plugin to resolve Phosphor deep imports when serving raw admin source.
packages/blocks/tests/renderer.test.tsx Updates chart mocks and adds a test ensuring height is reserved during chart lazy-load.
packages/blocks/tests/renderer-chart-failure.test.tsx Adds coverage for chart chunk failure: logs error and keeps placeholder mounted.
packages/blocks/src/renderer.tsx Switches chart block rendering to React.lazy + Suspense with failure fallback.
packages/admin/vitest.config.ts Adds a Vite resolve plugin so Phosphor deep imports resolve correctly in browser tests.
packages/admin/tsdown.config.ts Bundles Phosphor deep imports for bucket chunks while keeping root import external.
packages/admin/tests/lib/phosphor-icon-loader-failure.test.ts Ensures icon-bucket load failures log and fall back to Plug.
packages/admin/tests/components/Sidebar.test.tsx Adds assertions for name/alias/icon-module-alias resolution via the bucket loader.
packages/admin/src/lib/phosphor-icon-loader.ts Implements deterministic bucket selection + lazy loading + Plug fallback on chunk failure.
packages/admin/src/generated/phosphor-icon-buckets/module-aliases.ts Generated mapping of exported aliases to owning icon modules.
packages/admin/src/generated/phosphor-icon-buckets/bucket-00.ts Generated Phosphor icon bucket 00 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-01.ts Generated Phosphor icon bucket 01 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-02.ts Generated Phosphor icon bucket 02 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-03.ts Generated Phosphor icon bucket 03 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-04.ts Generated Phosphor icon bucket 04 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-05.ts Generated Phosphor icon bucket 05 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-06.ts Generated Phosphor icon bucket 06 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-07.ts Generated Phosphor icon bucket 07 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-08.ts Generated Phosphor icon bucket 08 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-09.ts Generated Phosphor icon bucket 09 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-10.ts Generated Phosphor icon bucket 10 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-11.ts Generated Phosphor icon bucket 11 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-12.ts Generated Phosphor icon bucket 12 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-13.ts Generated Phosphor icon bucket 13 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-14.ts Generated Phosphor icon bucket 14 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-15.ts Generated Phosphor icon bucket 15 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-16.ts Generated Phosphor icon bucket 16 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-17.ts Generated Phosphor icon bucket 17 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-18.ts Generated Phosphor icon bucket 18 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-19.ts Generated Phosphor icon bucket 19 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-20.ts Generated Phosphor icon bucket 20 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-21.ts Generated Phosphor icon bucket 21 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-22.ts Generated Phosphor icon bucket 22 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-23.ts Generated Phosphor icon bucket 23 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-24.ts Generated Phosphor icon bucket 24 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-25.ts Generated Phosphor icon bucket 25 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-26.ts Generated Phosphor icon bucket 26 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-27.ts Generated Phosphor icon bucket 27 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-28.ts Generated Phosphor icon bucket 28 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-29.ts Generated Phosphor icon bucket 29 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-30.ts Generated Phosphor icon bucket 30 exports.
packages/admin/src/generated/phosphor-icon-buckets/bucket-31.ts Generated Phosphor icon bucket 31 exports.
packages/admin/src/components/admin-navigation-icons.ts Switches plugin nav icon lazy import path to bucket loader + fallback.
packages/admin/scripts/generate-phosphor-icon-buckets.js Generates deterministic icon bucket modules + module-alias mapping; supports --check.
packages/admin/package.json Adds icon generation/check scripts and wires them into build/dev/typecheck.
package.json Adds bundle:check and bundle:check:ci scripts.
fixtures/perf-site/astro.config.mjs Adds bundle-metadata emitter plugin to the perf fixture’s Vite build.
.oxfmtrc.json Excludes generated icon buckets from formatting.
.github/workflows/query-counts.yml Runs bundle:check:ci after SQLite snapshot regeneration and before D1.
.changeset/small-icons-load.md Changeset describing deferred icon/chart payloads and failure behavior.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@richardjoo

Copy link
Copy Markdown
Contributor Author

Addressed the automated review in 88f59ccc:

  • removed exact Tailwind class assertions from both chart fallback tests
  • retained behavior checks for configured-height reservation, transition to the loaded chart, and persistent placeholder height on chunk failure
  • removed the threshold-rationale comments from the bundle guard

Reverification:

  • blocks targeted tests: 29 passed
  • blocks typecheck passed
  • pnpm format:check passed
  • pnpm bundle:check passed with the same bundle metrics

The first Browser Tests run failed only in unchanged SeoPanel.test.tsx after 1,461 other tests passed. That test passed three consecutive isolated browser runs locally, so the new push will provide a clean full-suite rerun.

@github-actions github-actions Bot added review/needs-rereview Author pushed changes since the last review and removed review/awaiting-author Reviewed; waiting on the author to respond labels Aug 16, 2026
@richardjoo

Copy link
Copy Markdown
Contributor Author

CI lint exposed four prefer-static-regex diagnostics that the local Oxlint allocator crash had hidden. Fixed in f25491ce by hoisting the icon bucket, chart module, Phosphor definition, and JavaScript module expressions to module scope.

pnpm format:check and pnpm bundle:check pass after the fix with unchanged bundle metrics.

@richardjoo

Copy link
Copy Markdown
Contributor Author

Retriggering CI after unrelated runner flakes and a transient pkg.pr.new 500; no code change.

@richardjoo richardjoo closed this Aug 16, 2026
@richardjoo richardjoo reopened this Aug 16, 2026

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the right change for the right reason: it defers the full Phosphor catalog and the Block Kit chart renderer out of the admin entry chunk, matching the approved Discussion and EmDash's existing code-splitting patterns. The deterministic FNV buckets, alias-preserving loader, and production-fixture bundle guard are well scoped.

Both prior-review issues are resolved in this revision:

  • The chart failure and chart height tests no longer assert exact Tailwind class strings; they now check the Suspense placeholder through the aria-hidden container and the rendered chart's data-height.
  • The bundle-guard script no longer opens with long rationale comments about threshold constants.

I reviewed the diff, the generator/loader logic, the bundle-guard script, the Vite dev/test resolution plugins, the lazy chart path, and the new tests against AGENTS.md. No security issues, DB regressions, localization leaks, or API contract breaks. There is one remaining robustness issue in the icon loader.

Remaining issue: loadPhosphorIcon does an own-property check before returning a bucket member. Because the bucket object inherits from Object.prototype, a name like "toString" would return Object.prototype.toString instead of undefined, and isIconComponent would then accept it as a valid icon. The normal caller (resolveNavIcon) PascalCases the name first, so the current UI path avoids this, but the exported loader's contract is to return undefined for unknown names and should not be sensitive to prototype properties.

const loadBucket = PHOSPHOR_ICON_BUCKETS[getBucket(moduleName)];
if (!loadBucket) return undefined;
try {
return (await loadBucket())[name];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] The bucket access return (await loadBucket())[name]; does not verify that name is an own property of the module namespace. Since the namespace inherits from Object.prototype, names like "toString" or "constructor" return prototype methods, pass isIconComponent, and get rendered as bogus icons instead of falling back to Plug.

The surrounding code already returns undefined for truly unknown icons, so the intent is clearly to fall back; prototype pollution breaks that intent. Add an own-property check:

Suggested change
return (await loadBucket())[name];
const bucket = await loadBucket();
if (!Object.prototype.hasOwnProperty.call(bucket, name)) return undefined;
return bucket[name];

@richardjoo

Copy link
Copy Markdown
Contributor Author

Addressed the final loader robustness issue in 08002e56.

  • added a regression test proving inherited constructor previously reached hashing and rejected instead of resolving undefined
  • made generated alias-map and loaded bucket lookups own-property-only with Object.hasOwn

Verification:

  • icon-focused browser tests: 24 passed
  • admin typecheck passed
  • pnpm format:check passed
  • pnpm bundle:check passed (PluginRegistry 1,869,922 bytes; initial closure 2,753,894 bytes; 32 lazy buckets and one lazy chart chunk)

@richardjoo

Copy link
Copy Markdown
Contributor Author

Retriggering the unchanged head once more for the detached Chromium frame flake in unchanged hooks.test.tsx; all changed-code, lint, typecheck, bundle, preview, visual, and E2E gates pass.

@richardjoo richardjoo closed this Aug 16, 2026
@richardjoo richardjoo reopened this Aug 16, 2026

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the right change for the right reason. It defers the full Phosphor catalog and the Block Kit chart renderer out of the admin entry chunk, matching the approved performance Discussion and EmDash’s existing client-only architecture (the admin shell renders with client:only="react", so Suspense/lazy icons do not affect SSR). The deterministic FNV buckets, alias-preserving loader, production-fixture bundle guard, and dev/test deep-import resolvers are all well-scoped.

I reviewed the diff and the full changed files statically — I did not re-run pnpm test, lint, or the bundle guard in this environment, so I’m relying on the author’s verification checklist for those.

Headline checks:

  • The prior-review prototype-property issue is resolved: loadPhosphorIcon now uses Object.hasOwn both on the alias map and on the bucket module, and Sidebar.test.tsx adds a regression test for "constructor".
  • The chart Suspense/fallback preserves the configured height on load and on chunk failure; the tests now assert observable behavior (data-height, aria-hidden placeholder) rather than brittle Tailwind class strings.
  • The bundle-guard script no longer opens with long threshold-rationale comments.
  • No new user-facing strings are added, so the Lingui/Done column is correctly n/a.
  • No DB, auth, API-route, or locale-filter changes are introduced.
  • The changeset is present and describes the user-visible effect, not implementation internals.

I didn’t find any blocking issues, regressions, or AGENTS.md violations in the changed code. LGTM.

@github-actions

Copy link
Copy Markdown
Contributor

Overlapping PRs

This PR modifies files that are also changed by other open PRs:

This may cause merge conflicts or duplicated work. A maintainer will coordinate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants