JS modules
Import the site’s build data straight into your bundle. Rolldown inlines it, so there is no runtime fetch and nothing to keep in sync by hand.
import site from "baudelaire:site";
import { url } from "baudelaire:assets";
document.title = site.title;
loadLogo(url("/assets/logo.png")); // the fingerprinted name
These are served to the bundler, so they need assets { bundle #true } and a binary with the js feature. See the asset pipeline. They are the JavaScript counterpart of the @baudelaire/* Typst modules, built from the same data.
TypeScript
Nothing on disk holds these modules, so an editor reads every import of one as unknown. Every build writes the declarations out, and baudelaire mirror does it on demand for a checkout that has not been built:
baudelaire mirror
Either way the file is .baudelaire/generated/baudelaire.d.ts. Put it on your include list:
{ "include": ["assets/**/*.ts", ".baudelaire/generated/baudelaire.d.ts"] }
site, config and i18n are typed from your own config, so config.api is the type your client { } block gives it, not unknown. A build never reads the file back, so a stale copy misleads an editor and changes no page.
The modules
| Specifier | Exports | Is |
|---|---|---|
baudelaire:site |
default + named | Site identity and baudelaire’s version. |
baudelaire:config |
default + named | Your own constants from the client { } block. |
baudelaire:assets |
default, url |
Request path to fingerprinted URL. |
baudelaire:pages |
default | Every authored page as a row. |
baudelaire:sections |
default | Section trees keyed by language code. |
baudelaire:taxonomies |
default | Each taxonomy’s terms mapped to pages. |
baudelaire:feed |
default | The most recent dated pages. |
baudelaire:i18n |
languages, strings |
Declared languages and their UI strings. |
baudelaire:search |
createSearch, mountSearch |
The search client. |
baudelaire:spa |
mountSpa, mountRouter |
The client-side navigation runtime. |
Where a module’s data is an object, every key that is a valid, non-reserved JavaScript identifier is also a named export, so import { title } from "baudelaire:site" pulls in that one and tree-shakes the rest. Keys that aren’t legal identifiers stay reachable through the default export.
site
The same values templates read from sys.inputs.baudelaire. See build metadata.
import { version, title, url, lang, author, languages } from "baudelaire:site";
title, url and author are null when the config never set them. languages is { code, name } objects, default first, and is empty unless i18n is on.
config
Build-time constants from the client { } block. Any KDL scalar works.
client {
analytics "https://plausible.io"
revalidate 3600
beta #false
}
import { analytics, revalidate } from "baudelaire:config";
Use it for settings you would otherwise hard-code. The same constants reach templates at sys.inputs.baudelaire.client, so server-side Typst and client-side JavaScript read one source.
assets
import assets, { url } from "baudelaire:assets";
url("/assets/logo.png"); // "/assets/logo.a1b2c3d4.png"
url("/nope.png"); // "/nope.png"
url(path) returns the fingerprinted name, or the path unchanged when it is not a known asset. The default export is the raw map.
NOTE
This sees every image, stylesheet, and copied asset, because scripts are bundled last, once the fingerprint map is done. One bundle cannot see another bundle’s hashed name.pages
An array of rows, one per authored page, the same shape a generated listing hands its template and the Typst @baudelaire/pages catalogue serves: url, label, collection, lang, date, display, note, description, image, alt, author, taxonomies, extra. Generated listings and the not-found page are excluded.
import pages from "baudelaire:pages";
const posts = pages.filter((p) => p.collection === "posts" && p.lang === "en");
sections
The section trees, keyed by language code, each node { id, pages: [{ url, title }], children: [...] } per content directory. Exactly what a page of that language gets as page.sections.
import sections from "baudelaire:sections";
for (const node of sections.fr) walk(node);
children nests subdirectories, so recurse it for the whole tree.
taxonomies
Each taxonomy’s terms mapped to the pages that carry them, for tag filtering or a term cloud.
import taxonomies from "baudelaire:taxonomies";
taxonomies.tags.rust; // [{ url, title, lang }, ...]
feed
The most recent dated pages as { url, title, lang, date }, newest first, capped at the feed’s configured limit. Every language is included, each row tagged with its own, so one bundle serves the whole site.
i18n
import { languages, strings } from "baudelaire:i18n";
strings.fr.more; // the fr UI string
languages is { code, name, dir } objects, default first; dir is ltr unless the config says otherwise. strings is keyed by code. For a language switcher and localized UI text. See multiple languages.
search
The palette client, minus the auto-mount the standalone file carries: you decide when it mounts and against what.
import { mountSearch } from "baudelaire:search";
mountSearch({ placeholder: "Search the docs" });
createSearch(url) is the lower-level half: it fetches an index and resolves to a search(query, { limit }) function. One specifier serves either index shape and every language, picking the index by the page’s own <html lang>. See search.
spa
import { mountSpa } from "baudelaire:spa";
mountSpa({ select: "#content" });
Served whether or not navigation { spa { } } is set: importing it is itself the opt-in, and the block’s fields are only the defaults mountSpa() starts from. mountRouter is the core underneath, for a site driving navigation itself. See SPA and single-file export.