Skip to content

Building and Development

Hails edited this page Jul 15, 2026 · 4 revisions

Building and Development

The build has two halves: esbuild bundles the TypeScript in ts/ into browser bundles under static/js/, and the Go toolchain compiles a single static binary that embeds locale files and game data snapshots. This page covers the Makefile targets, the npm scripts, cross-compiling for Linux, the development loop, and what is gitignored and why. For the runtime layout of the code, see Architecture; for the TypeScript side in depth, see Frontend-Guide.

Makefile targets

Target What it runs When to use
make setup npm install Once after cloning
make build npm run build then go build -o hailsDotGO . Produce a full local build
make dev npm run watch & plus go run . Development loop in one command
make run ./hailsDotGO Run a previously built binary
make clean rm -f hailsDotGO static/js/*.js Remove build output

There is no test or lint target; the Makefile is a thin convenience wrapper, and running the underlying commands directly (for example on Windows without make) is equivalent.

The TypeScript build

npm run build invokes esbuild with seven entrypoints, defined in package.json:

ts/raids.ts  ts/dps.ts  ts/pvp.ts  ts/events.ts  ts/shinies.ts  ts/iv.ts  ts/raidfinder.ts

Each entrypoint is bundled (shared code under ts/shared/ gets inlined into every bundle that imports it), targeted at ES2020, minified, and written to static/js/. npm run watch is the same command with --watch instead of --minify, recompiling on save.

Two consequences worth knowing:

  • static/js/ is generated output and is gitignored. A fresh clone has no JS until you run a build, and the deploy scripts pick up whatever is in static/js/ at deploy time, so always build before deploying. The main deploy.ps1 runs npm run build itself and uploads every *.js file it finds there, so a newly added entrypoint is never silently skipped.
  • Adding a page with its own script means adding an entrypoint to both the build and watch scripts in package.json.

The Go build

A plain go build produces the server binary for your current platform:

go build -o hailsDotGO .

The binary embeds (via go:embed) the locale JSON under internal/i18n/locales/ and the game data fallback snapshots, so those travel inside the executable. Templates under templates/ and assets under static/ are read from disk at runtime and must be shipped alongside the binary.

Generated data: internal/costumes/catalog.json

Costume data lives in two files under internal/costumes/. catalog.json is generated (its own note says do not hand-edit) by go run ./cmd/synccostumes (or make costumes), which reads the mined PokeMiners asset tree and records every costume code that has shiny art and exactly which species can wear it. labels.json is its curated companion: the human labels trainers see and type, mapped onto those codes. Both are go:embeded by the server and imported directly by ts/shared/costumes.ts, so browser and server share one source and there is no Go mirror to regenerate (the old costumes_data.go and scripts/gen-costumes.mjs are gone).

Regenerate the catalog after an event drops new costumes, then give any new codes a label. make costumes-check (go run ./cmd/synccostumes -check) writes nothing and exits non-zero when upstream has costumes the catalog does not know about, which is handy for CI or release prep. Generating the catalog is deliberately not a dependency of make build, so the build never needs the network.

Cross-compiling for Linux

Production servers run linux/amd64, and the app needs no cgo, so cross-compiling from Windows or macOS is trivial:

# PowerShell
$env:GOOS = "linux"; $env:GOARCH = "amd64"; $env:CGO_ENABLED = "0"
go build -ldflags="-s -w" -o hailsDotGO-linux .
$env:GOOS = ""; $env:GOARCH = ""; $env:CGO_ENABLED = ""
# bash
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-s -w" -o hailsDotGO-linux .

-ldflags "-s -w" strips the symbol table and DWARF debug info to shrink the binary. This is exactly what both deploy scripts do; see Deployment.

Development loop

Run two processes side by side:

# Terminal 1
npm run watch

# Terminal 2
go run .

Then iterate:

  • TypeScript changes are recompiled by the watcher; reload the browser.
  • Template (templates/*.html) and Go changes require restarting go run . (templates are parsed at startup).
  • CSS lives in static/css/main.css and is served from disk; reload the browser. Asset URLs carry a content-hash ?v= query computed at startup, so a restart also refreshes cache-busted URLs.
  • Raid Finder timer testing: set RAID_FAST_TIMERS=1 to shrink the confirm/invite windows to seconds (see Configuration).

The server needs a reachable MySQL database and the required env vars exported even in development; see Getting-Started.

What is gitignored and why

The repository deliberately ships no .gitignore (so the template itself stays visible); copy it once after cloning:

cp .gitignore.example .gitignore
Ignored path Why
hailsDotGO, hailsDotGO-linux, hailsDotGO.exe Build output, rebuilt every time
static/js/ esbuild output, regenerated from ts/ on every build
node_modules/ npm dependencies
.env, *.env, app.env Secrets: database credentials, CSRF key, PayPal keys, GitHub token
deploy-manifest.json Local deploy state (SHA256 hashes of last-uploaded files)
cache/ Runtime cache of fetched game data and scraped event pages
locales/ Runtime translation overrides and their backups, server-local state
.DS_Store macOS noise

Committing any of the secret files is the only truly dangerous mistake here; everything else is just clutter. If you accidentally commit .env, rotate every credential in it.

Clone this wiki locally