Macros
The import.meta.og surface
One compile-time namespace for the things ogygia can settle at build time. Each call looks like runtime code, but it runs during the build, can see the module graph, and inlines its result. This page covers the rules they share, and each macro has its own page.
Some of what an app does never changes between requests: which markdown files exist, how a class crosses an island boundary, what a code sample looks like once highlighted, or the nav tree computed from a corpus. That work belongs in the build rather than in every request, and import.meta.og.* is where ogygia puts it.
Everything under import.meta.og is a compile-time construct. It reads like a normal call, but the ogygia Vite plugin rewrites it at build time, before your code runs, and there is no import.meta.og object at runtime. If a construct ever reached the browser without being rewritten, it would throw on its first line rather than half-work.
The family
| Construct | Turns into |
|---|---|
loader.markdown/folder/json/git | a content source for content({ loader }) |
code(source, lang, meta?) | a highlighted snippet, baked to a static region |
md(text) | a Markdown string, rendered to a static region |
regions(glob) | a block registry from a folder |
asRegion(Comp, options) | any imported component (named / barrel) marked a region |
wire({ encode, decode }) | the transportable-codec key on a class |
bake(fn) | the result of running fn at build, inlined |
Why it looks like import.meta.glob
These constructs live on import.meta because they behave like Vite’s own import.meta.glob, which is a call that never runs and that the bundler resolves at build time. Putting them there carries over the intuitions people already have for import.meta.glob: the arguments are literals, you don’t alias the call or pass it around, and the build is what answers. It also gives you a single namespace and a single autocomplete entry, and because ogygia owns the namespace, nothing else can collide with it.
The test for whether something belongs here is literal inputs and a result the build can determine. A runtime wrapper around import.meta.glob is the pattern these constructs replace.
What they share
- Literal arguments. A glob, a language or a repo spec is a plain string that the build can read. A template literal with
${…}interpolation is a runtime value, so it is a build error that names the offending expression. - Detection by parsing. A construct is found by parsing the code rather than by searching its text, so the same words inside a comment or a string are never mistaken for a call.
- Errors at build time. Misuse fails the build with an
[ogygia]message that names the file, the line and the fix, rather than silently doing nothing or surprising you at runtime. - No runtime cost. The rewrite happens once, at build time, and what ships is only the result: a source, a region, a registry, a codec key or a constant.
Each construct has its own page with its full contract. If you have content, start with loaders; if you share state across islands, start with wire; and for running something at build and shipping only the answer, see bake.
Each macro above respects one boundary, and the constraints describe the rules.