Blueprint & build
The build & targets
Between your source and a running application sits the CLI. It discovers your modules, builds the Blueprint, generates the entry for the chosen target, and bundles it. You configure the little that varies in one file; the rest is derived.
#What a build does
On stone build the CLI scans app/, letting your decorators and define* modules register onto the Blueprint, generates the target entry into .stone/, and produces the output in dist/. There is no registry to maintain: add a decorated file and it is in; delete it and it is gone.
stone dev # dev server with hot reload
stone build # production build (rendering deduced from adapters)
stone build --ssg # same, pre-rendered to static HTML
stone preview # serve the production build
stone export # emit static output (SSG)#A target is registered, not hardcoded
The CLI knows how to run commands. It does not know what a React application is, nor a native one. Each is a target, declared on the Blueprint under stone.builder.builders.<name> by the package that is qualified to answer: how views become an application is the renderer's business, and it carries its own build to prove it.
| Target | Type | Description |
|---|---|---|
| server | @stone-js/cli | A backend service, bundled with Rollup. The CLI's own, declared through the same public key as every other, so a first-party target has no back door. |
| react | @stone-js/use-react | CSR, SSR and SSG, bundled with Vite. Auto-discovered from the renderer, so installing it is all a React project does. |
| native | @stone-js/use-react-native | Collects your modules, then hands the bundling to Expo and Metro. |
The consequence is worth stating plainly: a project that renders nothing installs no bundler. A backend service does not carry Vite, and a native application does not carry either. And adding a renderer to the ecosystem does not mean editing the CLI, which is what a build plugin is for.
The tool that runs commands does not get to know what a view is.
#Zero-config by default
There is nothing you must configure. The rendering strategy is deduced from the adapters you stacked, and the routes to pre-render are derived from the pages your app already declares. stone.config.mjs exists only for when you want to pin something.
import { defineBuilderConfig } from '@stone-js/cli'
// Zero-config: rendering is deduced from adapters, SSG is a build flag
// (stone build --ssg), and pre-rendered routes come from your own pages.
export default defineConfig({})The principle
A framework that already knows your adapters and your routes should not make you restate them. Defaults are deduced from what the app declares; configuration is a choice, never a tax.
In Stone.js
Rendering follows the adapters: a browser adapter alone is a SPA; a backend adapter alone is SOR (server-only rendering); both give SSR, and --ssg pre-renders that same SSR at build time. The pre-render set is collected from your scanned page routes, so adding a page adds a static file, with nothing to keep in sync.
#Pinning it, if you want to
Every option stays reachable. Pin the rendering mode, add extra static paths (e.g. expanded parameterized routes), or tune the bundlers, all optional.
export default defineBuilderConfig({
builder: {
target: 'react', // 'server' | 'react' | 'native' | yours (usually deduced)
rendering: 'ssg', // pin instead of using the --ssg flag
ssg: { routes: ['/sitemap'] } // ADDED to the auto-derived routes
}
})#Config reference
| Setting | Type | Default | Description |
|---|---|---|---|
| builder.target | string | · | Which registered target builds this application: `server`, `react`, `native`, or one a package of yours registered. Usually deduced from what the app enabled. |
| builder.rendering | 'csr' | 'ssr' | 'ssg' | · | Pin the rendering mode. Deduced from adapters when omitted. |
| builder.ssg.routes | string[] | · | Extra static paths, ADDED to the routes auto-derived from your pages. |
| builder.lazy | boolean | true | Per-route code splitting (and the scanned routes that feed SSG). |
| builder.imperative | boolean | · | Favour the imperative (define*) programming style. |
| builder.dotenv | object | · | How .env files are loaded. |
| builder.input | { app, views, mainCSS } | · | Module-discovery globs and the auto-linked entry stylesheet. |
| builder.browser.excludedModules | string[] | · | Modules to keep out of the browser build (server-only code). |
| public | string | 'public' | A directory copied verbatim to the build root. |
| output | string | · | The build output path. |
| builder.vite / builder.rollup | passthrough | · | Escape hatches to the underlying Vite (frontend) and Rollup (service) configs. |
Two escape hatches matter when you outgrow the defaults: builder.vite and builder.rollup pass options straight to the bundlers, and builder.browser.excludedModules keeps server-only code (a database driver, a secret) out of the client bundle.
#The .stone directory
.stone/ is generated: the entry that wires your discovered modules for the chosen target, plus intermediate artifacts. It is derived output, regenerated on every build. Never edit it or commit it; deleting it is safe.