UNPKG

autosnippet

Version:

Extract code patterns into a knowledge base for AI coding assistants

229 lines (228 loc) 9.95 kB
/** * React Enhancement Pack * 条件: { languages: ['typescript', 'javascript'], frameworks: ['react', 'nextjs'] } * * 覆盖 React 19 生态: * - Server / Client Components (RSC) * - Custom Hooks + Hook 组合模式 * - 组件结构约定 * - 状态管理模式 (Context / Zustand / Jotai) * - Suspense / ErrorBoundary */ import { EnhancementPack } from './EnhancementPack.js'; class ReactEnhancement extends EnhancementPack { get id() { return 'react'; } get displayName() { return 'React 19 Enhancement'; } get conditions() { return { languages: ['typescript', 'javascript'], frameworks: ['react', 'nextjs'] }; } getExtraDimensions() { return [ { id: 'hook-pattern-scan', label: '自定義 Hook 分析', guide: '自定义 Hook 提取(useXxx 函数 + 内部状态/副作用分析)、Hook 组合模式、Hook 依赖关系、useCallback/useMemo 使用合理性', tierHint: 2, knowledgeTypes: ['code-pattern'], skillWorthy: true, dualOutput: true, skillMeta: { name: 'project-react-hooks', description: 'Custom React Hook patterns and composition (auto-generated by enhancement)', }, }, { id: 'component-structure-scan', label: '组件结构约定', guide: '组件目录结构约定(文件组织、barrel export、props/state 命名规范、forwardRef 使用、组件拆分粒度)', tierHint: 2, knowledgeTypes: ['code-standard', 'architecture'], skillWorthy: true, dualOutput: true, skillMeta: { name: 'project-react-components', description: 'React component structure conventions (auto-generated by enhancement)', }, }, { id: 'rsc-boundary-scan', label: 'Server/Client 边界分析', guide: 'React Server Components 分析 — "use client" / "use server" 指令分布、Server → Client 数据传递边界、Serializable props 约束、async Server Component 模式', tierHint: 2, knowledgeTypes: ['architecture', 'code-pattern'], skillWorthy: true, dualOutput: true, skillMeta: { name: 'project-react-rsc', description: 'React Server Component boundaries, directives and data flow (auto-generated by enhancement)', }, }, { id: 'state-management-scan', label: '状态管理分析', guide: '状态管理模式分析 — Context Provider 层级、Zustand/Jotai/Redux store 结构、useReducer 有限状态机、状态提升 vs 组合', tierHint: 2, knowledgeTypes: ['architecture', 'code-pattern'], skillWorthy: true, dualOutput: true, skillMeta: { name: 'project-react-state', description: 'React state management topology — Context/Zustand/Redux patterns (auto-generated by enhancement)', }, }, ]; } getGuardRules() { return [ { ruleId: 'react-no-direct-dom', category: 'correctness', dimension: 'file', severity: 'warning', languages: ['typescript', 'javascript'], pattern: /document\.(getElementById|querySelector|getElementsBy)/, message: '避免直接 DOM 操作,使用 React ref 或状态管理', }, { ruleId: 'react-no-index-key', category: 'correctness', dimension: 'file', severity: 'warning', languages: ['typescript', 'javascript'], pattern: /\.map\s*\([^)]*,\s*(?:index|i|idx)\s*\)\s*=>\s*[\s\S]*?key\s*=\s*\{?\s*(?:index|i|idx)/, message: '避免使用 index 作为 key,使用稳定的唯一标识符', }, { ruleId: 'react-hooks-conditional', category: 'correctness', dimension: 'file', severity: 'error', languages: ['typescript', 'javascript'], pattern: /if\s*\([^)]*\)\s*\{[^}]*\buse[A-Z]\w*\s*\(/, message: 'Hook 不能在条件语句中调用 — 违反 Rules of Hooks(Hook 必须在组件顶层调用)', }, { ruleId: 'react-useeffect-missing-cleanup', category: 'correctness', dimension: 'file', severity: 'info', languages: ['typescript', 'javascript'], pattern: /useEffect\s*\(\s*\(\)\s*=>\s*\{[^}]*(?:addEventListener|setInterval|setTimeout|subscribe)[^}]*\}\s*,/, message: 'useEffect 中注册了事件/订阅/定时器但可能缺少清理函数,会导致内存泄漏', }, { ruleId: 'react-no-setState-in-render', category: 'correctness', dimension: 'file', severity: 'error', languages: ['typescript', 'javascript'], pattern: /(?:^|\n)\s*(?:const|let)\s+\[.*?,\s*set\w+\]\s*=\s*useState[\s\S]*?\n\s*set\w+\(/m, message: '避免在渲染阶段直接调用 setState — 会导致无限重渲染', }, { ruleId: 'react-exhaustive-deps', category: 'correctness', dimension: 'file', severity: 'warning', languages: ['typescript', 'javascript'], pattern: /\/\/\s*eslint-disable.*exhaustive-deps/, message: '不要禁用 exhaustive-deps 规则,修复依赖数组以避免 stale closure', }, { ruleId: 'react-use-client-server-mixing', category: 'correctness', dimension: 'file', severity: 'warning', languages: ['typescript', 'javascript'], pattern: /['"]use client['"]\s*;?\s*\n[\s\S]*?['"]use server['"]/, message: '同一文件不能同时包含 "use client" 和 "use server" 指令', }, ]; } detectPatterns(astSummary) { const patterns = []; // ── Custom Hooks ── for (const m of astSummary.methods || []) { if (m.name && /^use[A-Z]/.test(m.name) && !m.className) { patterns.push({ type: 'custom-hook', methodName: m.name, line: m.line, confidence: 0.9 }); } } // ── HOC: function withXxx(Component) ── for (const m of astSummary.methods || []) { if (m.name && /^with[A-Z]/.test(m.name) && !m.className) { patterns.push({ type: 'hoc', methodName: m.name, line: m.line, confidence: 0.8 }); } } // ── Context Providers ── for (const cls of astSummary.classes || []) { const nameLower = cls.name?.toLowerCase() || ''; if (nameLower.includes('provider') || nameLower.includes('context')) { patterns.push({ type: 'react-context-provider', className: cls.name, line: cls.line, confidence: 0.8, }); } } // ── forwardRef components ── for (const m of astSummary.methods || []) { if (m.name && !m.className) { // 导出的组件函数(大写开头) if (/^[A-Z]/.test(m.name)) { patterns.push({ type: 'react-component', methodName: m.name, line: m.line, confidence: 0.7, }); } } } // ── Error Boundary classes ── for (const cls of astSummary.classes || []) { if (cls.superclass && /Component/.test(cls.superclass)) { const methods = cls.methods || []; if (methods.includes('componentDidCatch') || methods.includes('getDerivedStateFromError')) { patterns.push({ type: 'react-error-boundary', className: cls.name, line: cls.line, confidence: 0.95, }); } } } // ── Zustand/Redux stores ── for (const m of astSummary.methods || []) { if (m.name && /^use\w*Store$/.test(m.name) && !m.className) { patterns.push({ type: 'react-store', methodName: m.name, line: m.line, confidence: 0.85, }); } } // ── React imports ── const reactImports = (astSummary.imports || []).filter((imp) => imp.includes('react') || imp.includes('zustand') || imp.includes('jotai') || imp.includes('@reduxjs') || imp.includes('react-query') || imp.includes('@tanstack')); if (reactImports.length > 0) { patterns.push({ type: 'react-ecosystem-usage', importCount: reactImports.length, confidence: 0.85, }); } return patterns; } } export const pack = new ReactEnhancement();