Getting started
Silgi is a full-stack web framework for Bun. This page creates an app on the html library, runs it, and builds it.
Before the first release
The packages are not on npm yet, so bunx silgi init and the bun install of a starter do not resolve. Until they are published, clone the repository, link silgi and the adapter of your library, create the app with the cloned CLI, and link the packages into it. The starter lists silgi with a version range, and bun link --save keeps an existing range, so use bun add with link: specifiers: it rewrites both entries and installs. The name silgi on npm belongs to an unrelated package, not to this project, so never install it from the registry. The sample uses react; for another library link packages/adapter-<library>, pass --framework <library>, and add @silgi/<library>@link:@silgi/<library>. Skip the bunx command in the next section: the app already exists, and the packages are installed.
git clone https://github.com/productdevbook/cati.git
cd cati && bun install
(cd packages/silgi && bun link)
(cd packages/adapter-react && bun link)
cd ..
bun cati/packages/silgi/bin/silgi.ts init my-app --framework react
cd my-app
bun add silgi@link:silgi @silgi/react@link:@silgi/react
This section goes when the packages are published.
Create an app
silgi init writes a starter app into a directory. Run it with bunx, which fetches the silgi package. Pass the library with --framework: one of react, vue, preact, svelte, solid or html. The command refuses a directory that is not empty unless you add --force. With --force it overwrites the starter's files, including package.json, and replaces {{name}} only in the files it writes from the template; your other files are left alone. It refuses to write through a symbolic link.
bunx silgi init my-app --framework html
cd my-app
bun install
The starter has a package.json with the scripts dev, build, preview, typecheck (silgi typecheck: it writes the types, then runs tsc, vue-tsc or svelte-check from the app's own node_modules, so .vue and .svelte files are checked, with TypeScript 6 as the typescript of those two starters) and postinstall (silgi typegen), a tsconfig.json that references the four generated contexts (see Modules), a silgi.config.ts, and the app/ and server/ folders described in Directory structure.
Configure it
silgi.config.ts default-exports the config. The only key the starter sets is the library:
import { defineConfig } from "silgi";
export default defineConfig({ framework: "html" });
Run the dev server
silgi dev starts the dev server with hot reload on http://localhost:3000. Use --port and --host to change them.
bun run dev
- When the default port is busy, the server takes the next free one, up to 10 ports on, and logs
Port 3000 is in use, using 3001. A port you chose (--port,PORTordevServer.port) is never replaced: if it is busy the command exits with code 1 and printsPort <n> is in use; pick another with --port. --hostwith no value listens on0.0.0.0, as--host 0.0.0.0does. TheLocal:line then showslocalhost, and oneNetwork:line per network interface follows it.silgi --helplists the commands andsilgi --versionprints the version ofsilgi.silgi devrestarts whensilgi.config.*,bunfig.tomlor a.envfile changes. If the new config is broken, it logs the error and waits; the server starts again at the next save of one of those files.
To serve the dev server over HTTPS, set devServer.https to a key and a certificate. Each of key, cert and ca is PEM text or a path to a PEM file, resolved from the app root; passphrase unlocks an encrypted key. silgi dev then listens with TLS and prints a https:// address. Browsers warn about a self-signed certificate.
import { defineConfig } from "silgi";
export default defineConfig({
framework: "react",
devServer: {
port: 3000,
https: { key: "./certs/key.pem", cert: "./certs/cert.pem" },
},
});
Write a page
Every file under app/pages/ is a page, and its path is its URL. With the html library a page is a function that returns a string:
import { useHead, useRoute } from "@silgi/html";
const escape = (value: unknown) => String(value).replace(/[&<>"']/g, (char) => `&#${char.charCodeAt(0)};`);
export default function Home(): string {
const route = useRoute();
useHead({ title: "Home" });
return `<main>
<h1>My app</h1>
<p>Route: ${escape(route.path)}</p>
</main>`;
}
Add a handler
Server code is a handler: a typed function in server/ that pages call and, with a route, a URL too. The starter has one in server/hello.ts. A handler is reachable only when server/index.ts exports it, so the starter's server/index.ts holds export { hello } from "./hello.ts";.
import { defineHandler } from "silgi/server";
export const hello = defineHandler({
route: "/api/hello",
method: "GET",
handler: () => ({ message: "Hello" }),
});
GET /api/hello answers {"message":"Hello"}. A page reads a handler by importing it from #server; Data shows the calls and Server the file that makes a handler reachable. A page that needs a value before it renders adds a load file next to it, here blog/[slug].load.ts for blog/[slug].ts:
import type { Load } from "silgi/app";
export const load = (({ params }) => ({ title: `Post ${params.slug}` })) satisfies Load;
Build and preview
silgi build writes the production build into .output/. The default bun build runs on Bun (bun .output/server/index.mjs); build with --preset node to run it with Node. silgi preview serves that build: it runs the server of a bun or node build and serves the files of a static build. For cloudflare, vercel and netlify it prints the platform's own command instead.
bun run build
bun run preview
Generate a static site
silgi generate builds with the static preset and prerenders every route it can reach by following links from /. The result is .output/public/, which any static host can serve. The starter has no generate script, so run it with bunx, which uses the app's own silgi.
bunx silgi generate
Commands
Every command takes the app's directory as its first argument, . by default. --debug prints the stack of a failing command and writes the build's virtual modules to .silgi/virtual/.
| Command | Usage |
|---|---|
init | silgi init [dir] --framework <id> [--force] |
dev | silgi dev [root] [--port <n>] [--host <name>] [--dotenv <file>] [--mode <name>] |
build | silgi build [root] [--preset <id>] [--dotenv <file>] [--mode <name>] [--analyze] |
generate | silgi generate [root] [--preset <id>] [--dotenv <file>] [--mode <name>] [--analyze] |
preview | silgi preview [root] [--port <n>] [--host <name>] [--dotenv <file>] [--mode <name>] |
typegen | silgi typegen [root] [--mode <name>] |
info | silgi info [root] |
--presetpicks the deploy target; see Deploy.--dotenv <file>loads environment variables from a file, resolved from the app root. It only fills variables that are not set yet, so it does not override.env,.env.<mode>,.env.localor the shell.--mode <name>(letters, digits,-and_) setsimport.meta.env.MODEand reads<root>/.env.<name>and then<root>/.env.<name>.localafter Bun's own.env,.env.<NODE_ENV>and.env.local: they replace a value from those files and never a value set in the shell, and.env.<name>.localwins over.env.<name>. Without--mode,MODEisdevelopmentfordevandproductionotherwise, and no mode file is read.NODE_ENVis not changed by--mode.--analyzeprints the largest modules of each client chunk after the build.typegenwrites.silgi/tsconfig.jsonand.silgi/types/without building.infoprints the Silgi and Bun versions, the package manager (from the nearest lockfile), the OS, the library, the preset, the modules and the resolved directories.- A command that takes a root reads that root's
.env,.env.<mode>and.env.local, wherever you run it from:silgi build apps/webrun in a monorepo usesapps/web/.env. The mode isdevelopmentfordevandproductionforbuild,generateandpreview, sosilgi devreads.env.developmentand never.env.production. A variable that is already set, in the shell for example, wins over the files. buildandgeneratelist the output files with their size and gzip size. Above 50 files, the files ofpublic/are summed per directory (public/blog/ (3000 files));--debuglists every file.- A command that fails exits with code 1 and prints the message. A build error names the file, the line and the column.
Deploy targets other than a static host are in Deploy. Routing and Data are the next pages to read.