Project Structure
Pulse enforces a single convention: one spec per page, one directory per concern. The structure is not a recommendation — it is how the framework discovers and registers your pages. There is nothing to configure because there is nothing to decide.
Layout
my-app/
├── package.json
├── public/ # static assets served directly
│ ├── app.css
│ └── dist/ # generated bundles (pulse build)
│ ├── manifest.json
│ ├── runtime-[hash].js
│ └── [name].boot-[hash].js
└── src/
├── pages/ # one file per page — auto-discovered
│ ├── home.js
│ ├── about.js
│ └── contact.js
├── lib/ # shared helpers
│ └── db.js
└── components/ # shared view fragments (optional)
└── card.js
src/pages/
Each file in src/pages/ exports a single spec as the default export. pulse dev auto-discovers every file in this directory and registers it as a route. There is no route registry to maintain — the file is the registration.
about.js → /about). For dynamic segments, route is set explicitly in the spec.public/
Static files served directly — CSS, fonts, images. Referenced via meta.styles in the spec. The dist/ subdirectory is generated by pulse build. Its contents are content-hashed and must not be edited manually.
| Path | Purpose |
|---|---|
public/app.css | Your global stylesheet — reference it in spec meta.styles |
public/dist/ | Generated bundles from npm run build — do not edit manually |
public/dist/manifest.json | Maps spec hydrate paths to hashed bundle filenames |
src/lib/
Shared helpers used across multiple pages — database clients, API wrappers, utility functions. Imported directly into specs. Plain JavaScript modules with no framework coupling — they work identically in tests, scripts, or other contexts.
src/components/ (optional)
Reusable view fragments. Since views are just functions that return HTML strings, a component is simply a function:
// src/components/card.js
export function card({ title, body }) {
return `
<div class="card">
<h2>${title}</h2>
<p>${body}</p>
</div>
`
}
// src/pages/home.js
import { card } from '../components/card.js'
export default {
route: '/',
state: {},
view: () => card({ title: 'Hello', body: 'Welcome to Pulse' }),
}
One file per page
Pulse enforces one spec per file. Every page is self-contained — state, view, mutations, actions, and validation in one place. This is not a style preference. It is how the framework eliminates the question of where things live.