v0.5.0
Minor Changes
-
onConfigapplies the return value to config for a mutable allowlist:build.output,build.clean,structure.*,passthrough,plugins.workers, andplugins.timeout. Fields outside the allowlist are ignored without warning. Returning a non-object is a build error.alloy.hook("onConfig", {}, (config) => { config.build.output = "dist"; config.structure.content = "pages"; return config; });Multiple
onConfighooks chain in priority order. Alloy validates path fields before applying any of them. Absolute paths,..traversals above the project root, and.are rejected. Passthrough entries follow the same rules, withto: "."allowed (root of the output directory).Previously the return value was discarded.
-
onAssetProcessfires once per asset file with{path, content}instead of once per build with directory paths. Return{content: "..."}to replace the file in output. Returnnullor omit thecontentkey to keep the original. Multiple hooks chain.alloy.hook("onAssetProcess", {}, (asset) => { if (asset.path.endsWith('.css')) { return { content: minifyCSS(asset.content) }; } });Previously the hook received
{assetsDir, outputDir}and discarded the return value. -
onBeforeValidationreceives{outputPaths: [...]}and accepts{addOutputs: {path: source}}to register additional output paths before conflict detection.onAfterValidationreceives{outputPaths: [...], cascade: {...}}and accepts{cascade: {...}}merged into site data for templates. Both reject unrecognized return keys.Previously both hooks fired before content discovery with a stub payload and threw away the return value.
-
onPagesReadyaccepts a second return shape,{addPages: [...]}, for injecting virtual pages without round-tripping the full pages array through the plugin bridge. Withpages: false, Alloy skips serialization of existing pages.alloy.hook("onPagesReady", { data: ["elements"], pages: false }, (payload) => { return { addPages: payload.siteData.elements.map(el => ({ path: `demos/${el.slug}.md`, url: `/demos/${el.slug}/`, frontMatter: { title: el.name, layout: "demo" }, content: `# ${el.name}` })) }; });The
pagesandaddPagesreturn shapes are mutually exclusive. Returning both is a build error. Returning an unrecognized key errors instead of dropping pages without warning. -
onPagesReadyvirtual pages accept adependenciesarray of project-root-relative file paths. Duringalloy dev, Alloy re-renders only the virtual pages whose listed files changed.dependencies: ['elements/button/demo/index.html']dependencies: []skips re-rendering (no file deps to invalidate). Omittingdependenciesre-renders on every rebuild (safe default, pre-0.5.0 behavior). -
alloy.source(name, fn)registers a data source handler in Node plugins. Configuretype: "plugin"insources:to route data acquisition through the handler instead of the built-inrestorgraphqlfetchers.alloy.source("cms-posts", async () => { const resp = await fetch("https://api.example.com/posts", { headers: { Authorization: `Bearer ${process.env.TOKEN}` } }); return resp.json(); });Alloy caches results to
.alloy/fetch-cache/with the same TTL and--refetchsemantics as built-in sources. -
Alloy loads data files from subdirectories of
data/into nested template namespaces.data/nav/main.yamlbecomessite.data.nav.main. Nesting goes to any depth. A file and non-empty directory sharing the same stem (e.g.,nav.yamlalongsidenav/) produces a build error. Alloy skips empty subdirectories.Previously, Alloy ignored subdirectories of
data/without warning. -
Go template block shortcodes use
{{% tag "args" %}}...{{% /tag %}}delimiters. A preprocessor runs after Goldmark and before Go template rendering, pairing tags and passing inner HTML to the shortcode callback. Nesting resolves innermost-first. Delimiters inside<pre>and<code>are literal text.Previously, Go template block shortcodes received empty
contentbecause the engine hard-coded it to"". -
Liquid shortcode arguments resolve variables from the template context. Unquoted arguments like
{% youtube page.videoId %}look uppage.videoIdand pass the resolved value to the shortcode callback. Quoted arguments remain literal strings. Unresolved variables fall back to their token string. Shortcodes returning""produce no output instead of emitting a placeholder element. -
alloy version --checkqueries GitHub Releases for newer versions. SetupdateCheck: truein config for a one-line notification whenalloy devoralloy servestarts. The check runs in the background, caches for 24 hours, and defaults to off.
Patch Changes
- Fix
onContentLoadeddroppinghtmlmutations without warning. Alloy now appliespage.htmlchanges viaSetRenderedBody. BothhtmlandfrontMattermutations work independently or together. - Fix omitted
pagesscope defaulting to “all pages” instead of “none.” Hooks registered with{}now skip page serialization. Plugins that want pages must declarepages: true. - Fix batch hooks firing a spurious timeout warning with 0 items. Alloy skips the hook when scope filtering leaves no payloads.
- Fix
alloy devskipping virtual pages on incremental rebuilds. Plugin-generated pages re-render when source files change instead of serving stale content. - Fix Node bridge protocol corruption when plugins call
process.stdout.write(). The bridge captures the realprocess.stdout.writeat startup and redirects subsequent calls to stderr. The error diagnostic names stdout pollution as the cause instead of reporting a generic framing error. - Fix QuickJS runtime panic on build teardown when a plugin hook exceeds its timeout.
Close()waits for in-flight calls to finish before releasing the runtime.