3.9 KiB
Munch Ease — Plan, Cook, Shop, Repeat
Munch Ease is a snappy Vue 3 app that helps you plan meals, manage recipes, and turn plans into stress-free shopping lists. Search and save recipes, build your weekly meal plan, and seamlessly check items off your shopping list—everything stays in sync so you can focus on what’s cooking.
Built with modern Vue patterns, a clean API layer, and lightweight tests, the project is easy to extend and fun to work on.
Quick start
- Install dependencies
npm install
- Run the dev server
npm run serve
- Run unit tests (Vitest)
npm run test
- Build for production
npm run build
Environment
- API base URL: set VUE_APP_API_BASE (e.g. http://localhost:8081)
Architecture and conventions
Strict TypeScript, Vue 3 Composition API, and a single typed API boundary.
Key axioms
- OpenAPI (generated
src/api/types.ts) is the single source of truth for shapes. - SDK (
src/api/sdk.ts) is the only data access surface; UI uses domain types fromsrc/domain/types.ts. - Domain normalization is minimal in
src/domain/decoders.ts(e.g., date strings →Date). - No casts (
as, angle brackets) and noany/unknownin app code. Generated files are exempt. - Arrays that are required in OpenAPI are non-nullable in domain types (e.g.,
Meal.recipes). - Disallow runtime type checks in app code; acceptable exceptions: DOM event narrowing, error/env handling in the boundary.
- Let the type system prevent mistakes: encode intent in function signatures. Prefer explicit option objects to loose primitives, make required fields non-optional, and avoid permissive overloads that widen the error surface.
- Navigation is slug-scoped and helper-driven: only use
src/router/links.tshelpers and named routes. For non-id pages pass{ slug }or omit to infer; for id pages passidor{ id, slug }. Never build path strings or read slugs from route params in components.
Layout
src/api/— Typed client and SDK boundarysrc/domain/— Domain types and decoderssrc/composables/— Reusable app logic (auth, meals, shopping, pagination, alert)src/components/— UI components and pagessrc/router/— Routes and helpers (links.tsfor slug-scoped navigation, plusparseRouteId,parseQueryString)
Testing and tooling
- Vitest + MSW under
tests/ - ESLint (type-aware) + Prettier + Volar
Development tips
- Prefer composables for shared logic; keep components thin.
- Use computed for derived values; avoid mutating props directly.
- Use discriminated unions for UI-only shapes when helpful (e.g., shopping
Group). - Add/adjust tests when changing behavior.
Troubleshooting
- API errors: verify
VUE_APP_API_BASEis set and reachable. - Type/IDE help: ensure Volar is enabled and ESLint is not conflicting with Prettier.
- Build issues: this project uses Vue CLI 5. If migrating to Vite, update scripts and configs accordingly.
Type checking and codegen
- Type check (TS strict + vue-tsc):
npm run typecheck
- Generate API types (consumed by SDK):
npm run codegen
Environment
- VUE_APP_API_BASE (Vue CLI) or VITE_API_BASE_URL (Vite) for API base URL
Testing
- Unit tests use MSW; the client defaults to a localhost base in tests for easy mocking
CurrentShoppingList item kinds
The OpenAPI spec models current shopping list items as distinct kinds:
- outstandingItems: ListIngredientItem[]
- requestedMeals: RequestedMealItem[]
- purchasedItems: ListIngredientItem[]
The SDK maps these to a domain DTO (CurrentShoppingListDTO) and may attach refs (ingredient, recipe, meal, list) for convenience. UI code should:
- Prefer stable IDs (
ingredientId,mealId,recipeId,listId) for actions and lookups - Treat attached refs as optional view helpers (never required)
- Keep all normalization at the boundary (decoders); avoid casts and runtime type checks in app code