wednesday-dev

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Wednesday 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
@trivago/prettier-plugin-sort-imports
to enforce this automatically.
导入必须遵循严格的排序规则。项目使用
@trivago/prettier-plugin-sort-imports
自动强制执行该规则。

Required 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 (
    import * as
    ) unless necessary
  • 每个导入组之间必须用空行分隔
  • 每个组内的导入应按字母顺序排序
  • 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

快速整改方案

ComplexityAction Required
1-4Good - easy to test
5-7Acceptable - consider simplifying
8At limit - refactor if adding logic
9+Must refactor before merging
复杂度所需操作
1-4良好 - 易于测试
5-7可接受 - 考虑简化
8达到上限 - 若添加逻辑则需重构
9+合并前必须重构

How to Reduce Complexity

如何降低复杂度

  1. Extract helper functions - Break large functions into smaller, focused ones
  2. Use early returns - Replace nested conditions with guard clauses
  3. Replace conditionals with polymorphism - Use strategy pattern for complex branching
  4. Simplify boolean expressions - Extract complex conditions into named variables
  5. 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]
}
  1. 提取辅助函数 - 将大型函数拆分为更小、聚焦的函数
  2. 使用提前返回 - 用守卫子句替换嵌套条件
  3. 用多态替换条件语句 - 对复杂分支使用策略模式
  4. 简化布尔表达式 - 将复杂条件提取为命名变量
  5. 使用查找表 - 用对象映射替换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
is
,
has
,
should
,
can
, or
will
for clarity.
typescript
// 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
前缀使用
is
has
should
can
will
以提升可读性。
typescript
// 良好
const isAuthenticated = true
const hasPermission = false
const shouldRefetch = true
const canEdit = user.role === 'ADMIN'

// 不良
const authenticated = true
const permission = false
const refetch = true

Files & Folders

文件与文件夹

TypeConventionExample
Component filesPascalCase
UserProfile.tsx
Hook filescamelCase with
use
prefix
useAuth.ts
Utility filescamelCase
formatDate.ts
Constant filescamelCase
apiRoutes.ts
Type filescamelCase
user.types.ts
Test filesMatch source +
.test
UserProfile.test.tsx
FolderscamelCase
components/
,
hooks/
类型命名规范示例
组件文件大驼峰式
UserProfile.tsx
Hook文件小驼峰式加
use
前缀
useAuth.ts
工具文件小驼峰式
formatDate.ts
常量文件小驼峰式
apiRoutes.ts
类型文件小驼峰式
user.types.ts
测试文件与源文件名称一致加
.test
UserProfile.test.tsx
文件夹小驼峰式
components/
,
hooks/

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
    any
    type - use
    unknown
    and narrow types
  • 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:
  1. Type definitions (props interface)
  2. Component function declaration
  3. Hooks (useState, useEffect, custom hooks)
  4. Derived state / computations
  5. Event handlers
  6. Effects
  7. 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>
  )
}
组件内遵循以下顺序:
  1. 类型定义(props接口)
  2. 组件函数声明
  3. Hooks(useState、useEffect、自定义Hooks)
  4. 派生状态/计算逻辑
  5. 事件处理函数
  6. 副作用
  7. 返回语句(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
    next/dynamic
    for code splitting
  • 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