wednesday-dev
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWednesday Technical Development Guidelines
Wednesday 技术开发指南
This skill enforces code quality standards for Wednesday Solutions projects. Follow these guidelines to maintain consistency, readability, and maintainability across the codebase.
本指南为Wednesday Solutions项目强制执行代码质量标准。遵循这些准则可在整个代码库中保持一致性、可读性和可维护性。
1. Import Order
1. 导入排序
Imports must follow a strict ordering pattern. The project uses to enforce this automatically.
@trivago/prettier-plugin-sort-imports导入必须遵循严格的排序规则。项目使用自动强制执行该规则。
@trivago/prettier-plugin-sort-importsRequired Order
要求的排序顺序
typescript
// 1. React (always first)
import React, { useState, useEffect } from 'react'
// 2. Next.js imports
import { useRouter } from 'next/navigation'
import Image from 'next/image'
// 3. State management (Recoil, Redux)
import { useSelector } from 'react-redux'
// 4. UI libraries (MUI, Radix)
import { Button } from '@mui/material'
// 5. Path alias imports (@/)
import { useAuth } from '@/lib/hooks/useAuth'
import { API_ROUTES } from '@/lib/constants'
// 6. External packages
import { z } from 'zod'
import { motion } from 'framer-motion'
// 7. Internal components
import { Header } from 'components/Header'
// 8. Relative imports (sibling/parent)
import { utils } from './utils'
import { types } from '../types'typescript
// 1. React(始终排在第一位)
import React, { useState, useEffect } from 'react'
// 2. Next.js 导入
import { useRouter } from 'next/navigation'
import Image from 'next/image'
// 3. 状态管理(Recoil、Redux)
import { useSelector } from 'react-redux'
// 4. UI 库(MUI、Radix)
import { Button } from '@mui/material'
// 5. 路径别名导入(@/)
import { useAuth } from '@/lib/hooks/useAuth'
import { API_ROUTES } from '@/lib/constants'
// 6. 外部包
import { z } from 'zod'
import { motion } from 'framer-motion'
// 7. 内部组件
import { Header } from 'components/Header'
// 8. 相对导入(同级/父级)
import { utils } from './utils'
import { types } from '../types'Rules
规则
- Each import group must be separated by a blank line
- Imports within each group should be alphabetically sorted
- Use type imports for TypeScript types:
import type { User } from '@/types' - Avoid wildcard imports () unless necessary
import * as
- 每个导入组之间必须用空行分隔
- 每个组内的导入应按字母顺序排序
- TypeScript 类型使用类型导入:
import type { User } from '@/types' - 除非必要,否则避免使用通配符导入()
import * as
2. Code Complexity
2. 代码复杂度
Cyclomatic Complexity
圈复杂度
Maximum allowed: 8 (stricter than industry standard of 10)
Functions exceeding this limit must be refactored. See references/COMPLEXITY.md for detailed strategies.
最大允许值:8(比行业标准10更严格)
超过此限制的函数必须重构。详细策略请参阅references/COMPLEXITY.md。
Quick Remediation
快速整改方案
| Complexity | Action Required |
|---|---|
| 1-4 | Good - easy to test |
| 5-7 | Acceptable - consider simplifying |
| 8 | At limit - refactor if adding logic |
| 9+ | Must refactor before merging |
| 复杂度 | 所需操作 |
|---|---|
| 1-4 | 良好 - 易于测试 |
| 5-7 | 可接受 - 考虑简化 |
| 8 | 达到上限 - 若添加逻辑则需重构 |
| 9+ | 合并前必须重构 |
How to Reduce Complexity
如何降低复杂度
- Extract helper functions - Break large functions into smaller, focused ones
- Use early returns - Replace nested conditions with guard clauses
- Replace conditionals with polymorphism - Use strategy pattern for complex branching
- Simplify boolean expressions - Extract complex conditions into named variables
- Use lookup tables - Replace switch statements with object maps
typescript
// BAD: High complexity
function getDiscount(user: User, items: Item[]) {
let discount = 0
if (user.isPremium) {
if (items.length > 10) {
discount = 20
} else if (items.length > 5) {
discount = 10
} else {
discount = 5
}
} else {
if (items.length > 10) {
discount = 10
} else if (items.length > 5) {
discount = 5
}
}
return discount
}
// GOOD: Low complexity with lookup table
const DISCOUNT_TABLE = {
premium: { large: 20, medium: 10, small: 5 },
regular: { large: 10, medium: 5, small: 0 },
} as const
function getCartSize(count: number): 'large' | 'medium' | 'small' {
if (count > 10) return 'large'
if (count > 5) return 'medium'
return 'small'
}
function getDiscount(user: User, items: Item[]) {
const tier = user.isPremium ? 'premium' : 'regular'
const size = getCartSize(items.length)
return DISCOUNT_TABLE[tier][size]
}- 提取辅助函数 - 将大型函数拆分为更小、聚焦的函数
- 使用提前返回 - 用守卫子句替换嵌套条件
- 用多态替换条件语句 - 对复杂分支使用策略模式
- 简化布尔表达式 - 将复杂条件提取为命名变量
- 使用查找表 - 用对象映射替换switch语句
typescript
// 不良:高复杂度
function getDiscount(user: User, items: Item[]) {
let discount = 0
if (user.isPremium) {
if (items.length > 10) {
discount = 20
} else if (items.length > 5) {
discount = 10
} else {
discount = 5
}
} else {
if (items.length > 10) {
discount = 10
} else if (items.length > 5) {
discount = 5
}
}
return discount
}
// 良好:使用查找表实现低复杂度
const DISCOUNT_TABLE = {
premium: { large: 20, medium: 10, small: 5 },
regular: { large: 10, medium: 5, small: 0 },
} as const
function getCartSize(count: number): 'large' | 'medium' | 'small' {
if (count > 10) return 'large'
if (count > 5) return 'medium'
return 'small'
}
function getDiscount(user: User, items: Item[]) {
const tier = user.isPremium ? 'premium' : 'regular'
const size = getCartSize(items.length)
return DISCOUNT_TABLE[tier][size]
}File Size Limits
文件大小限制
- Max file lines: 250
- Max function lines: 350
- Max line length: 120 characters
- 最大文件行数:250
- 最大函数行数:350
- 最大行长度:120个字符
3. Naming Conventions
3. 命名规范
Components & Classes
组件与类
Use PascalCase for React components, classes, types, and interfaces.
typescript
// Components
function UserProfile() { }
function PaymentCard() { }
// Types & Interfaces
interface UserProfileProps { }
type PaymentMethod = 'card' | 'bank'
// Classes
class AuthService { }React组件、类、类型和接口使用**大驼峰式(PascalCase)**命名。
typescript
// 组件
function UserProfile() { }
function PaymentCard() { }
// 类型与接口
interface UserProfileProps { }
type PaymentMethod = 'card' | 'bank'
// 类
class AuthService { }Functions & Variables
函数与变量
Use camelCase for functions, variables, hooks, and object properties.
typescript
// Functions
function fetchUserData() { }
function calculateTotal() { }
// Variables
const isLoading = true
const userProfile = { }
// Hooks
function useAuth() { }
function useLocalStorage() { }函数、变量、Hook和对象属性使用**小驼峰式(camelCase)**命名。
typescript
// 函数
function fetchUserData() { }
function calculateTotal() { }
// 变量
const isLoading = true
const userProfile = { }
// Hooks
function useAuth() { }
function useLocalStorage() { }Constants & Enums
常量与枚举
Use UPPER_SNAKE_CASE for constants and enum values.
typescript
// Constants
const API_BASE_URL = 'https://api.example.com'
const MAX_RETRY_ATTEMPTS = 3
// Enums
enum UserRole {
ADMIN = 'ADMIN',
USER = 'USER',
GUEST = 'GUEST',
}常量和枚举值使用大写蛇形命名法(UPPER_SNAKE_CASE)。
typescript
// 常量
const API_BASE_URL = 'https://api.example.com'
const MAX_RETRY_ATTEMPTS = 3
// 枚举
enum UserRole {
ADMIN = 'ADMIN',
USER = 'USER',
GUEST = 'GUEST',
}Boolean Variables
布尔变量
Prefix with , , , , or for clarity.
ishasshouldcanwilltypescript
// Good
const isAuthenticated = true
const hasPermission = false
const shouldRefetch = true
const canEdit = user.role === 'ADMIN'
// Bad
const authenticated = true
const permission = false
const refetch = true前缀使用、、、或以提升可读性。
ishasshouldcanwilltypescript
// 良好
const isAuthenticated = true
const hasPermission = false
const shouldRefetch = true
const canEdit = user.role === 'ADMIN'
// 不良
const authenticated = true
const permission = false
const refetch = trueFiles & Folders
文件与文件夹
| Type | Convention | Example |
|---|---|---|
| Component files | PascalCase | |
| Hook files | camelCase with | |
| Utility files | camelCase | |
| Constant files | camelCase | |
| Type files | camelCase | |
| Test files | Match source + | |
| Folders | camelCase | |
| 类型 | 命名规范 | 示例 |
|---|---|---|
| 组件文件 | 大驼峰式 | |
| Hook文件 | 小驼峰式加 | |
| 工具文件 | 小驼峰式 | |
| 常量文件 | 小驼峰式 | |
| 类型文件 | 小驼峰式 | |
| 测试文件 | 与源文件名称一致加 | |
| 文件夹 | 小驼峰式 | |
Descriptive Naming
描述性命名
Names should be self-documenting:
typescript
// Bad - unclear intent
const d = new Date()
const arr = users.filter(u => u.a)
function proc(x: number) { }
// Good - clear intent
const currentDate = new Date()
const activeUsers = users.filter(user => user.isActive)
function processPayment(amount: number) { }名称应具备自文档性:
typescript
// 不良 - 意图不明确
const d = new Date()
const arr = users.filter(u => u.a)
function proc(x: number) { }
// 良好 - 意图清晰
const currentDate = new Date()
const activeUsers = users.filter(user => user.isActive)
function processPayment(amount: number) { }4. TypeScript Best Practices
4. TypeScript 最佳实践
Strict Type Safety
严格类型安全
- Enable strict mode in
tsconfig.json - Avoid type - use
anyand narrow typesunknown - Use explicit return types for public functions
- Leverage discriminated unions for state
typescript
// Use discriminated unions
type AsyncState<T> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; error: Error }
// Narrow types properly
function processValue(value: unknown) {
if (typeof value === 'string') {
return value.toUpperCase()
}
if (typeof value === 'number') {
return value.toFixed(2)
}
throw new Error('Unsupported type')
}- 在中启用严格模式
tsconfig.json - 避免使用类型 - 改用
any并收窄类型unknown - 公共函数使用显式返回类型
- 对状态使用可区分联合类型
typescript
// 使用可区分联合类型
type AsyncState<T> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; error: Error }
// 正确收窄类型
function processValue(value: unknown) {
if (typeof value === 'string') {
return value.toUpperCase()
}
if (typeof value === 'number') {
return value.toFixed(2)
}
throw new Error('Unsupported type')
}Type Imports
类型导入
Separate type imports from value imports:
typescript
import { useState } from 'react'
import type { FC, ReactNode } from 'react'
import { fetchUser } from '@/lib/api'
import type { User, UserRole } from '@/types'将类型导入与值导入分开:
typescript
import { useState } from 'react'
import type { FC, ReactNode } from 'react'
import { fetchUser } from '@/lib/api'
import type { User, UserRole } from '@/types'5. React Patterns
5. React 模式
Component Structure
组件结构
Follow this order within components:
- Type definitions (props interface)
- Component function declaration
- Hooks (useState, useEffect, custom hooks)
- Derived state / computations
- Event handlers
- Effects
- Return statement (JSX)
typescript
interface UserCardProps {
userId: string
onSelect?: (user: User) => void
}
export function UserCard({ userId, onSelect }: UserCardProps) {
// 1. Hooks
const [isExpanded, setIsExpanded] = useState(false)
const { data: user, isLoading } = useUser(userId)
// 2. Derived state
const displayName = user ? `${user.firstName} ${user.lastName}` : 'Unknown'
// 3. Event handlers
const handleClick = () => {
setIsExpanded(!isExpanded)
onSelect?.(user)
}
// 4. Early returns for loading/error states
if (isLoading) return <Skeleton />
if (!user) return null
// 5. Main render
return (
<Card onClick={handleClick}>
<CardTitle>{displayName}</CardTitle>
{isExpanded && <CardContent>{user.bio}</CardContent>}
</Card>
)
}组件内遵循以下顺序:
- 类型定义(props接口)
- 组件函数声明
- Hooks(useState、useEffect、自定义Hooks)
- 派生状态/计算逻辑
- 事件处理函数
- 副作用
- 返回语句(JSX)
typescript
interface UserCardProps {
userId: string
onSelect?: (user: User) => void
}
export function UserCard({ userId, onSelect }: UserCardProps) {
// 1. Hooks
const [isExpanded, setIsExpanded] = useState(false)
const { data: user, isLoading } = useUser(userId)
// 2. 派生状态
const displayName = user ? `${user.firstName} ${user.lastName}` : 'Unknown'
// 3. 事件处理函数
const handleClick = () => {
setIsExpanded(!isExpanded)
onSelect?.(user)
}
// 4. 加载/错误状态的提前返回
if (isLoading) return <Skeleton />
if (!user) return null
// 5. 主渲染逻辑
return (
<Card onClick={handleClick}>
<CardTitle>{displayName}</CardTitle>
{isExpanded && <CardContent>{user.bio}</CardContent>}
</Card>
)
}Use Client Directive
Use Client 指令
Mark client components explicitly:
typescript
'use client'
import { useState } from 'react'
// ... client-side component显式标记客户端组件:
typescript
'use client'
import { useState } from 'react'
// ... 客户端组件6. Forbidden Patterns
6. 禁用模式
No Console Statements
禁止使用控制台语句
Console statements are forbidden in production code. Use proper logging utilities.
typescript
// Forbidden
console.log('debug:', data)
console.error('Error:', err)
// Use structured logging or remove before commit生产代码中禁止使用控制台语句,请使用正规的日志工具。
typescript
// 禁用
console.log('debug:', data)
console.error('Error:', err)
// 使用结构化日志或在提交前移除No Magic Numbers/Strings
禁止使用魔法数字/字符串
Extract to named constants:
typescript
// Bad
if (retryCount > 3) { }
if (status === 'active') { }
// Good
const MAX_RETRIES = 3
const STATUS_ACTIVE = 'active'
if (retryCount > MAX_RETRIES) { }
if (status === STATUS_ACTIVE) { }提取为命名常量:
typescript
// 不良
if (retryCount > 3) { }
if (status === 'active') { }
// 良好
const MAX_RETRIES = 3
const STATUS_ACTIVE = 'active'
if (retryCount > MAX_RETRIES) { }
if (status === STATUS_ACTIVE) { }No Unused Code
禁止未使用代码
- Remove unused imports (enforced by )
eslint-plugin-unused-imports - Delete commented-out code
- Remove unused variables (prefix with only if intentionally unused)
_
- 移除未使用的导入(由强制执行)
eslint-plugin-unused-imports - 删除注释掉的代码
- 移除未使用的变量(仅在有意不使用时添加前缀)
_
7. Testing Requirements
7. 测试要求
Unit Tests
单元测试
- Use Jest with React Testing Library
- Test file naming:
ComponentName.test.tsx - Focus on user behavior, not implementation details
typescript
import { render, screen, fireEvent } from '@testing-library/react'
import { UserCard } from './UserCard'
describe('UserCard', () => {
it('displays user name correctly', () => {
render(<UserCard userId="123" />)
expect(screen.getByText('John Doe')).toBeInTheDocument()
})
it('calls onSelect when clicked', () => {
const onSelect = jest.fn()
render(<UserCard userId="123" onSelect={onSelect} />)
fireEvent.click(screen.getByRole('button'))
expect(onSelect).toHaveBeenCalledWith(expect.objectContaining({ id: '123' }))
})
})- 使用Jest和React Testing Library
- 测试文件命名:
ComponentName.test.tsx - 聚焦于用户行为而非实现细节
typescript
import { render, screen, fireEvent } from '@testing-library/react'
import { UserCard } from './UserCard'
describe('UserCard', () => {
it('正确显示用户名', () => {
render(<UserCard userId="123" />)
expect(screen.getByText('John Doe')).toBeInTheDocument()
})
it('点击时调用onSelect', () => {
const onSelect = jest.fn()
render(<UserCard userId="123" onSelect={onSelect} />)
fireEvent.click(screen.getByRole('button'))
expect(onSelect).toHaveBeenCalledWith(expect.objectContaining({ id: '123' }))
})
})E2E Tests
端到端(E2E)测试
- Use Playwright for end-to-end tests
- Test file naming:
feature.spec.ts - Test critical user flows
- 使用Playwright进行端到端测试
- 测试文件命名:
feature.spec.ts - 测试关键用户流程
8. Security Considerations
8. 安全注意事项
- Sanitize HTML with DOMPurify before rendering
- Validate all user inputs with Zod schemas
- Never expose sensitive data in client-side code
- Use environment variables for secrets
- Implement CSRF protection for forms
- 渲染前使用DOMPurify清理HTML
- 使用Zod校验所有用户输入
- 绝不在客户端代码中暴露敏感数据
- 使用环境变量存储密钥
- 为表单实现CSRF防护
9. Performance Guidelines
9. 性能指南
- Use for code splitting
next/dynamic - Implement proper loading states
- Memoize expensive computations with
useMemo - Avoid unnecessary re-renders with
React.memo - Use image optimization with
next/image
For detailed examples and edge cases, see references/COMPLEXITY.md.
- 使用进行代码分割
next/dynamic - 实现合理的加载状态
- 使用缓存昂贵的计算
useMemo - 使用避免不必要的重渲染
React.memo - 使用优化图片
next/image
有关详细示例和边缘情况,请参阅references/COMPLEXITY.md。