Skip to content

Commit bd97e5f

Browse files
1000-x-t30claude
andcommitted
Initial commit
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
0 parents  commit bd97e5f

86 files changed

Lines changed: 7995 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
name: react-doctor
3+
description: Run after making React changes to catch issues early. Use when reviewing code, finishing a feature, or fixing bugs in a React project.
4+
version: 1.0.0
5+
---
6+
7+
# React Doctor
8+
9+
Scans your React codebase for security, performance, correctness, and architecture issues. Outputs a 0-100 score with actionable diagnostics.
10+
11+
## Usage
12+
13+
```bash
14+
npx -y react-doctor@latest . --verbose --diff
15+
```
16+
17+
## Workflow
18+
19+
Run after making changes to catch issues early. Fix errors first, then re-run to verify the score improved.

.claude/settings.local.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(npx tsc:*)"
5+
]
6+
}
7+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
---
2+
name: react-doctor
3+
description: Reactコンポーネントを診断する。パフォーマンス・アクセシビリティ・設計上の問題を検出してレポートを出力する。特定ファイルの診断、またはプロジェクト全体のスキャンが可能。
4+
allowed-tools: Read, Grep, Glob, Bash
5+
---
6+
7+
## React Doctor — Reactコンポーネント診断ツール
8+
9+
引数 `$ARGUMENTS` に応じて動作を切り替えてください:
10+
11+
- **引数あり(ファイルパス指定)**: 指定されたファイルのみ診断する
12+
- **引数なし(または `--all`**: プロジェクト全体の `src/**/*.tsx` をスキャンして診断する
13+
14+
---
15+
16+
## 診断手順
17+
18+
### ステップ1: 対象ファイルの特定
19+
20+
- 引数がある場合: そのファイルを読み込む
21+
- 引数がない場合: Glob で `src/**/*.tsx``src/**/*.ts` を列挙し、コンポーネントファイルを特定する
22+
23+
### ステップ2: 各ファイルを以下の観点で診断する
24+
25+
#### パフォーマンス
26+
- [ ] `memo` / `useMemo` / `useCallback` の不足または過剰使用
27+
- [ ] 不要な再レンダリングを引き起こすオブジェクト・配列のインライン生成
28+
- [ ] `useEffect` の依存配列が正しいか(空配列の乱用、依存漏れ)
29+
- [ ] 大きなコンポーネントを分割すべき箇所(100行超のJSXが目安)
30+
- [ ] `key` プロップが適切か(index使用の問題など)
31+
32+
#### アクセシビリティ
33+
- [ ] インタラクティブ要素(`div`, `span`)に `onClick` のみでキーボード操作が欠落していないか
34+
- [ ] `img``alt` 属性があるか
35+
- [ ] フォーム要素に `label` または `aria-label` があるか
36+
- [ ] `role``aria-*` 属性の適切な使用
37+
- [ ] カラーコントラストや非テキスト要素への代替テキスト(コードから判断できる範囲で)
38+
39+
#### 設計・コード品質
40+
- [ ] 単一責任原則: 1コンポーネントが複数の責務を持っていないか
41+
- [ ] Props の肥大化(5つ以上のPropsは要検討)
42+
- [ ] カスタムフックに切り出せるロジックがないか
43+
- [ ] `any` 型の使用
44+
- [ ] コンポーネント内での直接的なAPI呼び出し(フックやサービス層に切り出すべき)
45+
- [ ] マジックナンバー・ハードコードされた文字列
46+
47+
### ステップ3: レポートの出力形式
48+
49+
```
50+
## 実行コード
51+
npx -y react-doctor@latest . --verbose --diff
52+
```
53+
54+
```
55+
## React Doctor 診断レポート
56+
診断日時: <現在日時>
57+
対象: <ファイルパスまたは "プロジェクト全体">
58+
59+
---
60+
61+
### 📋 サマリー
62+
- 診断ファイル数: N
63+
- 問題あり: N ファイル
64+
- 重大度別: 🔴 Critical N件 / 🟡 Warning N件 / 🔵 Info N件
65+
66+
---
67+
68+
### 🔍 詳細結果
69+
70+
#### <ファイルパス>
71+
72+
**🔴 Critical(修正必須)**
73+
- [行番号] 問題の説明 → 推奨する修正方法
74+
75+
**🟡 Warning(修正推奨)**
76+
- [行番号] 問題の説明 → 推奨する修正方法
77+
78+
**🔵 Info(改善提案)**
79+
- [行番号] 問題の説明 → 推奨する修正方法
80+
81+
✅ 問題なし(問題がない場合)
82+
83+
---
84+
85+
### 💡 優先対応リスト(上位5件)
86+
1. ファイル名:行番号 — 問題の概要(重大度)
87+
...
88+
```
89+
90+
重大度の基準:
91+
- **Critical**: バグやセキュリティリスク、またはアクセシビリティの根本的な欠如
92+
- **Warning**: パフォーマンス劣化・保守性の問題
93+
- **Info**: ベストプラクティスへの改善提案

.editorconfig

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
root = true
2+
# 初めの行に記述する。この記述をすることで、フォルダ以下のディレクトリのみにルールが適用される
3+
4+
#[*.css] // 適用したいファイルを指定
5+
#indent_style = space // シャープでコメントできる
6+
7+
[*]
8+
# すべてのファイルに適用する
9+
charset = utf-8
10+
# 文字コードを統一
11+
indent_style = space
12+
#インデントを統一する。「tab」か「 space」
13+
indent_size = 2
14+
# インデントの数を統一
15+
trim_trailing_whitespace = true
16+
# 行末のホワイトスペースを削除
17+
insert_final_newline = true
18+
# フォルダの最後の行に改行
19+
end_of_line = lf
20+
21+
[*.php]
22+
charset = utf-8
23+
end_of_line = lf
24+
insert_final_newline = true
25+
indent_style = space
26+
indent_size = 4
27+
28+
[*.js]
29+
charset = utf-8
30+
end_of_line = lf
31+
insert_final_newline = true
32+
trim_trailing_whitespace = true
33+
indent_style = space
34+
indent_size = 2
35+
36+
[*.ts]
37+
charset = utf-8
38+
end_of_line = lf
39+
insert_final_newline = true
40+
trim_trailing_whitespace = true
41+
indent_style = space
42+
indent_size = 2
43+
44+
[*.tsx]
45+
charset = utf-8
46+
end_of_line = lf
47+
insert_final_newline = true
48+
trim_trailing_whitespace = true
49+
indent_style = space
50+
indent_size = 2
51+
52+
[*.scss]
53+
charset = utf-8
54+
end_of_line = lf
55+
insert_final_newline = true
56+
trim_trailing_whitespace = true
57+
indent_style = space
58+
indent_size = 2
59+
60+
[*.html]
61+
charset = utf-8
62+
end_of_line = lf
63+
indent_style = space
64+
indent_size = 2
65+
66+
[*.yaml]
67+
indent_style = space
68+
indent_size = 2

.gitignore

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
pnpm-debug.log*
8+
lerna-debug.log*
9+
10+
node_modules
11+
dist
12+
build
13+
dist-ssr
14+
*.local
15+
16+
app/bundle
17+
18+
/vendor
19+
.phplint-cache
20+
21+
# Editor directories and files
22+
.vscode/*
23+
!.vscode/extensions.json
24+
.idea
25+
.DS_Store
26+
*.suo
27+
*.ntvs*
28+
*.njsproj
29+
*.sln
30+
*.sw?

.node-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v22.20.0

app/GET/AI.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace Acms\Plugins\AI\GET;
4+
5+
use ACMS_GET;
6+
use Zend\Filter\Boolean;
7+
8+
class AI extends ACMS_GET
9+
{
10+
protected $authorized = false;
11+
protected $configField = [];
12+
protected $modelCur = '';
13+
protected $aiOrganizationId = '';
14+
protected $aiProjectId = '';
15+
protected $aiApiKey = '';
16+
protected $authorizedModels = [];
17+
protected $aiCertKey = [];
18+
protected $aiCustomPrompt = [];
19+
}

app/GET/AI/Admin.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
namespace Acms\Plugins\AI\GET\AI;
4+
5+
use Tpl;
6+
use Template;
7+
use ACMS_Corrector;
8+
use Acms\Plugins\AI\GET\AI;
9+
use Acms\Plugins\AI\Services\AI as ServiceAI;
10+
11+
class Admin extends AI
12+
{
13+
public function get()
14+
{
15+
$Tpl = new Template($this->tpl, new ACMS_Corrector());
16+
$models = [];
17+
18+
try {
19+
$ServiceAI = new ServiceAI();
20+
$config = $ServiceAI->getConfig();
21+
$cert = $ServiceAI->getCertification($config);
22+
23+
$models = $ServiceAI->auth($cert['ai_organization_id'], $cert['ai_project_id'], $cert['ai_api_key']);
24+
if ($models !== null) {
25+
$this->authorized = $models !== [] ? true : false;
26+
}
27+
if ($cert['ai_model']) {
28+
$this->modelCur = $cert['ai_model'];
29+
}
30+
31+
if (is_array($models) && $models !== []) {
32+
foreach ($models as $model) {
33+
$this->authorizedModels[] = [
34+
'model' => $model,
35+
'model_cur' => $this->modelCur
36+
];
37+
}
38+
}
39+
40+
$this->configField = Tpl::buildField($config, $Tpl);
41+
} catch (\Exception $e) {
42+
}
43+
44+
$obj = array_merge(
45+
['model' => $this->authorizedModels],
46+
['authorized' => $this->authorized ? 'true' : 'false'],
47+
$this->configField
48+
);
49+
50+
return $Tpl->render($obj);
51+
}
52+
}

app/GET/AI/Config.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace Acms\Plugins\AI\GET\AI;
4+
5+
use Tpl;
6+
use Template;
7+
use ACMS_Corrector;
8+
use Acms\Plugins\AI\GET\AI;
9+
use Acms\Plugins\AI\Services\AI as ServiceAI;
10+
11+
class Config extends AI
12+
{
13+
public function get()
14+
{
15+
$Tpl = new Template($this->tpl, new ACMS_Corrector());
16+
17+
try {
18+
$ServiceAI = new ServiceAI();
19+
$config = $ServiceAI->getConfig();
20+
$cert = $ServiceAI->getCertification($config);
21+
$this->configField = Tpl::buildField($config, $Tpl);
22+
23+
if (
24+
isset($cert['ai_api_key']) &&
25+
isset($cert['ai_model']) &&
26+
$ServiceAI->availableModel($cert['ai_model'])
27+
) {
28+
$this->authorized = true;
29+
$this->configField = Tpl::buildField($config, $Tpl);
30+
}
31+
} catch (\Exception $e) {
32+
}
33+
34+
$obj = array_merge(
35+
[
36+
'authorized' => $this->authorized ? 'true' : 'false',
37+
],
38+
$this->configField
39+
);
40+
41+
return $Tpl->render($obj);
42+
}
43+
}

app/Hook.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Acms\Plugins\AI;
4+
5+
class Hook
6+
{
7+
/**
8+
* JSが更新された場合に、以前のバージョンで作られたキャッシュを使用しないようにキャッシュバスティングを行う
9+
* scriptタグでJSを読み込む際に、acmsのグローバル変数を経由する
10+
*
11+
* @param \Field &$globalVars
12+
*/
13+
public function extendsGlobalVars(&$globalVars)
14+
{
15+
$globalVars->set(
16+
'AI_JS',
17+
cacheBusting(
18+
'/' . DIR_OFFSET . 'extension/plugins/AI/bundle/acms-ai.js',
19+
SCRIPT_DIR . '/extension/plugins/AI/bundle/acms-ai.js'
20+
)
21+
);
22+
23+
$globalVars->set(
24+
'AI_CSS',
25+
cacheBusting(
26+
'/' . DIR_OFFSET . 'extension/plugins/AI/bundle/acms-ai.css',
27+
SCRIPT_DIR . '/extension/plugins/AI/bundle/acms-ai.css'
28+
)
29+
);
30+
}
31+
}

0 commit comments

Comments
 (0)