project-rules
daodao-f2e 專案開發規範,涵蓋 package 使用、API 錯誤處理、命名慣例、程式碼風格
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Menu
daodao-f2e 專案開發規範,涵蓋 package 使用、API 錯誤處理、命名慣例、程式碼風格
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Baseado na classificação ocupacional SOC
Push 後收集 PR 上的所有 review feedback(CI、AI Code Review、Gemini Code Assist、人類 reviewer),分析並詢問使用者要修正哪些
在 iOS 模擬器上跑 apps/mobile(Expo)。涵蓋 prebuild 過期、code signing、模擬器選錯、Metro 舊 bundle、i18n key 對不到等已知陷阱與除錯法
daodao-f2e codebase 地圖——monorepo 結構、依賴方向、常見任務觸點、隱藏的 build 依賴。開工前先讀,省去盲目探索
島島 daodao 六個 repo 的系統全景圖——服務職責、呼叫關係、types 同步鏈、跨 repo 變更 SOP。任何變更可能影響其他 repo 時必讀
Push 前 review 整個 branch 的變更,檢查邏輯錯誤、安全問題、效能問題、架構一致性
commit 前執行 lint、typecheck 檢查,自動修復可修的錯誤
| name | project-rules |
| description | daodao-f2e 專案開發規範,涵蓋 package 使用、API 錯誤處理、命名慣例、程式碼風格 |
nvm use 20.19.4)必須用 @daodao/config,禁止 process.env。
import { getEnv, getRequiredEnv } from "@daodao/config";
const apiUrl = getEnv("NEXT_PUBLIC_API_URL", "https://api.example.com");
必須用 @daodao/shared 的 getStorage,禁止直接用 localStorage / sessionStorage。
import { getStorage, StorageEnum } from "@daodao/shared";
const userStorage = getStorage<UserInfo>(StorageEnum.UserInfo);
可用 key 的權威清單在 packages/shared/src/lib/storage.ts 的 StorageEnum(本文件不窮舉,清單會過時——盤點時已有 11 個 key,如 UserInfo、Quiz、ManualPracticeDraft、ActionMaker、AuthSignal 等)。
新增 key:在 StorageEnum 加 key → mapStorageKeyToStorageType 定義 local/session 類型。
注意:getStorage 是 SSR-safe 且靜默失敗(SSR 或例外時回 no-op),呼叫端不可假設寫入成功。
必須用 @daodao/ui,禁止直接用原生 HTML 元素(<button>、<input> 等),除非 @daodao/ui 沒有。
import { Button } from "@daodao/ui/components/button";
必須用 @daodao/shared,禁止重複實作。匯出的權威清單在 packages/shared/src/index.ts(本文件不窮舉)。寫新 hook/util 前先 grep 該檔確認是否已存在。
useScrollLock、useMediaQuery(含 useIsMobile 等 breakpoint 變體)、useQueryState、useFormDraftgetStorage、captureElementAsImage、parseTextLinks、getShareAPI必須用 @daodao/api,禁止 fetch / axios。
App 端只 import domain 匯出與 useMutate(useQuery/client 沒有從 package 對外匯出):
import { useCurrentUser, createPractice, useMutate } from "@daodao/api";
packages/api/src/services/ 內部才用相對路徑取 client 與 hooks 工廠:
import { client } from "../client";
import { useQuery } from "../hooks";
已知例外(歷史債務,不可仿效):action-maker feature 直接 fetch worker、recommendation/showcase hooks 直接 fetch ai-backend。新程式碼一律走 @daodao/api 的模式。
必須用 date-fns,禁止原生 Date 方法(toLocaleDateString()、setDate() 等)。
import { format, addDays, parse } from "date-fns";
所有 API 呼叫必須檢查 response.error。openapi-fetch 不拋異常,錯誤在 response.error 裡。
const response = await createPractice(apiRequest);
if (response.error) {
const errorMessage =
response.error && typeof response.error === "object" && "message" in response.error
? String(response.error.message)
: "操作失敗";
toast.error(errorMessage);
setIsSubmitting(false);
return; // 必須 return
}
// 成功邏輯
clearDraft();
router.push("/success");
response.errorresponse.error.message)toast.error() 顯示return 避免執行成功邏輯console.error() 記錄if ("details" in error && Array.isArray(error.details)) {
const details = error.details as Array<{ path?: string; message?: string }>;
details.forEach((detail) => {
if (detail.path && detail.message) {
form.setError(detail.path as keyof FormValues, {
type: "server",
message: detail.message,
});
}
});
}
放在 apps/product/src/constants/,const object + type 模式:
export const TaskStatus = {
draft: "draft",
notStarted: "not-started",
inProgress: "in-progress",
completed: "completed",
} as const;
export type TaskStatus = typeof TaskStatus[keyof typeof TaskStatus];
禁止直接用字串字面量表示常數值。
現有常數以 apps/product/src/constants/ 目錄為準(本文件不窮舉——盤點時已有十多個,如 MoodType、TaskStatus、PracticeStatus、NotificationType、ReactionType 等)。新增前先 grep 該目錄避免重複。
Type 後綴(TaskStatusType)I 前綴(ICheckInFormData)Props 後綴(ButtonProps)(() => { ... })()順序:Imports → Types → Query Hooks → Mutation Hooks
"use client";
// Imports
import { client } from "../client";
import { useQuery } from "../hooks";
// Types
export type CreateRequestType = components["schemas"]["CreateRequest"];
// Query Hooks
export const useResource = (id: string) => useQuery("/api/v1/resource/{id}", { params: { path: { id } } });
// Mutation Hooks
export const createResource = async (data: CreateRequestType) => client.POST("/api/v1/resource", { body: data });