Files
RenewCMS/docs/development.md
T

6.3 KiB
Raw Blame History

Development

Prerequisites

Tool Version Install
Go 1.27+ (see go.mod)
Node.js 20.19+ or 22.12+ (22 LTS recommended) nvm install 22 — Vite and vue-tsc run on Node
Bun (package manager and script runner) 1.x powershell -c "irm bun.sh/install.ps1 | iex" or curl -fsSL https://bun.sh/install | bash
Task 3.43+ go install github.com/go-task/task/v3/cmd/task@latest
Git any

task setup installs the rest: air for hot reload, gremlins for mutation testing, Go modules and frontend dependencies. Make sure $(go env GOPATH)/bin is on your PATH.

No C compiler is needed. The SQLite driver (glebarez/sqlite, backed by modernc.org/sqlite) is pure Go, and every build runs with CGO_ENABLED=0.

First run

cp .env.example .env     # set at least JWT_SECRET; SMTP is only needed to actually send mail
task setup
task dev

Open http://localhost:5173.

How the dev setup works

browser ──▶ http://localhost:5173   Vite dev server (HMR)          task dev:web
              │  /v1/*, /healthz
              └──proxy──▶ http://localhost:8080   Go API via air   task dev:api
  • Always use the Vite URL (5173). The Vite dev server proxies API calls to Go (see server.proxy in web/vite.config.ts). The browser therefore sees a single origin, so the jwt cookie works and no CORS setup is needed. In the frontend, call the API with relative URLs (/v1/...) and never hardcode localhost:8080.
  • air rebuilds and restarts the API when a .go or .html file (such as a mail template) changes. Its config is in .air.toml.
  • Run the two halves separately with task dev:api and task dev:web if you want their logs in separate terminals.

The nofrontend build tag

The production binary embeds the built SPA from internal/infrastructure/assets/dist/ (the //go:embed in assets_embed.go). That directory only exists after task build:web.

To keep Go work independent of the frontend toolchain, all dev and quality tasks build with -tags nofrontend. That tag compiles assets_stub.go instead of the embed. The resulting binary serves the API normally and answers / with a notice that points to Vite.

Command Tag Needs the frontend built?
task dev:api, test, cover, mutate, lint:go nofrontend no
task build, release, run (none) yes (the task builds it first)
plain go build ./..., go vet ./... (none) yes, so add -tags nofrontend

If your editor reports pattern all:dist: no matching files found, either run task build:web once or set the gopls build flags to -tags=nofrontend. In VS Code, add this to settings.json: "gopls": { "buildFlags": ["-tags=nofrontend"] }.

Everyday commands

task --list          # everything available
task test            # Go tests; extra args pass through: task test -- -run TestCreateArticle -v
task test:mysql      # MySQL tests against real servers in Docker (mysql:8.4, then mariadb:11)
task cover           # coverage of the use cases → tmp/coverage.out
task mutate          # gremlins mutation testing → tmp/gremlins.json
task test:web        # SPA component tests (Vitest)
task visual:check -- log-in   # screenshot design vs SPA for a screen → tmp/visual/log-in/
task lint            # gofmt check, go vet, eslint --fix, vue-tsc
task fmt             # gofmt -w + prettier
task run             # build the production binary and run it (SPA embedded) on :8080
task clean           # remove bin/, tmp/ and the built SPA

Tests

  • All Go tests live in test/ (package test, black-box). They use in-memory fakes of the domain Repository interfaces, so they need no database, SMTP server or network.
  • HTTP-level tests use net/http/httptest against a chi router. Call validator.InitValidator() before invoking a handler.
  • SPA component tests use Vitest + Vue Test Utils (task test:web). task lint:web runs eslint and a type check.

Visual check against the design

task visual:check -- <screen> [state] compares the SPA with the Claude Design boards in docs/design/source. The screen is a file name from docs/design/screens (log-in, home, …), and the state is one of its headings.

  • It builds the SPA into tmp/visual/dist and serves both the build and the boards locally, so no API or dev server is needed.
  • For every state it screenshots:
    • the board's frame for that state;
    • the SPA in the same state, at 1440×900 and 390×844, in light and dark.
  • It saves design-*.png, app-*.png and diff-*.png (differing pixels in red) to tmp/visual/<screen>/<state>/, and a summary to tmp/visual/<screen>/report.md.
  • web/visual/states.ts says how to reach each state in the SPA: the route, the mocked /v1 responses (keyed 'METHOD /v1/path') and any user steps. A state without an entry is captured on the design side only, and marked "design only" in the report.
  • Narrow a run with --viewport=desktop|mobile and --theme=light|dark.
  • Lucide icons are served from the local @iconify-json/lucide set, for both the boards and the SPA. api.iconify.design rate-limits repeated runs.
  • The first time on a machine, run bunx playwright install chromium in web/.

The pixel percentage only points at where to look, because font anti-aliasing always differs a little. Judge by comparing the image pairs.

Data

With the default configuration, local state lives in ./data/: renewcms.db and uploads/. The directory is gitignored. Delete it to start from scratch.