Repository navigation
refactor(dorm): isolate the dorm feature in its own workspace package - #230
Conversation
The dorm feature is moving into its own workspace package and still needs Typewriter, so the component moves out of the web app into a package both can depend on.
Dorm providers wrapped the whole app, so every page loaded dorm code and fetched every dorm from Supabase on startup, and the shell layout reached into housing state. The feature now lives in its own package behind one lazy /dorms/* route: the host lends it the Supabase client, session, chat stream, and two shell slots, and the package imports nothing from the app. Its dependencies (Mapbox GL, rc-slider, Headless UI) leave the web app with it. Filters, search, and the compare list now reset when you leave /dorms; favorites and history persist through localStorage as before.
They write the dorm package's googleReviews.ts, so they now live beside it. Their output paths and the Python env path pointed at directories that no longer exist (../web/src and the legacy app/); they now resolve to the package and apps/web/.env.local.
Dorm SQL, seed, and data-script docs now sit with the package that owns them, and the repo guides describe the boundary: the web app reaches dorm code only through its /dorms adapter.
🚀 Deploying Preview to Cloudflare 🚀Preview Deployments by commit
|
|
React Doctor found 116 new issues in 32 files · 9 errors & 107 warnings · score 44 / 100 (Critical) · 127 fixed · vs Errors
107 warnings
57 more warnings not shown. Reviewed by React Doctor for commit |
Moving the providers under the /dorms route made them unmount on every visit elsewhere, resetting filters, search, the compare list, and the loaded dorms. Their state now outlives the route for the page session, as it did when the providers wrapped the whole app; a reload still clears it.
React Doctor's exhaustive-deps cannot tell that useRetainedState returns React's stable setter, so it flagged every callback and memo using one. Listing them changes nothing at runtime and clears those warnings.
The provider built a new value object on every Layout render, re-rendering every useLayout consumer, including the dorm route, even when nothing they read had changed.
Main pruned package scripts in favor of vp built-ins and rewrote the docs. The dorm docs now use vp dlx tsx from packages/dorm, and the new packages drop their scripts to match.
Main moved operational docs from the READMEs into AGENTS.md. The dorm database and data-script sections it added to apps/web/AGENTS.md now live in a new packages/dorm/AGENTS.md with package paths, and the root architecture and deploy notes point there.
| @@ -1,14 +1,14 @@ | |||
| import { Typewriter } from "@iguide/ui" | |||
| import { motion, AnimatePresence } from "framer-motion" | |||
There was a problem hiding this comment.
React Doctor · react-doctor/use-lazy-motion (warning)
Importing "motion" ships about 30 kb of extra code and slows page load. Use "m" with LazyMotion instead.
Fix → Use import { LazyMotion, m } from "framer-motion" with domAnimation features. Saves about 30kb.
| @@ -1,3 +1,4 @@ | |||
| import { Typewriter } from "@iguide/ui" | |||
| import { AnimatePresence, motion } from "framer-motion" | |||
There was a problem hiding this comment.
React Doctor · react-doctor/use-lazy-motion (warning)
Importing "motion" ships about 30 kb of extra code and slows page load. Use "m" with LazyMotion instead.
Fix → Use import { LazyMotion, m } from "framer-motion" with domAnimation features. Saves about 30kb.
中文摘要
把宿舍功能拆成独立的 workspace 包
@iguide/dorm(packages/dorm),web 侧只通过一个懒加载路由/dorms/*接触它:/chat也会加载宿舍代码、去 Supabase 拉全部宿舍;Layout也直接依赖住房筛选状态。apps/web/src/pages/dorms/DormRoute.tsx挂载它;包本身不从 web 导入任何东西。/chat加载的宿舍模块从 14 个降到 0;所有页面都要加载的主入口 chunk 从 106.95 kB 降到 87.91 kB。/dorms再回来,筛选、搜索、对比列表和已加载的宿舍数据都会保留(不会重新查询),刷新页面才重置——和改动前一致。Why
The dorm feature was half of
apps/web/src(84 of 167 files) and was wired into the app shell:App.tsxwrapped every page in four dorm providers,Layout.tsxread housing filter state and rendered the dorm list's mobile header, andSidebarPanel.tsxrenderedDormSidebardirectly. So a dorm change, bug, or slow query reached pages that have nothing to do with dorms.What changed
packages/dorm(@iguide/dorm) holds the feature: UI, state, services, dorm SQL migrations, data scripts (vp dlx tsx, documented inpackages/dorm/AGENTS.md), and the review scrapers. Files keep their old relative layout, so 88 of the 108 moves are pure renames with history intact. Mapbox GL, react-map-gl, rc-slider, and Headless UI are now the package's dependencies and are gone from the web app.The boundary runs one way: the host passes what the package needs, the package imports nothing from the host.
configureDormServices({ supabase, streamChatResponse })lends the host's Supabase client and the chat stream. The package never creates its own client, because a second GoTrue client would race the host's for the same session.<DormRoutes language user requestLogin layout />is mounted at/dorms/*, with all dorm providers inside it.layoutgives the package the flying-heart target refs plus two slot setters. The dorm sidebar and the list's mobile header portal into those slots, which lets them render in the shell while still reading dorm context — a plain slot node can't do that.Web app:
App.tsxhas no dorm providers left,Layout.tsxlost 133 lines of housing code,SidebarPanelrenders a genericsidebarSlot(a newLayoutContextfield mirroringmobileHeaderSlot), andapps/web/src/pages/dorms/DormRoute.tsxis the only web file that knows the package's API.@iguide/uiholdsTypewriter, which both the chat sidebars and dorm use.Also:
index.cssadds@sourcefor both packages, and the built CSS lost exactly the 0.25 kB of dorm-only CSS that moved into the dorm chunk, so the class set is unchanged.dorm_scripts/moved topackages/dorm/scrapers/; its output paths resolved to a nonexistent<root>/web/srcand its Python env path pointed at the legacyapp/, so both now point at the package and atapps/web/.env.local.Behavior changes
/dorms/*would have reset filters, search, the compare list, and the loaded dorms on every return; the last commit keeps them for the page session (useRetainedState), and a reload still clears them. The filter modal and compare overlay open flags are deliberately not retained, so returning never pops them back open./dorms/*(e.g./dorms/a/b) now redirect to/dorms; before, they fell through to/chat.Verification
All checks below compare against a clean
origin/mainworktree.origin/mainvp lint(type-aware)tsc --noEmit@iguide/dorm✓,@iguide/ui✓vp build(apps/web)index.js/chat(dev)vp dlx tsx scripts/validate-dorm-data.tsEvery commit builds, typechecks, and lints clean on its own.
In the browser (dev server):
/chatmakes no dorm requests and loads no dorm modules;/dormslazy-loads the package and renders the dorm sidebar in the shell; at mobile width the search, filter, sort, and view controls render in the shell header and typing keeps focus;/dorms/:idrenders; navigating back to/chatremoves the sidebar and leaves no placeholder; and with searchisrplus list view, a round trip through/chatkeeps both andgetAllDormsran once in total.Not exercised: my local env has no Supabase or Mapbox keys, so both
origin/mainand this branch show an empty list and "map unavailable". Real dorm data, the map, reviews, and admin editing still need a quick look on a preview deploy.Review guide
refactor(ui)extractsTypewriter.refactor(dorm)is the move and the boundary — focus onpackages/dorm/src/{index.ts,DormRoutes.tsx,services/host.ts,contexts/DormHostContext.tsx},apps/web/src/pages/dorms/DormRoute.tsx, and theLayout/SidebarPanel/LayoutContextdiffs; the rest is moves. Thenchore(dorm)for the scrapers, docs, ci,fix(dorm)for the retained state (see Behavior changes), and three React Doctor fixes (hook deps,LayoutContextvalue, sort-buttonaria-label) with no runtime change.Notes
main: the dorm docs follow main'svp dlx tsxconvention and the packages drop theirscriptsblocks, and operational docs moved out of the READMEs into a newpackages/dorm/AGENTS.mdthat takes precedence withinpackages/dorm/**. Lint, typecheck,vp build, and data validation pass after both merges.Workers Builds: uiucfails here, but it fails on every open PR today, including ones that don't touch dorm code (feat(auth): add authentication service #217, feat(ai-agent): implement durable objects-based AI agents #219, feat(web): redesign frontend with shadcn components #220, refactor(website): eliminate QMD integration #225, fix(web): remove the unauthenticated provider proxy routes #229), whilemainbuilt fine in between.pnpm run build:previewpasses locally; the log is only in the Cloudflare dashboard, so the preview build/deploy step needs a look there.exhaustive-deps, theLayoutContextvalue, and the sort-button label. The twouse-lazy-motioncomments (AIChat.tsx,DormSidebar.tsx) are left as is: the app has 24 fullframer-motionimports, so switching two moved files toLazyMotionalone would be inconsistent — that migration belongs in its own PR.68f0a6c's edit toDormDetailReviews.tsxinto the package). feat(auth): add authentication service #217, feat(ai-agent): implement durable objects-based AI agents #219, and feat(web): redesign frontend with shadcn components #220 touch none of the moved files and conflict only inpnpm-lock.yaml, which a reinstall regenerates.