Developer Guide
This guide covers setting up a local development environment, working on the codebase, running tests, and cutting releases. For contribution guidelines, issue etiquette, and the code of conduct, see CONTRIBUTING.md.
Prerequisites
- Node.js 24 or later — install from nodejs.org
- npm (bundled with Node.js)
- Git
Setup
git clone https://gitlab.com/cozybadgerde/applications/plate.git
cd plate
npm install
Apply the commit message template so your commits follow the project's Conventional Commits format:
git config commit.template .gitmessage
Development workflow
Start the development server with hot reload:
npm run dev
Plate starts on http://localhost:3000 and reloads on every source change. It requires a running Snackbox instance — point SNACKBOX_API_URL at it:
SNACKBOX_API_URL=http://localhost:8080 npm run dev
For theme development with a live Snackbox fixture (no real Snackbox needed):
npm run theme:dev -- src/themes/picnic # or any other theme path
Testing
npm test # all tests (unit + integration)
npm run test:unit # unit tests only
npm run test:integration # integration tests only
npm run test:watch # watch mode
npm run test:coverage # coverage report → build/coverage/
Unit tests are mandatory. Every new function or class must ship with at least one unit test. Bug fixes must include a regression test that fails before the fix and passes after.
Unit tests live next to the source in src/lib/__tests__/. Integration tests live in test/.
Checks
Run all checks before every commit:
npm run check # lint + spellcheck + typecheck + all tests
Or individually:
npm run lint # ESLint
npm run spellcheck # cspell (en-US)
npm run typecheck # TypeScript (no emit)
All four must pass before opening a merge request.
Project structure
src/
├── config.ts env var loader + validateConfig()
├── server.ts Express app, ThemeManager, bootstrap, error handlers
├── types/ TypeScript interfaces (snackbox, theme, context)
├── lib/ core logic — every file has a corresponding test
├── middleware/ Express middleware (globalContext, requestMetrics)
├── routes/ one file per route group (home, posts, pages, tags, health)
├── fixture/ built-in Snackbox fixture server (ships in dist/)
│ ├── data.ts shared edge-case dataset — single source of truth
│ └── server.ts startFixture() / stopFixture() — built-in http only
├── cli/ npx plate CLI entry point
│ └── index.ts theme:init, theme:validate, theme:check, theme:dev
└── themes/picnic/ built-in fallback theme — reference implementation
test/ integration tests
scripts/ dev tooling (benchmark, sync-version, theme-validator)
contracts/ theme manifest JSON Schema + contract documentation
deployments/ Docker Compose, systemd unit, Caddy config
For a deeper look at how the pieces fit together, see docs/ARCHITECTURE.md.
Commit messages
This project uses Conventional Commits. The .gitmessage template is the authoritative reference. Quick summary:
<type>(<scope>): <subject>
Body — explain the why, not the what. Bullet points are fine.
- resolves: #42
Types: feat, fix, docs, test, refactor, perf, chore, ci
Scope: optional — the affected area (e.g. theme, cache, renderer).
Releasing
-
Bump the version in
package.json. -
Sync the version to the Picnic manifest and theme schema:
bash npm run version -
Regenerate the changelog:
bash npm run changelog -
Run all checks:
bash npm run check -
Commit and tag:
bash git add package.json CHANGELOG.md src/themes/picnic/manifest.json contracts/theme-manifest.schema.json git commit -m "chore(release): v<version>" git tag v<version> git push --follow-tags
The CI pipeline builds and publishes the container image on tag push.
Documentation
The docs site is built with MkDocs Material. Source files live in docs/, configuration in mkdocs.yml. The CI pipeline publishes the site to GitLab Pages on every push to trunk.
The build runs with --strict, which treats warnings as errors. Broken internal links, missing nav entries, and invalid tags all fail the build.
Render locally
Docker (no install required):
docker run --rm -it -p 8000:8000 -v "$PWD":/docs \
squidfunk/mkdocs-material:9.5.47 serve --dev-addr=0.0.0.0:8000
The site is then available at http://localhost:8000 with live reload on save.
Local install:
pip install mkdocs-material mkdocs-minify-plugin
mkdocs serve
Build and check
mkdocs build --strict
# or via Docker:
docker run --rm -v "$PWD":/docs squidfunk/mkdocs-material:9.5.47 build --strict
A clean build with no warnings means the CI Pages job will pass.
Theme development
To develop or extend the built-in Picnic theme, or to build an external theme, see docs/THEME-DEVELOPER.md.
To validate a theme against the contract:
npm run theme:check -- <path-to-theme>