@stnd/plugins
v0.2.0
Published
Standard-Garden/apps/standard/plugins/README.md # Standard Plugins
Readme
Standard-Garden/apps/standard/plugins/README.md
Standard Plugins
Standard is now a lean plugin loader. Each *.plugin.js exports a manifest the loader understands. This doc captures the schema and authoring guidance—no backward-compat shims.
Manifest Schema
A plugin file must be named index.plugin.js and export default { ... } with this shape:
// REQUIRED
id: string // unique plugin id
name: string // human-friendly label
description?: string // human-friendly description// Routes (Astro) routes?: Array<{ path: string // URL pattern (e.g., "/robots.txt") entrypoint: string // relative to plugin dir (e.g., "./route.js" or "./route.astro") }>
// Styles // - starts with "@" → imported as-is (package import) // - else resolved relative to plugin dir and injected via injectScript("page-ssr") styles?: string[]
// Scripts // - starts with "@" → imported as-is // - else resolved relative to plugin dir and injected on the client page scripts?: string[]
// Head entries // - string → imported like styles (SSR import) // - { inline: string } → injected as inline head script head?: Array<string | { inline: string }>
// Middleware // - string → entrypoint, order defaults to 0 // - { entrypoint: string; order?: number } middleware?: Array<string | { entrypoint: string; order?: number }>
// Astro integrations (passed through) integrations?: Array
// Actions (Astro Actions) // - string → path to file exporting actions object(s) actions?: string
// Dependencies // - List of plugin IDs that must be loaded before this plugin dependencies?: string[]
// Hooks // - Keys starting with "astro:" → Proxied to Astro integration hooks // - Other keys → Treated as Custom Application Hooks available via virtual:standard/hooks hooks?: { [hookName: string]: string }
// Components (UI Zones) // Map of zone → entries // Supports both 'components' and 'extensions' keys. // Entry can be: // - string → component path (relative to plugin dir) // - object → { component?: string; action?: string; meta?: object; trigger?: any } components?: { [zone: string]: Array< | string | { component?: string action?: string meta?: Record<string, any> trigger?: any }
} // Alias for components extensions?: Record<string, any[]>
// Notes: // - Anything outside routes/styles/scripts/head/middleware/integrations/components is passed to the runtime (minus those fields) and tagged with __importPath for server-side use. // - Bare names in pluginLoad are prefixed with "@stnd/astro/plugins/" automatically; package/external specifiers still resolve as given.
### Example: Custom Hooks & Components
Plugins can extend the application runtime or inject UI components into zones.
**Manifest (`my.plugin.js`):**
```javascript
export default {
id: "my-plugin",
name: "My Plugin",
// Custom Hook: will be available via `virtual:standard/hooks`
hooks: {
"app:db:init": "./hooks/db-init.js"
},
// UI Component: inject into the 'backpack' zone
// Can also use 'components' key
extensions: {
"backpack": [
{
component: "./components/MyWidget.svelte",
meta: { "client:load": true }
}
]
}
}Hook Handler (./hooks/db-init.js):
export default async function(dbInstance) {
console.log("Plugin initializing DB...", dbInstance);
// Perform custom logic, migration, etc.
}Consuming Hooks (Application Code):
import { runHook } from "virtual:standard/hooks";
// Trigger all registered "app:db:init" hooks
await runHook("app:db:init", myDatabase);Middleware
Plugin middlewares are native Astro Middlewares. They must follow the (context, next) signature and call next() to continue the chain.
Manifest (auth.plugin.js):
export default {
id: "auth-plugin",
middleware: ["./middleware.js"]
}Middleware (./middleware.js):
import { defineMiddleware } from "astro:middleware";
export const onRequest = defineMiddleware(async (context, next) => {
console.log("Plugin middleware running...");
// You can wrap next(), modify the response, etc.
return next();
});Authoring Guide
- Place plugins under
plugins/<name>/index.plugin.jsat the project root. - Keep logic inside the plugin;
.astrofiles should only consume model instances. - Prefer OKLCH and Standard tokens for styles; avoid one-off CSS unless you’re defining a temperament/theme.
- No backward compatibility—ship only the current shape.
Loader Behavior (Quick)
- Discovers
**/*.plugin.{js,ts}inpluginFolder(defaultsrc/plugins; apps can override). pluginLoadaccepts:- Bare names (auto-prefixed):
"design","themes","lab", etc. - Explicit specifiers:
"@vendor/some-plugin"or"./local/plugin.js".
- Bare names (auto-prefixed):
- Routes, styles, scripts, head, middleware are injected per manifest.
- Integrations are forwarded to Astro via
updateConfig. - UI extensions are exposed via
virtual:standard/ui. - Client payload strips routes/styles/scripts/head/middleware/integrations/extensions; keeps
__importPathfor server use.
Core Plugins (shipped)
design— base stylesheetthemes— temperament/theme stylesheetlab— StandardLab inspector bundlecontent— content routerobots— robots.txt routeheaders— headers routemanifest— web manifest routesitemap— sitemap generation
Usage in an App
standard({
pluginFolder: "src/spores", // optional override
pluginLoad: [
"design",
"themes",
"lab",
"content",
"robots",
"headers",
"manifest",
"sitemap",
],
});Philosophy
- Vertical slice: keep plugins self-contained.
- Zero shims: no legacy flags (
design,theme,lab)—everything is a plugin. - Performance and clarity: small, explicit manifests; no hidden magic.
