mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-05-18 14:53:05 +08:00
docs: add native Japanese translation of ECC documentation (ja-JP)
Translate everything-claude-code repository to Japanese including: - 17 root documentation files - 60 agent documentation files - 80 command documentation files - 99 rule files across 18 language directories (common, angular, arkts, cpp, csharp, dart, fsharp, golang, java, kotlin, perl, php, python, ruby, rust, swift, typescript, web) - 199 skill documentation files Total: 455 files translated to Japanese with: - Consistent terminology glossary applied throughout - YAML field names preserved in English (name, description, etc.) - Code blocks and examples untouched (comments translated) - Markdown structure and relative links preserved - Professional translation maintaining technical accuracy This translation expands ECC accessibility to Japanese-speaking developers and teams. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
182
docs/ja-JP/rules/angular/coding-style.md
Normal file
182
docs/ja-JP/rules/angular/coding-style.md
Normal file
@@ -0,0 +1,182 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.component.ts"
|
||||
- "**/*.component.html"
|
||||
- "**/*.service.ts"
|
||||
- "**/*.directive.ts"
|
||||
- "**/*.pipe.ts"
|
||||
- "**/*.guard.ts"
|
||||
- "**/*.resolver.ts"
|
||||
- "**/*.module.ts"
|
||||
---
|
||||
# Angular コーディングスタイル
|
||||
|
||||
> このファイルは [common/coding-style.md](../common/coding-style.md) を Angular 固有のコンテンツで拡張します。
|
||||
|
||||
## バージョンの確認
|
||||
|
||||
コードを書く前に、必ずプロジェクトの Angular バージョンを確認してください — バージョン間で機能が大きく異なります。`ng version` を実行するか、`package.json` を確認してください。新しいプロジェクトを作成する場合、ユーザーが指定しない限りバージョンを固定しないでください。
|
||||
|
||||
Angular コードを生成または変更した後は、完了前に必ず `ng build` を実行してエラーを検出してください。
|
||||
|
||||
## ファイル命名
|
||||
|
||||
Angular CLI の規約に従い、1ファイルにつき1つの成果物を配置します:
|
||||
|
||||
- `user-profile.component.ts` + `user-profile.component.html` + `user-profile.component.spec.ts`
|
||||
- `user.service.ts`、`auth.guard.ts`、`date-format.pipe.ts`
|
||||
- 機能フォルダ: `features/users/`、`features/auth/`
|
||||
- CLI で生成: `ng generate component features/users/user-card`
|
||||
|
||||
## コンポーネント
|
||||
|
||||
スタンドアロンコンポーネント(v17+ デフォルト)を優先します。すべての新しいコンポーネントで `OnPush` 変更検知を使用してください。
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
selector: 'app-user-card',
|
||||
standalone: true,
|
||||
imports: [RouterModule],
|
||||
templateUrl: './user-card.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class UserCardComponent {
|
||||
user = input.required<User>();
|
||||
select = output<string>();
|
||||
}
|
||||
```
|
||||
|
||||
## 依存性注入
|
||||
|
||||
コンストラクタ注入よりも `inject()` を使用してください。コンストラクタは空にするか、完全に削除してください。
|
||||
|
||||
```typescript
|
||||
// 正しい
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class UserService {
|
||||
private http = inject(HttpClient);
|
||||
private router = inject(Router);
|
||||
}
|
||||
|
||||
// 誤り: コンストラクタ注入は冗長で、ツリーシェイキングが困難
|
||||
constructor(private http: HttpClient, private router: Router) {}
|
||||
```
|
||||
|
||||
非クラス依存関係には `InjectionToken` を使用してください:
|
||||
|
||||
```typescript
|
||||
const API_URL = new InjectionToken<string>('API_URL');
|
||||
|
||||
// 提供:
|
||||
{ provide: API_URL, useValue: 'https://api.example.com' }
|
||||
|
||||
// 使用:
|
||||
private apiUrl = inject(API_URL);
|
||||
```
|
||||
|
||||
## シグナル
|
||||
|
||||
### 基本プリミティブ
|
||||
|
||||
```typescript
|
||||
count = signal(0);
|
||||
doubled = computed(() => this.count() * 2);
|
||||
|
||||
increment() {
|
||||
this.count.update(n => n + 1);
|
||||
}
|
||||
```
|
||||
|
||||
### `linkedSignal` — 書き込み可能な派生状態
|
||||
|
||||
ソースが変更されたときにリセットまたは適応する必要があるが、独立して書き込み可能なシグナルには `linkedSignal` を使用してください:
|
||||
|
||||
```typescript
|
||||
selectedOption = linkedSignal(() => this.options()[0]);
|
||||
// options が変更されると最初のオプションにリセットされるが、ユーザーはオーバーライド可能
|
||||
```
|
||||
|
||||
### `resource` — 非同期データをシグナルに変換
|
||||
|
||||
手動サブスクリプションなしで非同期データをリアクティブに取得するには `resource()` を使用してください:
|
||||
|
||||
```typescript
|
||||
userResource = resource({
|
||||
request: () => ({ id: this.userId() }),
|
||||
loader: ({ request }) => fetch(`/api/users/${request.id}`).then(r => r.json()),
|
||||
});
|
||||
|
||||
// アクセス: userResource.value(), userResource.isLoading(), userResource.error()
|
||||
```
|
||||
|
||||
### `effect` の使用法
|
||||
|
||||
`effect()` はシグナルの変更に反応する必要がある副作用(ログ記録、サードパーティの DOM 操作)にのみ使用してください。シグナルの同期にエフェクトを使用しないでください — 代わりに `computed` または `linkedSignal` を使用してください。レンダリング後の DOM 作業には `afterRenderEffect` を使用してください。
|
||||
|
||||
```typescript
|
||||
// 正しい: 副作用
|
||||
effect(() => console.log('User changed:', this.user()));
|
||||
|
||||
// 誤り: 代わりに computed を使用
|
||||
effect(() => { this.fullName.set(`${this.first()} ${this.last()}`); });
|
||||
```
|
||||
|
||||
## テンプレート
|
||||
|
||||
v17+ のブロック構文を使用してください。`@for` では必ず `track` を指定してください:
|
||||
|
||||
```html
|
||||
@for (item of items(); track item.id) {
|
||||
<app-item [item]="item" />
|
||||
}
|
||||
|
||||
@if (isLoading()) {
|
||||
<app-spinner />
|
||||
} @else if (error()) {
|
||||
<app-error [message]="error()" />
|
||||
} @else {
|
||||
<app-content [data]="data()" />
|
||||
}
|
||||
```
|
||||
|
||||
テンプレート内のロジックは単純な条件式に留め、コンポーネントメソッドまたはパイプに移動してください。
|
||||
|
||||
## フォーム
|
||||
|
||||
プロジェクトの既存アプローチに合ったフォーム戦略を選択してください:
|
||||
|
||||
- **Signal Forms**(v21+): v21+ の新規プロジェクトで推奨。シグナルベースのフォーム状態。
|
||||
- **Reactive Forms**: `FormBuilder` + `FormGroup` + `FormControl`。動的バリデーションを持つ複雑なフォームに最適。
|
||||
- **Template-Driven Forms**: `ngModel`。シンプルなフォームにのみ適しています。
|
||||
|
||||
```typescript
|
||||
// Reactive Forms — ほとんどのアプリの標準的なアプローチ
|
||||
export class LoginComponent {
|
||||
private fb = inject(FormBuilder);
|
||||
|
||||
form = this.fb.group({
|
||||
email: ['', [Validators.required, Validators.email]],
|
||||
password: ['', [Validators.required, Validators.minLength(8)]],
|
||||
});
|
||||
|
||||
submit() {
|
||||
if (this.form.valid) {
|
||||
// this.form.value を使用
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## コンポーネントスタイル
|
||||
|
||||
`ViewEncapsulation.Emulated`(デフォルト)でコンポーネントレベルのスタイルを使用してください。意図的にスタイルを漏洩させるデザインシステムを構築する場合を除き、`ViewEncapsulation.None` を避けてください。
|
||||
|
||||
- スタイルをコンポーネントにスコープし、コンポーネントスタイルシート内でグローバルクラス名を使用しない
|
||||
- ホスト要素のスタイリングには `:host` を使用
|
||||
- テーマ設定可能な値には CSS カスタムプロパティを優先
|
||||
|
||||
## 変更検知
|
||||
|
||||
- すべての新しいコンポーネントでデフォルトとして `ChangeDetectionStrategy.OnPush` を使用
|
||||
- シグナルと `async` パイプが検知を自動的に処理 — `markForCheck()` と `detectChanges()` を避ける
|
||||
- OnPush 使用時に `@Input()` オブジェクトをインプレースで変更しない
|
||||
25
docs/ja-JP/rules/angular/hooks.md
Normal file
25
docs/ja-JP/rules/angular/hooks.md
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.component.ts"
|
||||
- "**/*.component.html"
|
||||
- "**/*.service.ts"
|
||||
- "**/*.directive.ts"
|
||||
- "**/*.pipe.ts"
|
||||
- "**/*.spec.ts"
|
||||
---
|
||||
# Angular フック
|
||||
|
||||
> このファイルは [common/hooks.md](../common/hooks.md) を Angular 固有のコンテンツで拡張します。
|
||||
|
||||
## PostToolUse フック
|
||||
|
||||
`~/.claude/settings.json` で設定してください:
|
||||
|
||||
- **Prettier**: 編集後に `.ts` と `.html` ファイルを自動フォーマット
|
||||
- **ESLint / ng lint**: Angular ソースファイルの編集後に `ng lint` を実行し、デコレータの誤用、テンプレートエラー、スタイル違反を検出
|
||||
- **TypeScript チェック**: `.ts` ファイルの編集後に `tsc --noEmit` を実行
|
||||
- **ビルドチェック**: Angular コードの生成または大幅な変更後に `ng build` を実行し、テンプレートと型エラーを早期に検出
|
||||
|
||||
## Stop フック
|
||||
|
||||
- **Lint 監査**: セッション終了前に変更されたファイル全体で `ng lint` を実行し、未解決の違反を検出
|
||||
249
docs/ja-JP/rules/angular/patterns.md
Normal file
249
docs/ja-JP/rules/angular/patterns.md
Normal file
@@ -0,0 +1,249 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.component.ts"
|
||||
- "**/*.component.html"
|
||||
- "**/*.service.ts"
|
||||
- "**/*.store.ts"
|
||||
- "**/*.routes.ts"
|
||||
---
|
||||
# Angular パターン
|
||||
|
||||
> このファイルは [common/patterns.md](../common/patterns.md) を Angular 固有のコンテンツで拡張します。
|
||||
|
||||
## Smart / Dumb コンポーネント分離
|
||||
|
||||
Smart(コンテナ)コンポーネントはデータ取得と状態を所有します。Dumb(プレゼンテーション)コンポーネントは入力の受け取りと出力の発行のみを行い、サービスの注入は行いません。
|
||||
|
||||
```typescript
|
||||
// Smart — データを所有
|
||||
@Component({ standalone: true, changeDetection: ChangeDetectionStrategy.OnPush })
|
||||
export class UserPageComponent {
|
||||
private userService = inject(UserService);
|
||||
user = toSignal(this.userService.getUser(this.userId));
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<!-- Dumb — 純粋なプレゼンテーション -->
|
||||
<app-user-card [user]="user()" (select)="onSelect($event)" />
|
||||
```
|
||||
|
||||
## サービスレイヤー
|
||||
|
||||
サービスがすべてのデータアクセスとビジネスロジックを所有します。コンポーネントは委譲のみ — コンポーネント内に `HttpClient` を配置しないでください。
|
||||
|
||||
```typescript
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class UserService {
|
||||
private http = inject(HttpClient);
|
||||
|
||||
getUsers(): Observable<User[]> {
|
||||
return this.http.get<User[]>('/api/users');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `resource` を使用した非同期データ
|
||||
|
||||
リアクティブな非同期フェッチには `resource()` を使用してください。単純なデータ読み込みには手動の RxJS パイプラインよりも優先してください:
|
||||
|
||||
```typescript
|
||||
export class UserDetailComponent {
|
||||
userId = input.required<string>();
|
||||
|
||||
userResource = resource({
|
||||
request: () => ({ id: this.userId() }),
|
||||
loader: ({ request }) =>
|
||||
firstValueFrom(inject(UserService).getUser(request.id)),
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
状態へのアクセス: `userResource.value()`、`userResource.isLoading()`、`userResource.error()`、`userResource.reload()`。
|
||||
|
||||
## シグナル状態パターン
|
||||
|
||||
```typescript
|
||||
// ローカルの可変状態
|
||||
count = signal(0);
|
||||
|
||||
// 派生(複製しない)
|
||||
doubled = computed(() => this.count() * 2);
|
||||
|
||||
// ソースとともにリセットされる書き込み可能な派生状態
|
||||
selectedItem = linkedSignal(() => this.items()[0]);
|
||||
|
||||
// Observable からシグナルへのブリッジ
|
||||
users = toSignal(this.userService.getUsers(), { initialValue: [] });
|
||||
```
|
||||
|
||||
派生値を別のシグナルに格納しないでください — `computed` を使用してください。シグナルの同期に `effect` を使用しないでください — `computed` または `linkedSignal` を使用してください。
|
||||
|
||||
## サブスクリプションのクリーンアップ
|
||||
|
||||
すべての手動サブスクリプションには `takeUntilDestroyed()` を使用してください。新しいコードでは手動の `ngOnDestroy` + `Subject` + `takeUntil` を使用しないでください。
|
||||
|
||||
```typescript
|
||||
export class UserComponent {
|
||||
private destroyRef = inject(DestroyRef);
|
||||
|
||||
ngOnInit() {
|
||||
this.userService.updates$
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe(update => this.handleUpdate(update));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ルーティング
|
||||
|
||||
### ルート定義
|
||||
|
||||
```typescript
|
||||
// app.routes.ts
|
||||
export const routes: Routes = [
|
||||
{ path: '', component: HomeComponent },
|
||||
{
|
||||
path: 'admin',
|
||||
canMatch: [authGuard], // CanMatch はチャンクの読み込み自体を防止
|
||||
loadChildren: () => import('./admin/admin.routes').then(m => m.ADMIN_ROUTES),
|
||||
},
|
||||
{
|
||||
path: 'users/:id',
|
||||
resolve: { user: userResolver },
|
||||
component: UserDetailComponent,
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
- 未認証ユーザーに対してルートモジュールを読み込まないようにする場合は `canMatch` を `canActivate` より優先
|
||||
- すべての機能モジュールを `loadChildren` で遅延読み込み
|
||||
- コンポーネント内のローディング状態を回避するため `resolve` でデータをプリフェッチ
|
||||
|
||||
### 関数型ガード
|
||||
|
||||
```typescript
|
||||
export const authGuard: CanActivateFn = () => {
|
||||
const auth = inject(AuthService);
|
||||
return auth.isAuthenticated()
|
||||
? true
|
||||
: inject(Router).createUrlTree(['/login']);
|
||||
};
|
||||
```
|
||||
|
||||
### データリゾルバ
|
||||
|
||||
```typescript
|
||||
export const userResolver: ResolveFn<User> = (route) => {
|
||||
return inject(UserService).getUser(route.paramMap.get('id')!);
|
||||
};
|
||||
```
|
||||
|
||||
### ビュートランジション
|
||||
|
||||
View Transitions API でスムーズなルート遷移を有効化:
|
||||
|
||||
```typescript
|
||||
// app.config.ts
|
||||
provideRouter(routes, withViewTransitions())
|
||||
```
|
||||
|
||||
## 依存性注入パターン
|
||||
|
||||
### スコープ付きプロバイダ
|
||||
|
||||
サービスがシングルトンであるべきでない場合、コンポーネントまたはルートレベルで提供してください:
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
providers: [UserEditService], // このコンポーネントサブツリーにスコープ
|
||||
})
|
||||
export class UserEditComponent {}
|
||||
```
|
||||
|
||||
### `InjectionToken`
|
||||
|
||||
```typescript
|
||||
export const CONFIG = new InjectionToken<AppConfig>('APP_CONFIG');
|
||||
|
||||
// プロバイダ内:
|
||||
{ provide: CONFIG, useValue: appConfig }
|
||||
{ provide: CONFIG, useFactory: () => loadConfig(), deps: [] }
|
||||
|
||||
// 使用:
|
||||
private config = inject(CONFIG);
|
||||
```
|
||||
|
||||
### `viewProviders` と `providers`
|
||||
|
||||
- `providers`: コンポーネントとそのすべてのコンテンツ子要素で利用可能
|
||||
- `viewProviders`: コンポーネント自身のビューでのみ利用可能(投影されたコンテンツでは不可)
|
||||
|
||||
## HTTP インターセプター
|
||||
|
||||
認証、エラーハンドリング、リトライには関数型インターセプター(v15+)を使用してください:
|
||||
|
||||
```typescript
|
||||
export const authInterceptor: HttpInterceptorFn = (req, next) => {
|
||||
const token = inject(AuthService).token();
|
||||
if (!token) return next(req);
|
||||
return next(req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }));
|
||||
};
|
||||
```
|
||||
|
||||
`app.config.ts` で登録:
|
||||
|
||||
```typescript
|
||||
provideHttpClient(withInterceptors([authInterceptor, errorInterceptor]))
|
||||
```
|
||||
|
||||
## RxJS オペレータ
|
||||
|
||||
- `switchMap` — 検索、ナビゲーション(前のリクエストをキャンセル)
|
||||
- `mergeMap` — 独立した並列リクエスト
|
||||
- `exhaustMap` — フォーム送信(完了するまで無視)
|
||||
- 常に `catchError` でエラーを処理 — ストリームを暗黙的に死なせない
|
||||
|
||||
```typescript
|
||||
search$ = this.query$.pipe(
|
||||
debounceTime(300),
|
||||
distinctUntilChanged(),
|
||||
switchMap(q => this.service.search(q).pipe(catchError(() => of([])))),
|
||||
);
|
||||
```
|
||||
|
||||
## フォーム
|
||||
|
||||
プロジェクトの既存のフォーム戦略に合わせてください。v21+ の新規アプリにはシグナルフォームを優先してください。
|
||||
|
||||
```typescript
|
||||
// Reactive Forms — 複雑なフォームの標準
|
||||
export class UserFormComponent {
|
||||
private fb = inject(FormBuilder);
|
||||
|
||||
form = this.fb.group({
|
||||
name: ['', Validators.required],
|
||||
email: ['', [Validators.required, Validators.email]],
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## レンダリング戦略
|
||||
|
||||
- **CSR**(デフォルト): 標準 SPA
|
||||
- **SSR + ハイドレーション**: `ng add @angular/ssr` — FCP と SEO を改善
|
||||
- **SSG(プリレンダリング)**: コンテンツの多いルート向けにビルド時に静的ページを生成
|
||||
|
||||
SSR を使用する場合、`window`、`document`、`localStorage` を直接使用しないでください — `isPlatformBrowser` または `DOCUMENT` トークンを使用してください。
|
||||
|
||||
## アクセシビリティ
|
||||
|
||||
ヘッドレスでアクセシブルなコンポーネント(Accordion、Listbox、Combobox、Menu、Tabs、Toolbar、Tree、Grid)には Angular CDK を使用してください。ARIA 属性を手動で管理するのではなく、スタイルを適用してください:
|
||||
|
||||
```css
|
||||
[aria-selected="true"] { background: var(--color-selected); }
|
||||
```
|
||||
|
||||
## スキルリファレンス
|
||||
|
||||
シグナル、フォーム、ルーティング、DI、SSR、アクセシビリティパターンの詳細なガイダンスについては、スキル: `angular-developer` を参照してください。
|
||||
87
docs/ja-JP/rules/angular/security.md
Normal file
87
docs/ja-JP/rules/angular/security.md
Normal file
@@ -0,0 +1,87 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.component.ts"
|
||||
- "**/*.component.html"
|
||||
- "**/*.service.ts"
|
||||
- "**/*.interceptor.ts"
|
||||
---
|
||||
# Angular セキュリティ
|
||||
|
||||
> このファイルは [common/security.md](../common/security.md) を Angular 固有のコンテンツで拡張します。
|
||||
|
||||
## XSS 防止
|
||||
|
||||
Angular はバインドされた値を自動的にサニタイズします。ユーザー制御の入力に対してサニタイザをバイパスしないでください。
|
||||
|
||||
```typescript
|
||||
// 誤り: サニタイゼーションをバイパス — XSS リスク
|
||||
this.safeHtml = this.sanitizer.bypassSecurityTrustHtml(userInput);
|
||||
|
||||
// 正しい: 信頼する前に明示的にサニタイズ
|
||||
this.safeHtml = this.sanitizer.sanitize(SecurityContext.HTML, userInput);
|
||||
```
|
||||
|
||||
- 文書化されレビューされた理由なしに `bypassSecurityTrust*` メソッドを使用しない
|
||||
- 信頼できないコンテンツに `[innerHTML]` を使用しない — `innerText` またはサニタイズパイプを使用
|
||||
- ユーザー入力に `[href]` をバインドしない — Angular はすべてのコンテキストで `javascript:` URL をブロックするわけではない
|
||||
- ユーザーデータからテンプレート文字列を構築しない
|
||||
|
||||
## HTTP セキュリティ
|
||||
|
||||
`HttpClient` のみを使用してください — 代替手段がない場合を除き、生の `fetch()` や `XHR` を使用しないでください。
|
||||
|
||||
```typescript
|
||||
// 誤り: インターセプターをバイパス(認証ヘッダー、エラーハンドリング、ログ記録)
|
||||
const res = await fetch('/api/users');
|
||||
|
||||
// 正しい
|
||||
users$ = this.http.get<User[]>('/api/users');
|
||||
```
|
||||
|
||||
- インターセプター経由で認証トークンを添付 — 個々のサービス呼び出しにハードコードしない
|
||||
- API レスポンスを型付けしてバリデーション — 境界では外部データを `unknown` として扱う
|
||||
- トークン、PII、または認証情報を含む可能性のある HTTP レスポンスをログに記録しない
|
||||
|
||||
## シークレット管理
|
||||
|
||||
```typescript
|
||||
// 誤り: ソースにハードコードされたシークレット
|
||||
const apiKey = 'sk-live-xxxx';
|
||||
|
||||
// 正しい: 環境経由で注入
|
||||
import { environment } from '../environments/environment';
|
||||
const apiKey = environment.apiKey;
|
||||
```
|
||||
|
||||
- `environment.ts` を設定の形として扱う — ソース管理されている環境ファイルに実際のシークレットを格納しない
|
||||
- CI/CD 経由で本番シークレットを注入(環境変数、シークレットマネージャー)
|
||||
|
||||
## ルートガード
|
||||
|
||||
すべての認証済みまたはロール制限されたルートにはガードが必要です。UI 要素の非表示だけに頼らないでください。
|
||||
|
||||
```typescript
|
||||
{
|
||||
path: 'admin',
|
||||
canMatch: [authGuard, roleGuard('admin')],
|
||||
loadChildren: () => import('./admin/admin.routes'),
|
||||
}
|
||||
```
|
||||
|
||||
機密性の高いルートには `canMatch` を使用してください — 未認証ユーザーに対してルートモジュールの読み込み自体を防止します。
|
||||
|
||||
## SSR セキュリティ
|
||||
|
||||
Angular SSR を使用する場合:
|
||||
|
||||
- 意図的に公開する場合を除き、`TransferState` 経由でサーバーサイドの環境変数をクライアントに公開しない
|
||||
- サーバーサイドレンダリング前にすべての入力をサニタイズ — DOM ベースの XSS はサーバーサイドでも発生する可能性がある
|
||||
- サーバー上で `window`、`document`、`localStorage` を使用しない — `isPlatformBrowser` でゲートするか、`DOCUMENT` トークン経由で注入
|
||||
|
||||
## コンテンツセキュリティポリシー
|
||||
|
||||
サーバーサイドで CSP ヘッダーを設定してください。`script-src` で `unsafe-inline` を避けてください。インラインスクリプトを使用する SSR では、Angular の CSP サポートを通じてナンスを使用してください。
|
||||
|
||||
## エージェントサポート
|
||||
|
||||
- 包括的なセキュリティ監査には **security-reviewer** スキルを使用
|
||||
164
docs/ja-JP/rules/angular/testing.md
Normal file
164
docs/ja-JP/rules/angular/testing.md
Normal file
@@ -0,0 +1,164 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.spec.ts"
|
||||
- "**/*.test.ts"
|
||||
---
|
||||
# Angular テスト
|
||||
|
||||
> このファイルは [common/testing.md](../common/testing.md) を Angular 固有のコンテンツで拡張します。
|
||||
|
||||
## テストランナー
|
||||
|
||||
プロジェクトで設定されているテストランナーを使用してください。`angular.json` と `package.json` を確認してください。Angular プロジェクトでは一般的に Vitest、Jest、または Jasmine + Karma が使用されます。
|
||||
|
||||
```bash
|
||||
ng test # ウォッチモード
|
||||
ng test --no-watch # CI モード
|
||||
```
|
||||
|
||||
## TestBed セットアップ
|
||||
|
||||
スタンドアロンコンポーネントの場合、コンポーネントを直接インポートします。外部テンプレートを持つコンポーネントには `compileComponents()` を呼び出してください。
|
||||
|
||||
```typescript
|
||||
describe('UserCardComponent', () => {
|
||||
let fixture: ComponentFixture<UserCardComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [UserCardComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(UserCardComponent);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## シグナル入力
|
||||
|
||||
シグナルベースの入力は `fixture.componentRef.setInput()` で設定します:
|
||||
|
||||
```typescript
|
||||
fixture.componentRef.setInput('user', mockUser);
|
||||
fixture.detectChanges();
|
||||
```
|
||||
|
||||
## コンポーネントハーネス
|
||||
|
||||
UI インタラクションには直接の DOM クエリよりも Angular CDK コンポーネントハーネスを優先してください。ハーネスはマークアップの変更に対してより耐性があります。
|
||||
|
||||
```typescript
|
||||
import { HarnessLoader } from '@angular/cdk/testing';
|
||||
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||
import { MatButtonHarness } from '@angular/material/button/testing';
|
||||
|
||||
let loader: HarnessLoader;
|
||||
|
||||
beforeEach(() => {
|
||||
loader = TestbedHarnessEnvironment.loader(fixture);
|
||||
});
|
||||
|
||||
it('triggers save on button click', async () => {
|
||||
const button = await loader.getHarness(MatButtonHarness.with({ text: 'Save' }));
|
||||
await button.click();
|
||||
expect(saveSpy).toHaveBeenCalled();
|
||||
});
|
||||
```
|
||||
|
||||
## ルーターテスト
|
||||
|
||||
ルーターに依存するコンポーネントには `RouterTestingHarness` を使用してください:
|
||||
|
||||
```typescript
|
||||
import { RouterTestingHarness } from '@angular/router/testing';
|
||||
|
||||
it('renders user on navigation', async () => {
|
||||
const harness = await RouterTestingHarness.create();
|
||||
const component = await harness.navigateByUrl('/users/1', UserDetailComponent);
|
||||
expect(component.userId()).toBe('1');
|
||||
});
|
||||
```
|
||||
|
||||
## 非同期テスト
|
||||
|
||||
制御された非同期には `fakeAsync` + `tick` を使用してください。実際の非同期には `waitForAsync` と `fixture.whenStable()` を使用してください。
|
||||
|
||||
```typescript
|
||||
it('loads user after delay', fakeAsync(() => {
|
||||
const service = TestBed.inject(UserService);
|
||||
vi.spyOn(service, 'getUser').mockReturnValue(of(mockUser));
|
||||
|
||||
fixture.detectChanges();
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.nativeElement.querySelector('.name').textContent).toBe(mockUser.name);
|
||||
}));
|
||||
```
|
||||
|
||||
## HTTP テスト
|
||||
|
||||
```typescript
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
import { HttpTestingController } from '@angular/common/http/testing';
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [provideHttpClient(), provideHttpClientTesting()],
|
||||
});
|
||||
httpMock = TestBed.inject(HttpTestingController);
|
||||
});
|
||||
|
||||
afterEach(() => httpMock.verify());
|
||||
```
|
||||
|
||||
## サービステスト
|
||||
|
||||
コンポーネントフィクスチャなしでサービスを直接注入します:
|
||||
|
||||
```typescript
|
||||
describe('UserService', () => {
|
||||
let service: UserService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [provideHttpClient(), provideHttpClientTesting()],
|
||||
});
|
||||
service = TestBed.inject(UserService);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## テスト対象
|
||||
|
||||
- **サービス**: すべてのパブリックメソッド、エラーパス、HTTP インタラクション
|
||||
- **コンポーネント**: 入力/出力バインディング、主要な状態のレンダリング結果、ハーネスを使用したユーザーインタラクション
|
||||
- **パイプ**: 純粋な変換 — TestBed 不要のプレーンなユニットテスト
|
||||
- **ガード/リゾルバ**: `RouterTestingHarness` を使用した許可および拒否状態の戻り値
|
||||
|
||||
## E2E テスト
|
||||
|
||||
重要なユーザーフローには、Cypress や Playwright などプロジェクトで設定されている E2E フレームワークを使用してください。
|
||||
|
||||
```typescript
|
||||
describe('Login flow', () => {
|
||||
it('redirects to dashboard on valid credentials', () => {
|
||||
cy.visit('/login');
|
||||
cy.get('[data-cy=email]').type('user@example.com');
|
||||
cy.get('[data-cy=password]').type('password123');
|
||||
cy.get('[data-cy=submit]').click();
|
||||
cy.url().should('include', '/dashboard');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
- 安定したセレクタのためにインタラクティブ要素に `data-cy` 属性を追加
|
||||
- E2E テストでセレクタに CSS クラスやテキストコンテンツに依存しない
|
||||
|
||||
## カバレッジ
|
||||
|
||||
サービスとパイプのカバレッジは80%以上を目標にしてください。コンポーネント: 実装の詳細ではなく、振る舞いをテストしてください。
|
||||
|
||||
## スキルリファレンス
|
||||
|
||||
包括的なテストパターン、ハーネスの使用法、非同期のベストプラクティスについては、スキル: `angular-developer` を参照してください。
|
||||
Reference in New Issue
Block a user