Files
sokoban/docs/code-standards.md
T
tiennm99 8a3d4b4a9d feat!: rewrite on Svelte 5, drop Phaser
Replace Phaser 3 with Svelte 5 as the rendering and UI layer. The
framework-agnostic core (level parser, board model, progress store,
microban level data) moves from src/game/core → src/lib/core with zero
code changes. Scenes and the hand-rolled button factory are gone; in
their place:

- src/App.svelte            root router (menu / levels / game)
- src/views/MenuView        title + play + progress + hints
- src/views/LevelSelectView paginated 5x4 grid with native <button>s
- src/views/GameView        owns BoardModel, handles input, HUD, win
- src/views/Board           purely presentational DOM renderer
- src/views/AppButton       shared themed wrapper for native <button>
- src/app.css               Nord palette ported to CSS variables

GameView uses a non-reactive BoardModel ref and syncs plain snapshot
fields (player, boxes, moves, won) into $state after every mutation —
Board consumes only plain props, so Svelte reactivity stays predictable
and the core class stays framework-agnostic. GameView is keyed on
levelIndex in App, so changing level remounts with fresh state.

Native <button> everywhere kills the click-hitbox class of bugs.
Animations are now CSS transform transitions (110ms) instead of tweens.

Bundle shrinks from ~1.5 MB Phaser to ~65 kB JS / 23 kB gzipped — about
60x smaller. Removed: phaser, terser, src/game, log.js (analytics
ping), phasermsg vite plugin, manual Phaser chunks, terser config,
public/style.css. Scripts simplified to dev/build.

Docs updated: codebase summary, architecture, code standards,
changelog, roadmap, README.
2026-04-12 00:50:46 +07:00

40 lines
2.1 KiB
Markdown

# Code Standards
## Language & Toolchain
- ES modules, modern JS (no TypeScript).
- Svelte 5 with runes (`$state`, `$derived`, `$props`).
- Vite as build tool. Dev: `npm run dev`. Prod: `npm run build`.
## Naming
- Plain JS files: **kebab-case** with descriptive names (`board-model.js`, `progress-store.js`).
- Svelte components: **PascalCase.svelte** per ecosystem convention (`MenuView.svelte`, `AppButton.svelte`).
- Classes: PascalCase (`BoardModel`).
- Functions and variables: camelCase.
- Constants: UPPER_SNAKE for module-level tuning knobs (`REPEAT_MS`, `PER_PAGE`).
## File size
- Code files must stay under 200 lines of code.
- Pure-data files (levels, palettes) are exempt.
## Architecture rules
- **Views** (`src/views/*.svelte`) own layout + user interaction. Each screen is one component, kept under 200 LOC.
- **Core** (`src/lib/core/`) is pure JS — no Svelte imports. Anything that can be unit-tested without a DOM lives here (parser, board model, progress store).
- **Data** (`src/lib/data/`) is framework-agnostic static data.
- `Board.svelte` is purely presentational: plain props in, DOM out. It does not import or touch `BoardModel`.
- `GameView.svelte` owns the mutable `BoardModel` instance and calls `syncFromModel()` after every mutation to reassign the reactive `$state` snapshots that `Board` consumes.
- No new dependencies without updating this doc.
## Style
- Prefer composition over inheritance.
- Fail loudly during development (console.error on unexpected state), fail gracefully at runtime (try/catch around level parsing, progress store).
- Comments: short, explain *why*, not *what*. File headers give a one-sentence purpose.
## Git / commits
- Conventional commits (`feat:`, `fix:`, `refactor:`, `docs:`, `chore:`).
- Never commit dotenv, keys, or build artifacts.
- Run `npm run build-nolog` before pushing to catch compile errors.
## Testing strategy (current)
No automated tests yet. Manual smoke test: load menu → play level 1 → complete → verify progress saved in localStorage → reload page → verify completion persists.
Future: unit tests for `level-parser.js` and `board-model.js` (both framework-free JS).