コード規約
Elftia は厳格な ESLint 設定(typescript-eslint/strict)を使用し、段階的な適用戦略を採用しています。
ツールチェーン
| ツール | 目的 | 設定ファイル |
|---|---|---|
| ESLint | コード品質チェック(strict モード) | eslint.config.js |
| Prettier | コードフォーマット | .prettierrc |
| Husky | Git Hooks 管理 | .husky/pre-commit |
| lint-staged | ステージング済みファイルのチェック | package.json |
よく使うコマンド
# Run ESLint checks
npm run lint:eslint
# Check Prettier formatting
npm run lint:prettier
# Auto-format code
npm run format
# Complete lint check (ESLint + TypeScript)
npm run lint
# Check a single file
npx eslint path/to/file.ts
# Auto-fix a single file
npx eslint path/to/file.ts --fix
コミット前の自動チェック
コードをコミットするとき、Husky はステージング済みの .ts/.tsx ファイルに対して eslint --fix 付きで lint-staged を自動実行します。
- error レベル: コミットをブロックします。修正が必須です
- warn レベル: 警告を表示し、コミットは許可します
- auto-fix: import のソートなど、修正可能な問題を自動修正します
ESLint ルール早見表
重大度レベル
| レベル | 意味 | コミット時の動作 |
|---|---|---|
error | 修正必須 | コミットをブロック |
warn | 修正推奨 | コミットを許可 |
off | 無効 | - |
Error レベルのルール(コミットをブロック)
// simple-import-sort/imports — Auto-fixable
// Imports must be ordered as specified, eslint --fix will auto-fix
// prefer-const
let value = 1; // Never reassigned, should use const
// no-var
var x = 1; // var usage is prohibited
// eqeqeq
if (a == b) { } // Use == instead of === (except for null checks)
// react-hooks/rules-of-hooks
if (condition) { useState(); } // Hooks can only be called at top level
Warn レベルのルール(コミットを許可)
TypeScript ルール
| ルール | 説明 | 修正方法 |
|---|---|---|
@typescript-eslint/no-unused-vars | 未使用の変数 | 削除するか _ を接頭辞として付ける |
@typescript-eslint/no-explicit-any | any 型の使用 | 具体的な型または unknown に変更 |
@typescript-eslint/consistent-type-imports | type-only ではない import | import { type Foo } に変更 |
@typescript-eslint/no-non-null-assertion | 非 null アサーション ! | オプショナルチェーン ?. または型ガードを使用 |
React ルール
| ルール | 説明 | 修正方法 |
|---|---|---|
react-hooks/exhaustive-deps | Hook の依存関係が不完全 | 依存配列を完全にする |
react/jsx-no-leaked-render | 漏れた render(count && <X />) | count > 0 && <X /> に変更 |
react/self-closing-comp | 空のタグが自己終了していない | <Comp></Comp> → <Comp /> |
react/jsx-curly-brace-presence | 不要な波括弧 | prop={"val"} → prop="val" |
react/jsx-boolean-value | 冗長な boolean 値 | disabled={true} → disabled |
react/hook-use-state | state 名が camelCase ではない | [Value, setV] → [value, setV] |
その他のルール
| ルール | 説明 | 修正方法 |
|---|---|---|
no-console | Renderer プロセスで console.log を使用 | console.warn/error/info/debug に変更 |
jsx-a11y/* | アクセシビリティの問題 | デザインシステム を参照 |
Import ソートルール
import 文は次の順序で並べる必要があります(eslint --fix で自動修正できます)。
// 1. Node.js built-in modules
import path from 'node:path';
import fs from 'node:fs';
// 2. External packages
import React, { useState } from 'react';
import { useQuery } from '@tanstack/react-query';
// 3. Internal alias @/
import { Button } from '@/components/ui/button';
import { useAppState } from '@/shared/hooks/useAppState';
// 4. @shared/ alias
import type { Message } from '@shared/contracts';
// 5. @main/ alias
import { AppPaths } from '@main/services/infra/paths/paths';
// 6. Parent directory imports
import { utils } from '../utils';
// 7. Same directory imports
import { helper } from './helper';
// 8. Style imports
import './styles.css';
Prettier 設定
| オプション | 値 | 説明 |
|---|---|---|
semi | true | セミコロンを使用 |
singleQuote | true | シングルクォートを使用 |
tabWidth | 2 | インデント幅 |
trailingComma | 'es5' | 末尾のカンマ |
printWidth | 100 | 行幅の制限 |
ファイルサイズ制限
黄金律: 1 つのファイルは 600 行のコードを超えないようにしてください。
| ファイル種別 | 推奨上限 | 警告ライン | ハード上限 |
|---|---|---|---|
| React コンポーネント | 400 行 | 600 行 | 800 行 |
| カスタム Hook | 300 行 | 400 行 | 600 行 |
| ユーティリティ/ヘルパー関数 | 150 行 | 200 行 | 300 行 |
| 型定義 | 100 行 | 150 行 | 200 行 |
| サービス/API | 300 行 | 400 行 | 600 行 |
結果:
- 警告ラインを超過 → 現在のタスク内で分割する必要があります。先送りしないでください
- ハード上限を超過 → すぐに停止し、先にリファクタリングしてから続行してください
命名規則
ファイル命名
| 種別 | ルール | 例 |
|---|---|---|
| React コンポーネント | PascalCase.tsx | UserMessage.tsx |
| Hooks | use + PascalCase.ts | useChatActions.ts |
| ユーティリティ関数 | camelCase.ts | messageHelpers.ts |
| 型定義 | camelCase.ts または types.ts | chatTypes.ts |
| 定数 | UPPER_CASE.ts | API_CONSTANTS.ts |
| サービス | PascalCase + Service.ts | ChatService.ts |
| ディレクトリ | kebab-case | chat-messages/ |
コンポーネント命名
// Good: Clear and descriptive
export function UserMessage() { }
export function EditTool() { }
export function ChatComposer() { }
// Bad: Too generic
export function Message() { }
export function Tool() { }
export function Input() { }
Hook 命名
// Good: use + feature description
export function useChatActions() { }
export function useMessageEffects() { }
// Bad: Does not follow React Hook naming convention
export function chatActions() { }
export function messageHook() { }
ディレクトリ構造規約
コンポーネントディレクトリ
components/
├── ComponentName/
│ ├── index.ts # Unified export
│ ├── ComponentName.tsx # Main component
│ ├── types.ts # Type definitions
│ ├── utils.ts # Utility functions
│ ├── SubComponent.tsx # Sub-component
│ └── __tests__/
│ └── ComponentName.test.tsx
Hooks ディレクトリ
hooks/
├── domain/
│ ├── index.ts # Unified export
│ ├── useDomainState.ts # State
│ ├── useDomainActions.ts # Actions
│ └── useDomainEffects.ts # Side effects
Services ディレクトリ
services/
├── ServiceName/
│ ├── index.ts # Unified export
│ ├── ServiceName.ts # Main service
│ ├── types.ts # Type definitions
│ └── subdomain/ # Sub-domain
TypeScript ベストプラクティス
Type import
// Use type-only imports
import { type SomeType } from './types';
import type { AnotherType } from '@shared/contracts';
any を避ける
// Bad
function handle(data: any) { }
// Good: Use specific types
function handle(data: Message) { }
// Good: Use generics
function handle<T extends BaseMessage>(data: T) { }
// Good: Use unknown + type guard
function handle(data: unknown) {
if (isMessage(data)) { /* data: Message */ }
}
Barrel export
// components/chat/messages/index.ts
export { MessageItem } from './MessageItem';
export { MessageList } from './MessageList';
export type { MessageItemProps } from './MessageItem';
コード整理の順序
React コンポーネント構造
// 1. Imports
// 2. Type definitions
// 3. Constants
// 4. Helper functions
// 5. Main component
// 5.1 Hooks
// 5.2 Derived state (useMemo)
// 5.3 Event handlers (useCallback)
// 5.4 Effects (useEffect)
// 5.5 Render (return)
// 6. Sub-components (if simple)
単一責任の原則
各ファイルの変更理由は 1 つだけであるべきです。コンポーネントは次の場合に分割してください。
- コードが 300 行を超える
- 独立したロジックブロックを 3 つ以上含む
- 再利用可能な部分がある
- 異なる部分が異なる頻度で変更される
- テストが難しくなる
分割戦略:
| 戦略 | 使用するタイミング | 例 |
|---|---|---|
| 機能別に分割 | UI ブロックが独立している | Toolbar / Message list / Input box → separate components |
| データフロー別に分割 | 状態ロジックが複雑 | State / Actions / Side effects → separate Hooks |
| ドメイン別に分割 | 類似する項目が複数ある | EditTool / WriteTool / BashTool → separate files |
Git ワークフロー
コミット規約
Conventional Commits 形式を使用してください。
feat: New feature description
fix: Bug fix description
refactor: Refactoring description
docs: Documentation update
style: Code formatting changes (no logic changes)
test: Test-related changes
chore: Build tool or auxiliary tool changes
コミット前チェックリスト
npm run lintを実行 — エラーがないことを確認- error レベルの問題をすべて修正
- 可能な場合は warn レベルの問題を修正
npm run formatを実行 — コードをフォーマット- 新規ファイルの警告は 0 件にしてください
- 既存ファイルを変更するときは、そのファイル内の警告もあわせて修正してください