refactor(routes): inline single-URL pages + clientLoader prefetch#123
Merged
Conversation
Combine react-router typegen, wrangler types, and pwa-icons into a single parallel `yarn generate` step. Drops the bespoke `pwa:generate-icons` and `codegen` scripts. CI now runs `yarn generate` instead of just icons. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mechanical refactor — function expressions to arrow functions, string concatenation to template literals. No behavior change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two intertwined changes (touching the same route files, so committed together): **Route consolidation.** Pages with one URL move from src/features/*Page.tsx into src/routes/*.tsx as their default export. Pages shared across multiple URLs (RecipeEditor, ExerciseDetail, WorkoutTemplate, WorkoutSession, ProgramEditor) stay in features/ and are re-exported from the route files. TimerMode → TimerModeView (presentational, consumed by both the timer route and the landing AutoSection). **clientLoader prefetch.** Every data-fetching route exports a typed clientLoader that fires its tRPC queries in parallel with bundle download via a new ~/lib/loader prefetchRoute helper. - Module-level singletons in ~/lib/trpc — queryClient, trpcClient, trpcUtils (createTRPCQueryUtils) — replace the per-render useState client. - IDB rehydration via persistQueryClientRestore awaited inside prefetchRoute, with persistQueryClientSubscribe wired up for ongoing persistence. Cold boots no longer fire network requests before the persisted cache restores. - ensureData() (not prefetch) for correct error semantics and warm-cache no-ops. Promise.allSettled so one query failing doesn't cancel siblings; TRPCClientError UNAUTHORIZED is swallowed so signed-out deep links render the SignedOut branch instead of tripping ErrorBoundary. - Drops refetchOnMount: 'always' — staleTime + mutation invalidations cover freshness; 'always' was forcing redundant refetches that defeated the loader cache hit. CLAUDE.md updated to reflect the new routes/ ↔ features/ split. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI doesn't have .dev.vars, so running `wrangler types` there overwrites the committed worker-configuration.d.ts with an empty Env type and breaks typecheck (15 errors in ai/ingredients/settings routes). worker-configuration.d.ts is committed and CI types depend on it (per CLAUDE.md). Restore the previous behavior: wrangler types is a manual `yarn codegen` for when wrangler.toml changes; not part of the parallel generate that runs in CI. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The "worker-configuration.d.ts is committed" gotcha didn't make it explicit that wrangler types must NEVER run in CI. The previous PR shipped a `generate:wrangler` inside `yarn generate` (which runs in CI typecheck), silently breaking every route that reads a secret. Strengthen the rule. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Preview deployment
|
Restores wrangler types as part of the parallel `yarn generate`, but short-circuits with `test -f .dev.vars && ... || true` so it only runs where the secrets are present. Local yarn check / yarn build now refresh worker-configuration.d.ts automatically; CI silently skips the step instead of overwriting the committed file. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
wrangler 4.30+ added --env-file (workers-sdk#11038) which makes wrangler types deterministic across machines: point it at a committed template that lists every secret key with placeholder values, and the generated Env is identical locally and in CI regardless of which secrets the developer has in .dev.vars. Switch generate:wrangler to use the template, gitignore the generated file, and untrack the previously-committed copy. Local yarn check / build regenerate it from scratch; CI does the same with no extra setup. Bonus: workers/.dev.vars.template doubles as documentation of which secrets a contributor needs to provide. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Spent a chunk of the previous PR debugging why \`yarn workspace @macromaxxing/workers wrangler types --env-file ...\` failed with "node: <path>: not found" even though the same args worked when invoking wrangler directly. Node 24 parses --env-file before the script, eats the path, then can't find it. Wrapping the call in a workspace script avoids the collision. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two intertwined changes on the same route files:
src/features/*Page.tsxintosrc/routes/*.tsxas the default export. Multi-URL pages (RecipeEditor, ExerciseDetail, WorkoutTemplate, WorkoutSession, ProgramEditor) stay infeatures/and are re-exported by the route files.TimerMode→TimerModeView(presentational, consumed by both the timer route and the landing AutoSection).clientLoaderprefetch — every data-fetching route exports a typedclientLoaderthat fires its tRPC queries in parallel with bundle download. New~/lib/loader.tshelper centralizes the pattern for an eventual tRPC v12 migration.Plus two preliminary
chore:commits —yarn generateparallelizes codegen (typegen + wrangler types + pwa-icons), andpublic/self-heal.jsis mechanically modernized to arrow syntax.Why these are bundled
The clientLoader work and the route inlining touch the same files. Splitting them would require
git add -pchunk-by-chunk on every route file. Commit history separates the codegen and self-heal chores so the route diff stays focused.clientLoader design notes
~/lib/trpc—queryClient,trpcClient,trpcUtils(viacreateTRPCQueryUtils) replace the per-renderuseState-bound client. Loaders need module-level access.persistQueryClientRestoreis awaited before anyensureDatacall via theidbReadypromise. Cold boots no longer fire network requests before the persisted cache restores.persistQueryClientSubscriberuns fire-and-forget for ongoing persistence..ensureData()over.prefetch()— propagates errors instead of swallowing, no-ops on warm cache.Promise.allSettled+UNAUTHORIZEDswallow — composite pages don't cancel sibling queries on one failure; signed-out deep links render theSignedOutbranch instead of tripping ErrorBoundary. Other errors propagate to the route's ErrorBoundary (an improvement over today's silent.prefetch()swallowing).refetchOnMount: 'always'—staleTime+ mutation invalidations already cover freshness;'always'was forcing redundant refetches on every navigation that defeated the loader cache hit.Routes audited
All 17 data-fetching routes covered.
recipes.newandworkouts.sessions.$sessionId.timerskipped (no fetch). For composite pages (analytics, plans, settings, workouts._index) the loader mirrors every unconditionaluseQueryat mount, including those nested inside child components likeMuscleHeatGridandProfileForm. Component-state-driven queries (search inputs, dropdown windows on detail pages) are NOT prefetched — they refetch as today.Test plan
yarn check— lint + typecheck + test all passyarn build— SPA prerender producesworkers/dist/client/index.html/recipes/<id>(cold cache) — tRPC batch fires before component mount, no spinner flash/→/analytics(warm cache) — 6 analytics queries batch in parallel with bundle, all 6 cards populate without per-card spinners/—LandingPagerenders, no console error, no ErrorBoundary trip/recipes— IDB cache restores, page renders with stale data/workouts(signed in) —HydrateFallbackshows briefly, page renders with all 5 queries' data ready🤖 Generated with Claude Code