Files
sokoban/docs/code-standards.md
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

2.1 KiB

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).