7.3 KiB
Refactor Strategy
This document outlines a pragmatic, step-by-step refactor plan to improve structure, readability, and maintainability. Each step includes a clear outcome and a checkbox to track progress.
Last updated: 2025-10-18
Goals
- Separate concerns (routing, HTTP/API, mapping/normalization, UI logic)
- Improve readability and testability
- Establish light-weight standards (naming, lint/format) without blocking development
- Keep changes incremental and safe
Phases and Steps
Phase 1 — Routing and Auth (Foundational)
- Extract router into
src/router/index.jswith named routes - Add route meta
requiresAuthand a global auth guard - Remove auth-redirect from
App.vue(handled by guard instead) - Convert top-level nav to use route names consistently (optional)
Outcome: Routing logic is centralized and testable; pages redirect consistently based on auth.
Phase 2 — API Layer Split (Incremental)
- Add
src/api/http.jswrapper for JSON fetch with error handling and env-based base URL - Add
src/api/mappers/mealMapper.jsto normalize Meal data (dates) - Add
src/api/meals.jsand migrate MealPlan API calls (get upcoming, mark consumed, delete) - Create
src/api/recipes.jsand migrate recipe endpoints - Create
src/api/shopping.jsand migrate shopping endpoints - Create
src/api/auth.jsand migrate auth endpoints
Outcome: Feature modules call cohesive services; logic for mapping/normalization is isolated and testable.
Phase 3 — Composables (UI-Facing Logic)
- Add
src/composables/useAuth.js(user ref, ensureAuth) - Add
src/composables/useMeals.js(fetch and mutate meals) - Refactor pages to use composables and
<script setup>where appropriate- Converted:
MealPlanPage.vue,EditMealPage.vue,CurrentShoppingListPage.vue,MyShoppingPage.vue,PurchasedShoppingListPage.vue - Added:
src/composables/useShopping.js; adopted by shopping pages
- Converted:
Outcome: Components get smaller and easier to read; business logic is reusable.
Phase 4 — Tooling and Standards
- Add Prettier config and .editorconfig; wire Prettier with ESLint
- Upgrade ESLint (if/when convenient) and align with Vue 3 rules
- Ensure Volar is used (dev environment) for Vue 3 type intelligence
- Add lint-staged + husky for
pre-commitformatting - Optional next: enable Vue macros (defineProps/defineOptions) in ESLint or upgrade ESLint/vue plugin
- Add lint-staged + husky for
Outcome: Stable formatting and consistent linting across contributors.
Phase 5 — Tests (Targeted)
Composition API Migration Plan
This document tracks the migration of remaining components to Vue 3 Composition API using <script setup>, with clear ordering and acceptance criteria.
Last updated: 2025-10-18
Goals
- Convert all SFCs to Composition API
<script setup>. - Remove Options API patterns (
data,methods,computed,watch,this.*). - Standardize on composables for cross-cutting concerns (auth, alerts, API calls).
- Keep changes incremental and safe with focused PRs and existing tests.
Current status
Already using <script setup>:
- Meals:
MealPlanPage.vue,EditMealPage.vue - Shopping:
CurrentShoppingListPage.vue,MyShoppingPage.vue,PurchasedShoppingListPage.vue
Remaining to migrate (Options API or mixed):
-
Core
App.vuecomponents/AlertToast.vuecomponents/ActionItem.vuecomponents/LoginPage.vue
-
Recipes
components/recipes/RecipesPage.vuecomponents/recipes/RecipeSearchBox.vuecomponents/recipes/RecipeCard.vuecomponents/recipes/EditRecipePage.vue(most complex)
-
Meals
components/meals/MealCard.vuecomponents/meals/DatePicker.vuecomponents/meals/PersonList.vue(mixed Options+setup, unify under<script setup>)
-
Ingredients
components/ingredients/CompactParsedIngredient.vuecomponents/ingredients/IngredientLine.vuecomponents/ingredients/EditableIngredientsPanel.vue
-
Shopping
components/shopping/MealSelectionList.vuecomponents/shopping/ShoppingListItem.vue
Migration order (batches)
- Leaf/presentational components (low risk)
ActionItem.vue,RecipeCard.vue,CompactParsedIngredient.vue- Patterns: defineProps, no router; replace
props: ['x']withdefineProps<{...}>(or JSDoc). Simple emits withdefineEmits.
- Simple interactive components
IngredientLine.vue,MealCard.vue,MealSelectionList.vue,DatePicker.vue- Patterns: replace
datawithref/reactive, computed withcomputed, methods with local functions. Replacethis.$emitwithemit.
- Components with watchers and DOM refs
EditableIngredientsPanel.vue,PersonList.vue,ShoppingListItem.vue- Patterns: use
reffor elements,onMountedfor subscriptions/layout,watchfor reactive sources. Ensure timeouts/listeners are cleaned up.
- Pages and core scaffolding
RecipesPage.vue,RecipeSearchBox.vue,EditRecipePage.vue,LoginPage.vue,App.vue,AlertToast.vue- Patterns: replace
this.$router/this.$routewithuseRouter/useRoute. Consider a smalluseAlertcomposable to replace the event bus pattern used byAlertToast.vueand currentalert.js.
Conventions and helpers
- Routing:
const router = useRouter(); const route = useRoute(); - Props/Emits:
const props = defineProps({ ... })const emit = defineEmits(['event-name'])
- State:
const state = reactive({...})orconst x = ref(initial) - Computed/Watch:
const y = computed(() => ...);watch(source, (val, old) => ...) - Lifecycle:
onMounted,onBeforeUnmount - Assets: import statics via
new URL('@/assets/foo.svg', import.meta.url).hrefor leave templaterequire()where needed (non-blocking). - Testing: keep current Vitest setup; prefer unit tests for any functional changes.
Acceptance criteria per component
- The component uses a single
<script setup>block. - No
this.*usage remains; props accessed viapropsor destructured; emits viaemit. - Route navigation uses
useRouter/useRoutewhere applicable. - All existing functionality and events preserved.
- Lint/test/build pass.
Tracking checklist
-
Core:
App.vue -
Core:
components/AlertToast.vue -
Core:
components/ActionItem.vue -
Core:
components/LoginPage.vue -
Recipes:
components/recipes/RecipesPage.vue -
Recipes:
components/recipes/RecipeSearchBox.vue -
Recipes:
components/recipes/RecipeCard.vue -
Recipes:
components/recipes/EditRecipePage.vue -
Meals:
components/meals/MealCard.vue -
Meals:
components/meals/DatePicker.vue -
Meals:
components/meals/PersonList.vue -
Shopping:
components/shopping/MealSelectionList.vue -
Shopping:
components/shopping/ShoppingListItem.vue
Notes and risks
PersonList.vuealigns a dropdown to an input via DOM measurements; ensure the ref-based approach updates positions correctly on focus/resize.RecipeSearchBox.vueuses timeouts for debouncing; preferwatchwith a debounced effect and clean up on unmount.AlertToast.vueuses a simple event-bus (alert.js); consider migrating to auseAlertcomposable with aref-based queue to simplify subscriptions.
Done (context)
- Routing extracted with auth guard; API layer split; composables for auth/meals/shopping; prettier/husky configured; Vitest tests for mappers and units in place.