--- name: feature-generator description: React Native Feature 模块生成指南。当用户提到"创建 feature"、"新建模块"、"生成功能"、"添加功能模块"、"feature 模板"时使用此 skill。 --- # Feature 模块生成指南 在 `comet` CLI 落地前,本指南提供手动创建符合设计规范的 feature 模块的完整流程。 ## 快速生成 ### 完整 Feature(三层架构) ```bash # 创建目录结构 mkdir -p src/features//{presentation/{screens,components,stores},domain/{entities,repositories},data/{datasources,repositories}} # 创建测试目录 mkdir -p __tests__/features//{presentation,domain,data} ``` ### 轻量 Feature(仅 Presentation) ```bash mkdir -p src/features//presentation/{screens,components,stores} mkdir -p __tests__/features//presentation ``` --- ## 目录结构 ### 完整三层架构 ```text src/features// presentation/ screens/ Screen.tsx # 页面组件 components/ View.tsx # 纯展示组件 .tsx # 其他 UI 组件 stores/ useStore.ts # Zustand Store hooks/ useQuery.ts # TanStack Query(可选) domain/ entities/ .ts # 实体类型定义 repositories/ Repository.ts # 仓库接口 data/ datasources/ RemoteDataSource.ts # 远程数据源 LocalDataSource.ts # 本地数据源(可选) repositories/ RepositoryImpl.ts # 仓库实现 ``` --- ## 文件模板 ### 1. Screen 组件 `presentation/screens/Screen.tsx` ```tsx import React from 'react'; import {View, Text} from 'react-native'; // import {useStore} from '../stores/useStore'; type Props = { // navigation props if needed }; export function Screen({}: Props) { // const {state, action} = useStore(); return ( Screen ); } ``` ### 2. View 组件 `presentation/components/View.tsx` ```tsx import React from 'react'; import {View, Text} from 'react-native'; type Props = { // view props }; export function View({}: Props) { return ( View ); } ``` ### 3. Zustand Store `presentation/stores/useStore.ts` ```ts import {create} from 'zustand'; type State = { // state isLoading: boolean; error: string | null; // actions setLoading: (loading: boolean) => void; setError: (error: string | null) => void; reset: () => void; }; const initialState = { isLoading: false, error: null, }; export const useStore = create<State>((set) => ({ ...initialState, setLoading: (isLoading) => set({isLoading}), setError: (error) => set({error}), reset: () => set(initialState), })); ``` ### 4. TanStack Query Hook(可选) `presentation/hooks/useQuery.ts` ```ts import {useQuery, useMutation, useQueryClient} from '@tanstack/react-query'; // import {Repository} from '../../domain/repositories/Repository'; export const Keys = { all: [''] as const, lists: () => [...Keys.all, 'list'] as const, list: (filters: string) => [...Keys.lists(), {filters}] as const, details: () => [...Keys.all, 'detail'] as const, detail: (id: string) => [...Keys.details(), id] as const, }; export function useListQuery() { return useQuery({ queryKey: Keys.lists(), queryFn: async () => { // return Repository.getList(); return []; }, }); } export function useDetailQuery(id: string) { return useQuery({ queryKey: Keys.detail(id), queryFn: async () => { // return Repository.getById(id); return null; }, enabled: !!id, }); } ``` ### 5. Domain Entity `domain/entities/.ts` ```ts // Domain entity - pure TypeScript, no RN dependencies export type = { id: string; // other properties createdAt: Date; updatedAt: Date; }; export type CreateInput = Omit<, 'id' | 'createdAt' | 'updatedAt'>; export type UpdateInput = PartialInput>; ``` ### 6. Repository Interface `domain/repositories/Repository.ts` ```ts import type {, CreateInput, UpdateInput} from '../entities/'; export type Repository = { getAll(): Promise<[]>; getById(id: string): Promise< | null>; create(input: CreateInput): Promise<>; update(id: string, input: UpdateInput): Promise<>; delete(id: string): Promise; }; ``` ### 7. Remote DataSource `data/datasources/RemoteDataSource.ts` ```ts import type {, CreateInput, UpdateInput} from '../../domain/entities/'; // import {httpClient} from '@/core/network/httpClient'; export type RemoteDataSource = { fetchAll(): Promise<[]>; fetchById(id: string): Promise< | null>; create(input: CreateInput): Promise<>; update(id: string, input: UpdateInput): Promise<>; delete(id: string): Promise; }; export function createRemoteDataSource(): RemoteDataSource { const BASE_PATH = '/s'; return { async fetchAll() { // const response = await httpClient.get<{data: []}>(BASE_PATH); // return response.data.data; return []; }, async fetchById(id) { // const response = await httpClient.get<{data: }>(`${BASE_PATH}/${id}`); // return response.data.data; return null; }, async create(input) { // const response = await httpClient.post<{data: }>(BASE_PATH, input); // return response.data.data; throw new Error('Not implemented'); }, async update(id, input) { // const response = await httpClient.patch<{data: }>(`${BASE_PATH}/${id}`, input); // return response.data.data; throw new Error('Not implemented'); }, async delete(id) { // await httpClient.delete(`${BASE_PATH}/${id}`); }, }; } ``` ### 8. Repository Implementation `data/repositories/RepositoryImpl.ts` ```ts import type {Repository} from '../../domain/repositories/Repository'; import type {RemoteDataSource} from '../datasources/RemoteDataSource'; export function createRepository( remoteDataSource: RemoteDataSource ): Repository { return { async getAll() { return remoteDataSource.fetchAll(); }, async getById(id) { return remoteDataSource.fetchById(id); }, async create(input) { return remoteDataSource.create(input); }, async update(id, input) { return remoteDataSource.update(id, input); }, async delete(id) { return remoteDataSource.delete(id); }, }; } ``` --- ## 路由注册 ### 1. 添加路由常量 `app/navigation/routes.ts` ```ts export const AppRoutes = { // ... existing routes : '', } as const; export type RootStackParamList = { // ... existing params : undefined; // 或 { id: string } 等参数 }; ``` ### 2. 注册到 Navigator `app/navigation/RootNavigator.tsx` ```tsx import {Screen} from '@/features//presentation/screens/Screen'; // 在 Stack.Navigator 中添加 Screen} /> ``` --- ## 测试模板 ### Store 测试 `__tests__/features//presentation/useStore.test.ts` ```ts import {useStore} from '@/features//presentation/stores/useStore'; describe('useStore', () => { beforeEach(() => { useStore.getState().reset(); }); it('should initialize with default state', () => { const state = useStore.getState(); expect(state.isLoading).toBe(false); expect(state.error).toBeNull(); }); it('should update loading state', () => { useStore.getState().setLoading(true); expect(useStore.getState().isLoading).toBe(true); }); it('should reset to initial state', () => { useStore.getState().setLoading(true); useStore.getState().setError('test error'); useStore.getState().reset(); const state = useStore.getState(); expect(state.isLoading).toBe(false); expect(state.error).toBeNull(); }); }); ``` ### Screen 测试 `__tests__/features//presentation/Screen.test.tsx` ```tsx import React from 'react'; import {render, screen} from '@testing-library/react-native'; import {Screen} from '@/features//presentation/screens/Screen'; describe('Screen', () => { it('should render correctly', () => { render(<Screen />); expect(screen.getByText(//i)).toBeTruthy(); }); }); ``` --- ## 命名规范速查 | 类型 | 命名格式 | 文件名 | 示例 | | -------------- | ------------------------------- | ---------------------------------- | -------------------------------- | | Feature 目录 | `kebab-case` | - | `user-profile/` | | Screen | `Screen` | `Screen.tsx` | `UserProfileScreen.tsx` | | View | `View` | `View.tsx` | `UserProfileView.tsx` | | Store | `useStore` | `useStore.ts` | `useUserProfileStore.ts` | | Query Hook | `useQuery` | `useQuery.ts` | `useUserProfileQuery.ts` | | Entity | `` | `.ts` | `userProfile.ts` | | Repository | `Repository` | `Repository.ts` | `userProfileRepository.ts` | | DataSource | `RemoteDataSource` | `RemoteDataSource.ts` | `UserProfileRemoteDataSource.ts` | | RepositoryImpl | `RepositoryImpl` | `RepositoryImpl.ts` | `UserProfileRepositoryImpl.ts` | --- ## 分层依赖规则 ```text presentation ──► domain ◄── data │ │ │ │ │ │ ▼ │ ▼ core/* │ core/network (UI, theme) │ core/storage │ 纯 TypeScript (无 RN 依赖) ``` | 层级 | 可依赖 | 禁止依赖 | | --------------- | ------------------------------ | ------------------------------ | | `presentation/` | `domain/`、`core/`、React/RN | 其他 feature 的 store/组件 | | `domain/` | 纯 TypeScript 类型 | `react-native`、UI 包、`data/` | | `data/` | `domain/` 接口、`core/network` | `presentation/` | --- ## 子代理执行建议 创建 feature 时,建议通过子代理执行以确保一致性: ```typescript Task({ subagent_type: 'general-purpose', description: '创建 user-profile feature', prompt: `按照 .claude/skills/feature-generator/SKILL.md 创建完整的 user-profile feature,包含三层架构`, }); ``` --- ## Checklist 创建 Feature 时的检查清单: - [ ] 目录结构符合规范(`presentation/domain/data`) - [ ] 文件命名遵循约定(PascalCase/camelCase) - [ ] Store 使用 Zustand 模式 - [ ] Domain 层不依赖 React Native - [ ] 已注册路由(如需要) - [ ] 已创建基础测试文件 - [ ] 导出路径配置正确(tsconfig paths)