Vite Glob Bundle-Time Data Loading
meridian intermediate 5 min read
ELI5
Instead of opening a file drawer every time someone asks for a recipe, the kitchen tapes every recipe to the wall before the restaurant opens. Once service starts, cooks read the wall — they never touch the drawer.
Technical Deep Dive
src/lib/units.ts and src/lib/doctrines.ts use Vite’s import.meta.glob to bundle workspace YAML at build time. There is no runtime filesystem access — important because the Vercel adapter runs in a server runtime where the source tree is not present.
Glob Patterns Used
const unitYamlFiles = import.meta.glob<{ default: string }>( "../../../../packages/units/**/*.yaml", { query: "?raw", eager: true });const councilYamlFiles = import.meta.glob<{ default: string }>( "../../../../councils/*/council.yaml", { query: "?raw", eager: true });const spritePngFiles = import.meta.glob<{ default: string }>( "../../../../councils/*/sprites/*.png", { query: "?url", eager: true });const spriteSvgFiles = import.meta.glob<{ default: string }>( "../../../../councils/*/sprites/*.svg", { query: "?raw", eager: true });Build-Time Wiring
flowchart LR SRC[packages/units/**/*.yaml<br/>councils/**/sprites/*<br/>.opencode/doctrines/*.doctrine.md] --> VITE[Vite import.meta.glob<br/>eager:true] VITE -->|?raw| TEXT[bundled string modules] VITE -->|?url| ASSET[fingerprinted asset URLs] TEXT --> PARSE[parseUnit / parseCouncil / parseDoctrineFile] ASSET --> SPRITE[ResolvedVisual content] PARSE --> LOADER[loadAllUnits / loadAllCouncils] SPRITE --> LOADER LOADER --> PAGE[Astro pages and API endpoints]Query Flag Semantics
| Flag | Result |
|---|---|
?raw | The file’s text content is inlined as the module default export |
?url | A fingerprinted asset URL string is the default export |
PNGs use ?url because they should be served as binary assets through Astro’s asset pipeline. SVGs use ?raw because ResolvedVisual.content carries the SVG markup itself for inlining into the orbital renderer.
Eager vs Lazy
eager: true resolves every match at module-import time, producing synchronous access. Lazy globs return functions returning Promises — fine for code-splitting routes, wrong for data that every page depends on. The orchestrator’s metric and graph builders treat loadAllUnits() as synchronous; lazy loading would cascade await through buildOrbitalData, buildGraphData, computeProtocolMetrics.
Key Terms
import.meta.glob→ Vite primitive for compile-time directory matching.?raw→ Vite query suffix returning the raw text of the matched file.?url→ Vite query suffix returning a public asset URL (with content hashing).- Workspace YAML → Source of truth for units, councils, doctrines; lives outside
apps/meridian/src.
Q&A
Q: What if a new unit YAML is added after astro build runs?
A: It is invisible until the next build. Bundling is at build time; Vercel deploys are immutable, so a redeploy is required.
Q: Why not just fs.readdirSync at module init?
A: Astro on Vercel runs server functions where the workspace tree is not present at runtime. The function bundle only contains what Vite included — if it was not globbed, it does not exist on the server.
Q: Why are large external deps like yjs, zod, blake3-wasm listed as external?
A: Same file (astro.config.mjs) — keeping them external avoids bundling them twice and lets the runtime resolve the npm copy. Globbing covers source data, externalisation covers npm dependencies.
Examples
Adding a new council:
- Drop
councils/<name>/council.yamland any sprites into the workspace. - The next
astro buildre-evaluates the globs; the council appears inloadAllCouncils(), inbuildOrbitalData, and on/councils/<name>/. - No code change in meridian itself — the glob pattern already matched the new path.
neighbors on the map
- CLI Command Surface wiring STRATT into a CI pipeline
- LORE+CAIRNET Shared Graph Primitive understanding how LORE and CAIRNET share the graph component