perf(dashboard): defer lazy-only config data tables off the eager path — tech-geo + airports (#4404)#4407
Conversation
…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
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Greptile SummaryThis PR defers the
Confidence Score: 4/5Safe 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
Important Files Changed
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
%%{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
Reviews (1): Last reviewed commit: "perf(dashboard): defer the tech-geo conf..." | Re-trigger Greptile |
| void import('@/services/tech-activity') | ||
| .then(({ getTopActiveHubs }) => techHubsPanel.setActivities(getTopActiveHubs(clusters))) | ||
| .catch(() => { /* non-critical */ }); |
There was a problem hiding this comment.
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.
| 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!
| // 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"`); |
There was a problem hiding this comment.
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.
| 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
|
Added table #2: |
Summary
Round 2 of the
main.jsdiet (#4404), first data table. The ~62 KBtech-geoconfig 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@/configbarrel value re-export and bydata-loader → tech-activity → tech-hub-indexrunning in the data-load cycle — amanualChunksrule alone is a no-op.Recipe (all parts required — build-proven)
tech-geovalue re-export from the eager@/configbarrel (keep type re-exports; erased, no runtime edge).@/config/tech-geo(they pulled them via the eager barrel).tech-activityin a newapplyTechHubActivities()helper, guarded on the lazytech-hubspanel being mounted, so thetech-activity → tech-hub-index → tech-geochain no longer runs at boot. Degrades silently on load failure (non-critical panel data).manualChunksrule isolatingtech-geointo its owntech-geo-datachunk.tech-geo-datastays out ofdashboard.htmlmodulepreload and is not statically imported bymain(extensible to the remaining tables).Bundle impact (VITE_VARIANT=full)
main-*.jsrawmain-*.jsgziptech-geo-dataVerification
tsc --noEmitclean (incl.src/e2e); full build has 0 "Circular chunk" warnings (perf: split panel chunks by domain #4382 guard).tech-geo-dataabsent fromdist/dashboard.htmlmodulepreload and not statically imported bymain(only lazy chunks: deck-stack/Map/MapContainer/search-manager/tech-activity).maplibrecanvas, not the SVG fallback), zero console errors,tech-geoloads on demand with the map.map-renderer-deferraltests pass.Scope
First of the lazy-only tables in #4404. The same recipe extends to
airports(14 KB) andai-datacenters(86 KB, needs thecountry-intel → related-assetslazy-load);geo.ts/feeds.tsare genuinely boot-eager (separate issue);panel-storageis not deferrable.Fixes part of #4404.
https://claude.ai/code/session_015Ae5Xavw1ZV4GMCWEsgKwe