Project structure
The layout of a Norns app and which SvelteKit files can be written in Civet.
A Norns app is an ordinary SvelteKit source tree. There is no generated tree and no spec directory.
src/
hooks.server.c boots the runtime, eager-loads feature modules
app.html, app.css html shell, global styles
routes/ SvelteKit routes
+layout.n, +layout.c
+page.n, +page.server.c
api/<name>/+server.c
lib/
components/ components auto-imported by name (<Header>, <Card>, ...)
norns/
<feature>/ feature folders, see next page
server/{module,repo,service,public}.c
shared/schema.c
migrations/
<feature>/<timestamp>_<slug>.sql
svelte.config.js, vite.config.js
wrangler.toml optional, Cloudflare deploys#File types
| Extension | What it is | Compiled by |
|---|---|---|
.n |
A Svelte component whose template is Pug and whose <script> is Civet. No lang attributes needed. |
norns-core preprocessor |
.c |
A Civet module. .civet is the same thing with a longer name. |
nornsCivetPlugin() |
.svelte, .js, .ts |
Plain Svelte and JavaScript, still allowed anywhere. | Vite / Svelte as usual |
#SvelteKit special files in Civet
nornsConfig() sets kit.moduleExtensions to ['.js', '.ts', '.c', '.civet'], so every SvelteKit module file has a Civet spelling: +page.server.c, +layout.c, +server.c, +page.c. Components use extensions: ['.svelte', '.n'], so +page.n, +layout.n and +error.n work.
Hooks are the one exception SvelteKit does not discover through moduleExtensions. nornsConfig() looks for src/hooks.server.c / .civet (and the client and universal counterparts) and sets kit.files.hooks explicitly when one exists. Name the file that way and nothing else is required.
#Where feature code goes
The starter and the demo keep features under src/lib/norns/<feature>/ and boot them with:
features := import.meta.glob './lib/norns/*/server/module.c', { eager: true }The glob in hooks.server.c is what decides; src/lib/<feature>/ works just as well if you change it. Keep migrations outside src/ so the bundler never sees them.