Skip to content

perf(dashboard): defer lazy-only config data tables off the eager path — tech-geo + airports (#4404)#4407

Merged
koala73 merged 3 commits into
mainfrom
feat/defer-tech-geo-table
Jun 24, 2026
Merged

perf(dashboard): defer lazy-only config data tables off the eager path — tech-geo + airports (#4404)#4407
koala73 merged 3 commits into
mainfrom
feat/defer-tech-geo-table

Conversation

@koala73

@koala73 koala73 commented Jun 24, 2026

Copy link
Copy Markdown
Owner

Summary

Round 2 of the main.js diet (#4404), first data table. The ~62 KB tech-geo config table (STARTUP_HUBS/ACCELERATORS/TECH_HQS/CLOUD_REGIONS) sat on the eager dashboard critical path even though every consumer is lazy (search / map / globe / tech-hubs panel). The audit (see #4404) showed it was dragged eager by the @/config barrel value re-export and by data-loader → tech-activity → tech-hub-index running in the data-load cycle — a manualChunks rule alone is a no-op.

Recipe (all parts required — build-proven)

  • config/index.ts — drop the tech-geo value re-export from the eager @/config barrel (keep type re-exports; erased, no runtime edge).
  • Map.ts / DeckGLMap.ts / e2e/map-harness.ts — import the tech-geo values directly from @/config/tech-geo (they pulled them via the eager barrel).
  • data-loader.ts — lazy-load tech-activity in a new applyTechHubActivities() helper, guarded on the lazy tech-hubs panel being mounted, so the tech-activity → tech-hub-index → tech-geo chain no longer runs at boot. Degrades silently on load failure (non-critical panel data).
  • vite.config.tsmanualChunks rule isolating tech-geo into its own tech-geo-data chunk.
  • tests/dashboard-eager-chunks.test.mjs — dist-gated regression guard: asserts tech-geo-data stays out of dashboard.html modulepreload and is not statically imported by main (extensible to the remaining tables).

Bundle impact (VITE_VARIANT=full)

Asset Before After
main-*.js raw 1,021.7 KB 996.4 KB
main-*.js gzip 300.2 KB 293.7 KB
tech-geo-data (eager, in main's graph) 51 KB raw / 11 KB gzip, lazy

Verification

  • tsc --noEmit clean (incl. src/e2e); full build has 0 "Circular chunk" warnings (perf: split panel chunks by domain #4382 guard).
  • tech-geo-data absent from dist/dashboard.html modulepreload and not statically imported by main (only lazy chunks: deck-stack/Map/MapContainer/search-manager/tech-activity).
  • Runtime (preview): WebGL map renders (maplibre canvas, not the SVG fallback), zero console errors, tech-geo loads on demand with the map.
  • 3/3 new regression tests + 7/7 map-renderer-deferral tests pass.

Scope

First of the lazy-only tables in #4404. The same recipe extends to airports (14 KB) and ai-datacenters (86 KB, needs the country-intel → related-assets lazy-load); geo.ts/feeds.ts are genuinely boot-eager (separate issue); panel-storage is not deferrable.

Fixes part of #4404.

https://claude.ai/code/session_015Ae5Xavw1ZV4GMCWEsgKwe

…ath (#4404)

Round 2 of the main.js diet (issue #4404), first table. The ~62KB tech-geo data
table (STARTUP_HUBS/ACCELERATORS/TECH_HQS/CLOUD_REGIONS) was on the eager
critical path even though every consumer is lazy (search/map/globe/tech-hub
panel). It was dragged eager by (a) the @/config barrel value re-export and
(b) data-loader → tech-activity → tech-hub-index, called in the data-load cycle.

Full recipe (a manualChunks rule alone is a no-op — main still imports the chunk;
the eager import edge must be severed too):
- config/index.ts: drop the tech-geo VALUE re-export from the @/config barrel
  (keep type re-exports — erased, no runtime edge).
- Map.ts / DeckGLMap.ts: import tech-geo values directly from '@/config/tech-geo'
  (they imported via the eager barrel) — both are lazy renderers.
- data-loader.ts: lazy-load tech-activity (getTopActiveHubs) in a new
  applyTechHubActivities() helper, guarded on the lazy tech-hubs panel being
  mounted, so the tech-activity → tech-hub-index → tech-geo chain stays off boot.
- vite.config.ts: manualChunks rule isolating tech-geo into 'tech-geo-data'.
- tests/dashboard-eager-chunks.test.mjs: dist-gated regression guard asserting
  tech-geo-data stays out of dashboard.html modulepreload + not statically
  imported by main (extensible to the other tables).

Verified: tsc clean; full build has 0 circular-chunk warnings; tech-geo-data
absent from dashboard.html modulepreload and not statically imported by main;
main.js 1,021.7 → 996.4 KB (-25KB raw / -6.4KB gzip), tech-geo-data (51KB raw /
11KB gzip) now lazy. Runtime (preview): WebGL map renders (not SVG fallback),
zero console errors, tech-geo loads on demand with the map. 3/3 new + 7/7
map-renderer-deferral tests pass.

Claude-Session: https://claude.ai/code/session_015Ae5Xavw1ZV4GMCWEsgKwe
@vercel

vercel Bot commented Jun 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
worldmonitor Ignored Ignored Preview Jun 24, 2026 5:33pm

Request Review

@greptile-apps

greptile-apps Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR defers the tech-geo config data table (~62 KB raw) off the eager dashboard critical path by (1) dropping its value re-exports from the @/config barrel, (2) redirecting Map/DeckGLMap/e2e consumers to import directly from @/config/tech-geo, (3) lazy-loading tech-activity in a new applyTechHubActivities() helper guarded on panel presence, and (4) isolating the module into its own tech-geo-data Vite chunk. A new dist-gated regression test asserts the chunk stays off the entry's static import graph.

  • config/index.ts: Value re-exports of the four tech-geo tables are removed from the barrel; type-only re-exports are kept (erased at runtime, zero-cost).
  • data-loader.ts: getTopActiveHubs is converted from a static import to a dynamic import() inside applyTechHubActivities(), severing the data-loader → tech-activity → tech-hub-index → tech-geo eager chain.
  • tests/dashboard-eager-chunks.test.mjs: New guard verifies the tech-geo-data chunk is absent from dashboard.html modulepreload and not statically imported by the main entry; easily extensible to future tables.

Confidence Score: 4/5

Safe to merge — the deferral recipe is complete and all four required parts work together correctly, with a passing regression test guarding against re-eagerization.

The implementation is sound and the PR description accounts for the key risks. The two minor concerns are: the .catch() in applyTechHubActivities discards all errors with no logging, making future bugs in getTopActiveHubs or setActivities invisible; and the static-import guard regex uses [A-Za-z0-9_]+ for the hash, which excludes - and could silently miss a regression if Vite emits a base64url hash containing a hyphen.

src/app/data-loader.ts (silent catch) and tests/dashboard-eager-chunks.test.mjs (hash regex in the static-import guard) are the two spots worth a second look before merging.

Important Files Changed

Filename Overview
src/app/data-loader.ts Replaces the static getTopActiveHubs import with a new applyTechHubActivities() helper that dynamic-imports tech-activity only when the tech-hubs panel is mounted; correctly guards on panel presence and snapshots clusters. Silent .catch swallows all errors including runtime bugs.
src/config/index.ts Drops STARTUP_HUBS, ACCELERATORS, TECH_HQS, CLOUD_REGIONS value re-exports from the eager barrel while retaining the type-only re-exports (erased at runtime). Core change that severs the static import chain from the barrel to the 62 KB data chunk.
src/components/Map.ts Moves tech-geo imports from the @/config barrel to the direct @/config/tech-geo path; already lazy-loaded so the import is now isolated to its own chunk boundary.
src/components/DeckGLMap.ts Same barrel-to-direct-import migration as Map.ts; DeckGLMap is also a lazy chunk so tech-geo data loads only on demand.
src/e2e/map-harness.ts E2E harness updated to mirror the barrel-to-direct-import change; consumed only by Playwright specs, not the production bundle.
vite.config.ts Adds a manualChunks rule isolating src/config/tech-geo.ts into the tech-geo-data chunk; correctly placed and commented with the rationale.
tests/dashboard-eager-chunks.test.mjs New dist-gated regression guard asserting chunk existence, absence from HTML modulepreload, and no static import by main entry. The static-import regex uses [A-Za-z0-9_]+ excluding -, potentially causing a silent false-negative on base64url hashes.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    subgraph Before["Before - eager path"]
        A1[main entry] -->|static| B1[data-loader.ts]
        B1 -->|static| C1[tech-activity.ts]
        C1 --> D1[tech-hub-index.ts]
        D1 --> E1["tech-geo.ts ~62 KB eager"]
        A1 -->|config barrel| F1["@/config barrel"]
        F1 -->|static| E1
    end
    subgraph After["After - lazy path"]
        A2[main entry] -->|static| B2[data-loader.ts]
        B2 -->|"dynamic import()"| C2[tech-activity.ts]
        C2 --> D2[tech-hub-index.ts]
        D2 --> E2["tech-geo-data chunk ~62 KB lazy"]
        A2 -->|"type-only erased"| F2["@/config barrel"]
        G2["Map.ts lazy chunk"] -->|direct static| E2
        H2["DeckGLMap.ts lazy chunk"] -->|direct static| E2
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    subgraph Before["Before - eager path"]
        A1[main entry] -->|static| B1[data-loader.ts]
        B1 -->|static| C1[tech-activity.ts]
        C1 --> D1[tech-hub-index.ts]
        D1 --> E1["tech-geo.ts ~62 KB eager"]
        A1 -->|config barrel| F1["@/config barrel"]
        F1 -->|static| E1
    end
    subgraph After["After - lazy path"]
        A2[main entry] -->|static| B2[data-loader.ts]
        B2 -->|"dynamic import()"| C2[tech-activity.ts]
        C2 --> D2[tech-hub-index.ts]
        D2 --> E2["tech-geo-data chunk ~62 KB lazy"]
        A2 -->|"type-only erased"| F2["@/config barrel"]
        G2["Map.ts lazy chunk"] -->|direct static| E2
        H2["DeckGLMap.ts lazy chunk"] -->|direct static| E2
    end
Loading

Reviews (1): Last reviewed commit: "perf(dashboard): defer the tech-geo conf..." | Re-trigger Greptile

Comment thread src/app/data-loader.ts
Comment on lines +3304 to +3306
void import('@/services/tech-activity')
.then(({ getTopActiveHubs }) => techHubsPanel.setActivities(getTopActiveHubs(clusters)))
.catch(() => { /* non-critical */ });

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.

P2 Silent catch swallows runtime errors

The .catch(() => { /* non-critical */ }) suppresses all errors, including programming errors thrown by getTopActiveHubs or setActivities after a future refactor. A chunk-load failure (the intended silent-failure case) is indistinguishable from a logic bug. Adding a console.warn or development-mode log would preserve the graceful-degradation intent while keeping runtime errors observable.

Suggested change
void import('@/services/tech-activity')
.then(({ getTopActiveHubs }) => techHubsPanel.setActivities(getTopActiveHubs(clusters)))
.catch(() => { /* non-critical */ });
void import('@/services/tech-activity')
.then(({ getTopActiveHubs }) => techHubsPanel.setActivities(getTopActiveHubs(clusters)))
.catch((err) => { console.warn('[DataLoader] tech-hub activities skipped:', err); });

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread tests/dashboard-eager-chunks.test.mjs Outdated
// The bare filename also appears in Vite's dynamic-import preload manifest
// (`"assets/<chunk>-hash.js"` inside an array) — that's expected for a lazy
// chunk and must NOT fail the guard, so match the static-import form only.
const staticImportRe = new RegExp(`(?:from|import)"\\./${chunk}-[A-Za-z0-9_]+\\.js"`);

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.

P2 Hash regex excludes hyphen, risking a false-negative guard

The regex character class [A-Za-z0-9_]+ does not include -. Vite can produce base64url chunk hashes containing hyphens (e.g. tech-geo-data-BV7zA-9Q.js), in which case the regex would not match the filename and the "not statically imported by main" assertion would silently pass even if the chunk were re-added to the static graph. Widening to [A-Za-z0-9_-]+ closes the gap without affecting correctness on hex-only hashes.

Suggested change
const staticImportRe = new RegExp(`(?:from|import)"\\./${chunk}-[A-Za-z0-9_]+\\.js"`);
const staticImportRe = new RegExp(`(?:from|import)"\\./${chunk}-[A-Za-z0-9_-]+\\.js"`);

…ath (#4404)

Round 2, table #2. The ~14KB airports table (MONITORED_AIRPORTS/FAA_AIRPORTS)
was pulled eager only by the @/config barrel (config/index.ts named re-export +
variants/full.ts `export *`); its sole consumer, the lazy AviationCommandBar,
already imports directly from '@/config/airports'. No eager service chain (unlike
tech-geo), so no data-loader change needed.

- config/index.ts + variants/full.ts: drop the airports barrel re-exports.
- vite.config.ts: manualChunks rule isolating 'airports-data'.
- tests/dashboard-eager-chunks.test.mjs: add airports-data to the guard.

Verified: tsc clean; build 0 circular-chunk warnings; airports-data (14KB) absent
from dashboard.html modulepreload + not statically imported by main; tech-geo
still deferred (no regression); 6/6 eager-chunk guard tests pass; runtime preview
WebGL map renders, no code console errors.

Claude-Session: https://claude.ai/code/session_015Ae5Xavw1ZV4GMCWEsgKwe
@koala73 koala73 changed the title perf(dashboard): defer the tech-geo config data table off the eager path (#4404) perf(dashboard): defer lazy-only config data tables off the eager path — tech-geo + airports (#4404) Jun 24, 2026
@koala73

koala73 commented Jun 24, 2026

Copy link
Copy Markdown
Owner Author

Added table #2: airports (~14 KB) to this PR — same recipe, low-risk (no eager service chain; AviationCommandBar already imports direct). Build-verified: airports-data out of dashboard.html modulepreload + not imported by main, tech-geo still deferred, 0 circular chunks, 6/6 eager-chunk guard tests, runtime map renders. ai-datacenters (86 KB) follows as a separate PR since it carries a behavioral country-intel → related-assets lazy-load. (#4404)

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant