eslint-plugin-smarthr 6.8.0 → 6.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/package.json +2 -2
- package/rules/autofixer-smarthr-ui-migration/DEVELOPER.md +213 -2
- package/rules/autofixer-smarthr-ui-migration/README.md +154 -0
- package/rules/autofixer-smarthr-ui-migration/index.js +4 -1
- package/rules/autofixer-smarthr-ui-migration/versions/v90-to-v91/REFERENCE.md +236 -3
- package/rules/autofixer-smarthr-ui-migration/versions/v90-to-v91/index.js +139 -3
- package/rules/autofixer-smarthr-ui-migration/versions/v90-to-v91/test.js +112 -0
- package/rules/best-practice-for-text-component/index.js +54 -2
- package/test/best-practice-for-text-component.js +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [6.9.1](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v6.9.0...eslint-plugin-smarthr-v6.9.1) (2026-04-07)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **best-practice-for-text-component:** 変換不可能なshr-クラスのみの場合に不要なスペースが挿入される問題を修正 ([#1199](https://github.com/kufu/tamatebako/issues/1199)) ([ad942fc](https://github.com/kufu/tamatebako/commit/ad942fcfa2484013fe82fd1c9d9b94c097a0e27a))
|
|
11
|
+
|
|
12
|
+
## [6.9.0](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v6.8.0...eslint-plugin-smarthr-v6.9.0) (2026-04-05)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* **autofixer-smarthr-ui-migration:** smarthr-uiのalias対応を追加 ([#1191](https://github.com/kufu/tamatebako/issues/1191)) ([4a23056](https://github.com/kufu/tamatebako/commit/4a23056695a2955885ca8c1134b953f40f4588d4))
|
|
18
|
+
|
|
5
19
|
## [6.8.0](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v6.7.0...eslint-plugin-smarthr-v6.8.0) (2026-04-03)
|
|
6
20
|
|
|
7
21
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-smarthr",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.9.1",
|
|
4
4
|
"author": "SmartHR",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "A sharable ESLint plugin for SmartHR",
|
|
@@ -37,5 +37,5 @@
|
|
|
37
37
|
"eslintplugin",
|
|
38
38
|
"smarthr"
|
|
39
39
|
],
|
|
40
|
-
"gitHead": "
|
|
40
|
+
"gitHead": "5f958652e81f7a69a347489108ff88cb1b3e56dd"
|
|
41
41
|
}
|
|
@@ -61,6 +61,7 @@ autofixer-smarthr-ui-migrationルールに新しいバージョン(v[XX]→v[Y
|
|
|
61
61
|
- rules/autofixer-smarthr-ui-migration/versions/v90-to-v91/README.md(ユーザー向け移行ガイド)
|
|
62
62
|
- rules/autofixer-smarthr-ui-migration/versions/v90-to-v91/test.js(テストケース)
|
|
63
63
|
- test/autofixer-smarthr-ui-migration.js(メインテスト)
|
|
64
|
+
- libs/common.js(rootPathの取得、tsconfig.jsonのpaths設定読み込み)
|
|
64
65
|
|
|
65
66
|
## 対応する変更
|
|
66
67
|
|
|
@@ -71,26 +72,54 @@ smarthr-ui v[YY]のリリースノート: [GitHubリリースページのURL]
|
|
|
71
72
|
1. [変更内容1の説明]
|
|
72
73
|
- 例: ComponentA が ComponentB にリネーム(破壊的変更)
|
|
73
74
|
- 自動修正: [可能/不可能/条件付き]
|
|
75
|
+
- セレクター: `ImportDeclaration`, `JSXOpeningElement[name.name="ComponentA"]`
|
|
74
76
|
|
|
75
77
|
2. [変更内容2の説明]
|
|
76
78
|
- 例: propsXがpropsYにリネーム(破壊的変更)
|
|
77
79
|
- 自動修正: [可能/不可能/条件付き]
|
|
80
|
+
- セレクター: `JSXAttribute[name.name="propsX"]`
|
|
78
81
|
|
|
79
82
|
3. [変更内容3の説明]
|
|
80
83
|
- 例: 非推奨パターンから推奨パターンへの置き換え(非破壊的)
|
|
81
84
|
- 自動修正: [可能/不可能/条件付き]
|
|
85
|
+
- セレクター: [該当するASTセレクター]
|
|
82
86
|
|
|
83
87
|
4. ...
|
|
84
88
|
|
|
89
|
+
**自動修正の判断基準:**
|
|
90
|
+
- ✅ 自動修正可能: 機械的に100%正しく変換できる場合
|
|
91
|
+
- ⚠️ エラーのみ: 手動確認が必要な場合(未知の属性がある、複数の対処方法がある等)
|
|
92
|
+
- ❌ 検出しない: 複雑すぎる、影響範囲が広すぎる場合
|
|
93
|
+
|
|
85
94
|
## 実装内容
|
|
86
95
|
|
|
87
96
|
1. versionディレクトリを作成: `versions/v[XX]-to-v[YY]/`
|
|
88
97
|
|
|
89
98
|
2. `versions/v[XX]-to-v[YY]/index.js` を作成
|
|
90
99
|
- messages定義
|
|
91
|
-
- createCheckers
|
|
100
|
+
- createCheckers関数の実装(`createCheckers(context, sourceCode, options = {})`)
|
|
92
101
|
- 必要に応じてヘルパー関数
|
|
93
102
|
|
|
103
|
+
**smarthrUiAliasオプションを利用する場合:**
|
|
104
|
+
```javascript
|
|
105
|
+
const { rootPath } = require('../../../../libs/common')
|
|
106
|
+
|
|
107
|
+
createCheckers(context, sourceCode, options = {}) {
|
|
108
|
+
const customSmarthrUiAlias = options.smarthrUiAlias
|
|
109
|
+
const validSources = ['smarthr-ui']
|
|
110
|
+
if (customSmarthrUiAlias) {
|
|
111
|
+
validSources.push(customSmarthrUiAlias)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const isAliasFile = customSmarthrUiAlias && isFileMatchingSmarthrUiAlias(
|
|
115
|
+
context.getFilename(),
|
|
116
|
+
customSmarthrUiAlias
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
// ... チェッカー実装
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
94
123
|
3. `versions/v[XX]-to-v[YY]/README.md` を作成(ユーザー向け移行ガイド)
|
|
95
124
|
- 各変更の説明
|
|
96
125
|
- Before/Afterのコード例
|
|
@@ -104,6 +133,26 @@ smarthr-ui v[YY]のリリースノート: [GitHubリリースページのURL]
|
|
|
104
133
|
- valid: v[YY]形式が正常に通ること
|
|
105
134
|
- invalid: v[XX]形式が検出されて修正されること
|
|
106
135
|
|
|
136
|
+
**テストケースの構造:**
|
|
137
|
+
```javascript
|
|
138
|
+
const v[XX]Tov[YY]Options = [{ from: '[XX]', to: '[YY]' }]
|
|
139
|
+
|
|
140
|
+
module.exports = {
|
|
141
|
+
valid: [
|
|
142
|
+
{ code: `import { NewComponent } from 'smarthr-ui'`, options: v[XX]Tov[YY]Options },
|
|
143
|
+
{ code: `<NewComponent>...</NewComponent>`, options: v[XX]Tov[YY]Options },
|
|
144
|
+
],
|
|
145
|
+
invalid: [
|
|
146
|
+
{
|
|
147
|
+
code: `import { OldComponent } from 'smarthr-ui'`,
|
|
148
|
+
output: `import { NewComponent } from 'smarthr-ui'`,
|
|
149
|
+
options: v[XX]Tov[YY]Options,
|
|
150
|
+
errors: [{ messageId: 'renameComponent', data: { old: 'OldComponent', new: 'NewComponent', to: 'v[YY]' } }],
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
107
156
|
6. `index.js`のVERSION_MODULESに登録
|
|
108
157
|
```javascript
|
|
109
158
|
const v[XX]Tov[YY] = require('./versions/v[XX]-to-v[YY]/index')
|
|
@@ -144,6 +193,50 @@ smarthr-ui v[YY]のリリースノート: [GitHubリリースページのURL]
|
|
|
144
193
|
- JSDocコメントを適切に追加してください
|
|
145
194
|
- ディレクトリ名は必ず `vXX-to-vYY` 形式にしてください(内部キーと統一)
|
|
146
195
|
|
|
196
|
+
## 共通機能:smarthrUiAlias オプション
|
|
197
|
+
|
|
198
|
+
プロジェクト固有のsmarthr-ui aliasパスに対応するため、`smarthrUiAlias`オプションが利用可能です。
|
|
199
|
+
|
|
200
|
+
### createCheckers関数でのオプション利用
|
|
201
|
+
|
|
202
|
+
```javascript
|
|
203
|
+
createCheckers(context, sourceCode, options = {}) {
|
|
204
|
+
const customSmarthrUiAlias = options.smarthrUiAlias
|
|
205
|
+
const validSources = ['smarthr-ui']
|
|
206
|
+
if (customSmarthrUiAlias) {
|
|
207
|
+
validSources.push(customSmarthrUiAlias)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// aliasファイルかどうかの判定
|
|
211
|
+
const isAliasFile = customSmarthrUiAlias && isFileMatchingSmarthrUiAlias(
|
|
212
|
+
context.getFilename(),
|
|
213
|
+
customSmarthrUiAlias
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
// ...
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### 主な用途
|
|
221
|
+
|
|
222
|
+
1. **importのチェック範囲拡張**: `smarthr-ui`に加えて、aliasパス(例: `@/components/parts/smarthr-ui`)からのimportも置換対象
|
|
223
|
+
2. **aliasファイル内のexport変数名置換**: `smarthrUiAlias`配下のファイルで、smarthr-uiコンポーネント名と同じexport変数名を自動置換
|
|
224
|
+
|
|
225
|
+
詳細は[README.md](./README.md#smarthr-ui-の-alias-を使用している場合)を参照。
|
|
226
|
+
|
|
227
|
+
### 🔄 今後の検討事項:共通化
|
|
228
|
+
|
|
229
|
+
**現状:** 各versionディレクトリ(v90-to-v91など)で個別にsmarthrUiAlias関連の処理を実装しています。
|
|
230
|
+
|
|
231
|
+
**検討中:** 以下の処理を共通化できる可能性があります:
|
|
232
|
+
- `validSources`の拡張ロジック
|
|
233
|
+
- `isFileMatchingSmarthrUiAlias`ヘルパー関数
|
|
234
|
+
- export変数名の置換チェッカー追加ロジック
|
|
235
|
+
|
|
236
|
+
**実装時期:** v92移行ルール追加時に、重複を確認して共通化を検討してください。共通化する場合は、以下のような場所が候補です:
|
|
237
|
+
- `libs/common.js`に共通ヘルパー関数を追加
|
|
238
|
+
- 各versionモジュールで共通の基底関数を提供
|
|
239
|
+
|
|
147
240
|
## 完了後の作業
|
|
148
241
|
|
|
149
242
|
実装が完了したら、**必ずこのDEVELOPER.mdを更新**してください:
|
|
@@ -163,7 +256,11 @@ smarthr-ui v[YY]のリリースノート: [GitHubリリースページのURL]
|
|
|
163
256
|
- [ ] `versions/vXX-to-vYY/` ディレクトリを作成
|
|
164
257
|
- [ ] `versions/vXX-to-vYY/index.js` を作成
|
|
165
258
|
- [ ] messages定義が含まれている
|
|
166
|
-
- [ ] createCheckers
|
|
259
|
+
- [ ] createCheckers関数が実装されている(`createCheckers(context, sourceCode, options = {})`)
|
|
260
|
+
- [ ] smarthrUiAliasオプションに対応している場合:
|
|
261
|
+
- [ ] `const { rootPath } = require('../../../../libs/common')` をimport
|
|
262
|
+
- [ ] `validSources`に`customSmarthrUiAlias`を追加
|
|
263
|
+
- [ ] `isAliasFile`でファイル判定を実装
|
|
167
264
|
- [ ] ヘルパー関数にJSDocコメントがある
|
|
168
265
|
- [ ] ファイル冒頭に変更サマリーコメントがある
|
|
169
266
|
- [ ] `versions/vXX-to-vYY/README.md` を作成(ユーザー向け移行ガイド)
|
|
@@ -191,6 +288,120 @@ smarthr-ui v[YY]のリリースノート: [GitHubリリースページのURL]
|
|
|
191
288
|
- [ ] `npm test -- test/autofixer-smarthr-ui-migration.js` が通過
|
|
192
289
|
- [ ] オプション `{ from: "[XX]", to: "[YY]" }` で動作確認
|
|
193
290
|
- [ ] 複数バージョンスキップ(例: `{ from: "90", to: "[YY]" }`)でも動作確認
|
|
291
|
+
- [ ] smarthrUiAliasオプション対応がある場合:
|
|
292
|
+
- [ ] `{ from: "[XX]", to: "[YY]", smarthrUiAlias: "@/components/parts/smarthr-ui" }` で動作確認
|
|
293
|
+
- [ ] aliasファイル内のexport変数名が置換されることを確認
|
|
294
|
+
|
|
295
|
+
### 実プロダクトでの検証
|
|
296
|
+
|
|
297
|
+
単体テストが通過したら、実際のプロダクトで動作確認を行います。
|
|
298
|
+
|
|
299
|
+
**手順:**
|
|
300
|
+
|
|
301
|
+
1. **対象プロダクトの選択**
|
|
302
|
+
- ローカルにある実プロダクトを選択(例: `/works/workflow/michi/frontend`)
|
|
303
|
+
|
|
304
|
+
2. **ブランチ作成**
|
|
305
|
+
- `staging` ブランチから新しいブランチを作成(`staging` がない場合は `master` または `main`)
|
|
306
|
+
```bash
|
|
307
|
+
cd /path/to/product
|
|
308
|
+
git checkout staging # または master/main
|
|
309
|
+
git pull
|
|
310
|
+
git checkout -b test/migrator-vXX-to-vYY
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
3. **migratorの追加と設定**
|
|
314
|
+
- 開発中の `eslint-plugin-smarthr` を対象プロダクトに追加
|
|
315
|
+
```bash
|
|
316
|
+
# 相対パスで開発中のパッケージを追加
|
|
317
|
+
pnpm add -D ../../../tamatebako/packages/eslint-plugin-smarthr
|
|
318
|
+
# または npm/yarn の場合
|
|
319
|
+
# npm install -D ../../../tamatebako/packages/eslint-plugin-smarthr
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
- **Legacy Config形式** (`.eslintrc.js`) の場合:
|
|
323
|
+
```javascript
|
|
324
|
+
module.exports = {
|
|
325
|
+
extends: ['smarthr'],
|
|
326
|
+
rules: {
|
|
327
|
+
'smarthr/autofixer-smarthr-ui-migration': [
|
|
328
|
+
'error',
|
|
329
|
+
{ from: 'XX', to: 'YY', smarthrUiAlias: '@/components/parts/smarthr-ui' }
|
|
330
|
+
]
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
- **Flat Config形式** (`eslint.config.mjs`) の場合:
|
|
336
|
+
```javascript
|
|
337
|
+
import smarthr from 'eslint-config-smarthr'
|
|
338
|
+
import smarthrPlugin from 'eslint-plugin-smarthr' // 追加
|
|
339
|
+
|
|
340
|
+
export default [
|
|
341
|
+
...smarthr,
|
|
342
|
+
{
|
|
343
|
+
plugins: {
|
|
344
|
+
'smarthr-local': smarthrPlugin, // 別名で登録
|
|
345
|
+
},
|
|
346
|
+
rules: {
|
|
347
|
+
'smarthr-local/autofixer-smarthr-ui-migration': [
|
|
348
|
+
'error',
|
|
349
|
+
{ from: 'XX', to: 'YY', smarthrUiAlias: '@/components/parts/smarthr-ui' }
|
|
350
|
+
],
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
]
|
|
354
|
+
```
|
|
355
|
+
**注意:** Flat Configでは、すでに `eslint-config-smarthr` 経由で `smarthr` プラグインが登録されているため、開発中のバージョンを使用するには別名(例: `smarthr-local`)で登録する必要があります。
|
|
356
|
+
|
|
357
|
+
4. **初回実行**
|
|
358
|
+
```bash
|
|
359
|
+
npm run lint:fix # または eslint --fix .
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
5. **問題の修正と再実行**
|
|
363
|
+
- 実行時に出た問題(エラー、不正な変換など)を確認
|
|
364
|
+
- 問題があれば migrator の実装を修正
|
|
365
|
+
- **重要:** 再実行前に、staging が更新されていない状態(migration前の状態)に戻す
|
|
366
|
+
```bash
|
|
367
|
+
git reset --hard HEAD # 変更を破棄
|
|
368
|
+
# migratorを修正後、再度実行
|
|
369
|
+
npm run lint:fix
|
|
370
|
+
```
|
|
371
|
+
- 問題が解決するまで手順5を繰り返す
|
|
372
|
+
|
|
373
|
+
6. **PR作成**
|
|
374
|
+
- 問題が修正されたことを確認できたら、差分を確認しやすいよう draft で PR 作成
|
|
375
|
+
```bash
|
|
376
|
+
git add .
|
|
377
|
+
git commit -m "test: vXX to vYY migration test"
|
|
378
|
+
git push -u origin test/migrator-vXX-to-vYY
|
|
379
|
+
gh pr create --draft --title "test: vXX to vYY migration test" --body "migratorの動作確認用PR"
|
|
380
|
+
```
|
|
381
|
+
- PR の差分をレビューして、期待通りの変換が行われているか確認
|
|
382
|
+
|
|
383
|
+
7. **クリーンアップ**
|
|
384
|
+
- 検証完了後、対象プロダクトを元の状態に戻す
|
|
385
|
+
```bash
|
|
386
|
+
# PRはクローズ(マージしない)
|
|
387
|
+
gh pr close test/migrator-vXX-to-vYY
|
|
388
|
+
|
|
389
|
+
# ブランチを削除
|
|
390
|
+
git checkout staging # または master/main
|
|
391
|
+
git branch -D test/migrator-vXX-to-vYY
|
|
392
|
+
git push origin --delete test/migrator-vXX-to-vYY
|
|
393
|
+
|
|
394
|
+
# package.jsonとlockファイルを元に戻す
|
|
395
|
+
git checkout package.json pnpm-lock.yaml # または package-lock.json/yarn.lock
|
|
396
|
+
pnpm install # 依存関係を再インストール
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
**確認ポイント:**
|
|
400
|
+
- [ ] エラーが出ずに実行完了する
|
|
401
|
+
- [ ] 意図した変換が正しく行われている
|
|
402
|
+
- [ ] 不要な変更が含まれていない
|
|
403
|
+
- [ ] エッジケースでも正しく動作する
|
|
404
|
+
- [ ] aliasファイル内のexport変数名も正しく置換されている(smarthrUiAliasオプション使用時)
|
|
194
405
|
|
|
195
406
|
## 参考情報
|
|
196
407
|
|
|
@@ -31,6 +31,160 @@ smarthr-ui のバージョン間の移行を支援する自動修正ルールで
|
|
|
31
31
|
}
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
### smarthr-ui の alias を使用している場合
|
|
35
|
+
|
|
36
|
+
プロジェクトで smarthr-ui を独自のパスから re-export している場合(例: `@/components/parts/smarthr-ui`)、`smarthrUiAlias` オプションを指定することで、alias ファイル内のコンポーネント定義も自動修正の対象になります。
|
|
37
|
+
|
|
38
|
+
#### 前提条件
|
|
39
|
+
|
|
40
|
+
このオプションを使用するには、`tsconfig.json` で paths 設定が必要です:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"compilerOptions": {
|
|
45
|
+
"paths": {
|
|
46
|
+
"@/*": ["src/*"]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
#### 使用方法
|
|
53
|
+
|
|
54
|
+
```javascript
|
|
55
|
+
{
|
|
56
|
+
"rules": {
|
|
57
|
+
"smarthr/autofixer-smarthr-ui-migration": [
|
|
58
|
+
"error",
|
|
59
|
+
{
|
|
60
|
+
"from": "90",
|
|
61
|
+
"to": "91",
|
|
62
|
+
"smarthrUiAlias": "@/components/parts/smarthr-ui"
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
#### 動作
|
|
70
|
+
|
|
71
|
+
このオプションを指定すると、以下の3つが置換対象になります:
|
|
72
|
+
|
|
73
|
+
1. **`smarthr-ui` からの直接 import**(`smarthrUiAlias` 指定に関わらず常に置換)
|
|
74
|
+
```typescript
|
|
75
|
+
// Before
|
|
76
|
+
import { ActionDialog } from 'smarthr-ui'
|
|
77
|
+
// After
|
|
78
|
+
import { ControlledActionDialog } from 'smarthr-ui'
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
2. **alias パスからの import**
|
|
82
|
+
```typescript
|
|
83
|
+
// Before
|
|
84
|
+
import { ActionDialog } from '@/components/parts/smarthr-ui'
|
|
85
|
+
// After
|
|
86
|
+
import { ControlledActionDialog } from '@/components/parts/smarthr-ui'
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
3. **alias ファイル内の export 変数名**(smarthr-ui のコンポーネント名と同じ場合のみ)
|
|
90
|
+
```typescript
|
|
91
|
+
// @/components/parts/smarthr-ui/ActionDialog.tsx(aliasファイル)
|
|
92
|
+
|
|
93
|
+
// Before(v90 使用時)
|
|
94
|
+
import { ActionDialog as ShrActionDialog } from 'smarthr-ui'
|
|
95
|
+
export const ActionDialog = (props) => {
|
|
96
|
+
return <ShrActionDialog {...props} customProp="value" />
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// After(v91 移行後)
|
|
100
|
+
import { ControlledActionDialog as ShrActionDialog } from 'smarthr-ui'
|
|
101
|
+
export const ControlledActionDialog = (props) => {
|
|
102
|
+
return <ShrActionDialog {...props} customProp="value" />
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### barrel import 構造への対応
|
|
107
|
+
|
|
108
|
+
`smarthrUiAlias` で指定されたパス配下のすべてのファイルが置換対象になります。
|
|
109
|
+
|
|
110
|
+
**ディレクトリ形式:**
|
|
111
|
+
```
|
|
112
|
+
@/components/parts/smarthr-ui/
|
|
113
|
+
├── index.tsx # ✅ 置換対象
|
|
114
|
+
├── ActionDialog.tsx # ✅ 置換対象
|
|
115
|
+
├── FormDialog.tsx # ✅ 置換対象
|
|
116
|
+
└── dialogs/
|
|
117
|
+
└── MessageDialog.tsx # ✅ 置換対象(サブディレクトリも含む)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
// index.tsx(barrel export)
|
|
122
|
+
export { ActionDialog } from './ActionDialog'
|
|
123
|
+
export { FormDialog } from './FormDialog'
|
|
124
|
+
|
|
125
|
+
// または、smarthr-uiから直接re-export
|
|
126
|
+
export { ActionDialog } from 'smarthr-ui'
|
|
127
|
+
// → export { ControlledActionDialog } from 'smarthr-ui' に自動置換
|
|
128
|
+
|
|
129
|
+
// ActionDialog.tsx
|
|
130
|
+
import { ActionDialog as ShrActionDialog } from 'smarthr-ui'
|
|
131
|
+
export const ActionDialog = (props) => <ShrActionDialog {...props} />
|
|
132
|
+
// → export const ControlledActionDialog に自動置換
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**単一ファイル形式:**
|
|
136
|
+
```
|
|
137
|
+
@/components/parts/smarthr-ui.tsx # ✅ 置換対象
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
// smarthr-ui.tsx
|
|
142
|
+
export const ActionDialog = (props) => <div>{props.children}</div>
|
|
143
|
+
// → export const ControlledActionDialog に自動置換
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
#### 制限事項
|
|
147
|
+
|
|
148
|
+
- **対象ファイルの範囲:** `smarthrUiAlias` で指定されたパス配下のファイルのみ。他のディレクトリにある同名の export は変更されません
|
|
149
|
+
```typescript
|
|
150
|
+
// src/components/parts/smarthr-ui/index.tsx → 置換される ✅
|
|
151
|
+
// src/features/custom/ActionDialog.tsx → 置換されない ✅
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
- **変数名の判定:** smarthr-ui が提供するコンポーネント名と完全一致する export 変数名のみ置換
|
|
155
|
+
```typescript
|
|
156
|
+
export const ActionDialog = ... // ✅ 置換される
|
|
157
|
+
export const MyActionDialog = ... // ❌ 置換されない
|
|
158
|
+
export const CustomDialog = ... // ❌ 置換されない
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
- **export 形式:** 現在は `export const` 形式のみサポート
|
|
162
|
+
```typescript
|
|
163
|
+
export const ActionDialog = ... // ✅ サポート
|
|
164
|
+
export function ActionDialog() // ❌ 未サポート
|
|
165
|
+
export class ActionDialog // ❌ 未サポート
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
- **ファイル名の変更:** ファイル名が変更対象のコンポーネント名と一致する場合、ファイル名の変更を促すエラーが表示されます(自動修正不可)
|
|
169
|
+
```
|
|
170
|
+
// エラー例
|
|
171
|
+
smarthr-ui v91 では ActionDialog が ControlledActionDialog にリネームされました。
|
|
172
|
+
以下の手順で対応してください:
|
|
173
|
+
1. ファイル名を変更(例: git mv ActionDialog.tsx ControlledActionDialog.tsx)
|
|
174
|
+
2. このファイルをimportしている箇所を更新(例: from '@/path/ActionDialog' → from '@/path/ControlledActionDialog')
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**対応手順:**
|
|
178
|
+
1. ファイル名を変更: `git mv ActionDialog.tsx ControlledActionDialog.tsx`
|
|
179
|
+
2. このファイルをimportしている箇所を手動で更新:
|
|
180
|
+
```typescript
|
|
181
|
+
// Before
|
|
182
|
+
import { FormDialog } from '@/components/parts/smarthr-ui/FormDialog'
|
|
183
|
+
|
|
184
|
+
// After
|
|
185
|
+
import { ControlledFormDialog } from '@/components/parts/smarthr-ui/ControlledFormDialog'
|
|
186
|
+
```
|
|
187
|
+
|
|
34
188
|
## サポートされているバージョン
|
|
35
189
|
|
|
36
190
|
各バージョンの破壊的変更の詳細と対応内容については、リンク先の移行ガイドを参照してください。
|
|
@@ -38,6 +38,9 @@ module.exports = {
|
|
|
38
38
|
type: 'string',
|
|
39
39
|
pattern: '^[0-9]+$',
|
|
40
40
|
},
|
|
41
|
+
smarthrUiAlias: {
|
|
42
|
+
type: 'string',
|
|
43
|
+
},
|
|
41
44
|
},
|
|
42
45
|
required: ['from', 'to'],
|
|
43
46
|
additionalProperties: false,
|
|
@@ -90,7 +93,7 @@ module.exports = {
|
|
|
90
93
|
// 例: v90→v92 なら [v90→v91のチェッカー] を収集
|
|
91
94
|
const checkersList = path.map((stepKey) => {
|
|
92
95
|
const module = VERSION_MODULES[stepKey]
|
|
93
|
-
return module.createCheckers(context, sourceCode)
|
|
96
|
+
return module.createCheckers(context, sourceCode, options)
|
|
94
97
|
})
|
|
95
98
|
|
|
96
99
|
const mergedCheckers = mergeCheckers(checkersList)
|
|
@@ -77,11 +77,36 @@ module.exports = {
|
|
|
77
77
|
|
|
78
78
|
ESLintのセレクターとハンドラーを返す関数です。
|
|
79
79
|
|
|
80
|
+
**シグネチャ:**
|
|
80
81
|
```javascript
|
|
81
|
-
createCheckers(context, sourceCode
|
|
82
|
-
|
|
82
|
+
createCheckers(context, sourceCode, options = {})
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- `context`: ESLintのcontext
|
|
86
|
+
- `sourceCode`: ESLintのsourceCode
|
|
87
|
+
- `options`: ユーザーが指定したオプション(`smarthrUiAlias`など)
|
|
88
|
+
|
|
89
|
+
**基本構造:**
|
|
90
|
+
```javascript
|
|
91
|
+
createCheckers(context, sourceCode, options = {}) {
|
|
92
|
+
// smarthrUiAliasオプションの取得
|
|
93
|
+
const customSmarthrUiAlias = options.smarthrUiAlias
|
|
94
|
+
const validSources = ['smarthr-ui']
|
|
95
|
+
if (customSmarthrUiAlias) {
|
|
96
|
+
validSources.push(customSmarthrUiAlias)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// aliasファイルかどうかの判定
|
|
100
|
+
const isAliasFile = customSmarthrUiAlias && isFileMatchingSmarthrUiAlias(
|
|
101
|
+
context.getFilename(),
|
|
102
|
+
customSmarthrUiAlias
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
const checkers = {
|
|
83
106
|
// インポート文の処理
|
|
84
107
|
ImportDeclaration(node) {
|
|
108
|
+
// smarthr-ui + smarthrUiAlias の両方をチェック
|
|
109
|
+
if (!validSources.includes(node.source.value)) return
|
|
85
110
|
// ...
|
|
86
111
|
},
|
|
87
112
|
|
|
@@ -95,6 +120,15 @@ createCheckers(context, sourceCode) {
|
|
|
95
120
|
// ...
|
|
96
121
|
},
|
|
97
122
|
}
|
|
123
|
+
|
|
124
|
+
// aliasファイルの場合のみ、export変数名の置換を追加
|
|
125
|
+
if (isAliasFile) {
|
|
126
|
+
checkers['ExportNamedDeclaration > VariableDeclaration > VariableDeclarator'] = function(node) {
|
|
127
|
+
// export const ActionDialog = ... を置換
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return checkers
|
|
98
132
|
}
|
|
99
133
|
```
|
|
100
134
|
|
|
@@ -156,13 +190,18 @@ function findTargetParent(node, sourceCode) {
|
|
|
156
190
|
|
|
157
191
|
### ✅ 自動修正可能
|
|
158
192
|
|
|
159
|
-
機械的に100
|
|
193
|
+
機械的に100%正しく変換できる場合に自動修正を実装します。ただし、実用上は100%の正確性が保証できない場合でも、以下の条件を満たせば自動修正を実装して構いません:
|
|
194
|
+
|
|
195
|
+
- **実際には使用されていない機能や属性**である可能性が高い
|
|
196
|
+
- 置換することで**利便性が明らかに向上する**
|
|
197
|
+
- 誤った変換が行われても**影響が限定的**である
|
|
160
198
|
|
|
161
199
|
**例:**
|
|
162
200
|
- コンポーネント名のリネーム(ActionDialog → ControlledActionDialog)
|
|
163
201
|
- 属性名のリネーム(type → status)
|
|
164
202
|
- 属性の削除(値が変わらない場合)
|
|
165
203
|
- **未知の属性を保持したまま移行可能な場合**(Text → span など、移行先が明確な置換)
|
|
204
|
+
- **理論的には100%正しくないが、実用上問題ない場合**(使用頻度が極めて低い機能の置換など)
|
|
166
205
|
|
|
167
206
|
### ⚠️ エラーのみ(自動修正なし)
|
|
168
207
|
|
|
@@ -437,6 +476,200 @@ function hasUnknownAttributes(node, ...knownAttrs) {
|
|
|
437
476
|
- **属性の移行先が明確** → パターンA(未知の属性も保持して自動修正)
|
|
438
477
|
- **属性の移行先が不明確** → パターンB(エラーのみ、手動対応)
|
|
439
478
|
|
|
479
|
+
### パターン6: smarthrUiAlias オプションへの対応
|
|
480
|
+
|
|
481
|
+
プロジェクト固有のsmarthr-ui aliasパスに対応するため、`smarthrUiAlias`オプションを利用します。
|
|
482
|
+
|
|
483
|
+
#### validSourcesの拡張
|
|
484
|
+
|
|
485
|
+
`smarthr-ui`に加えて、aliasパスからのimportもチェック対象にします。
|
|
486
|
+
|
|
487
|
+
```javascript
|
|
488
|
+
createCheckers(context, sourceCode, options = {}) {
|
|
489
|
+
const customSmarthrUiAlias = options.smarthrUiAlias
|
|
490
|
+
const validSources = ['smarthr-ui']
|
|
491
|
+
if (customSmarthrUiAlias) {
|
|
492
|
+
validSources.push(customSmarthrUiAlias)
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
return {
|
|
496
|
+
ImportDeclaration(node) {
|
|
497
|
+
// smarthr-ui または @/components/parts/smarthr-ui からのimport
|
|
498
|
+
if (!validSources.includes(node.source.value)) return
|
|
499
|
+
|
|
500
|
+
// ...置換処理
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
#### aliasファイル内のexport変数名置換
|
|
507
|
+
|
|
508
|
+
aliasディレクトリ配下のファイルで、smarthr-uiコンポーネント名と同じ変数名をexportしている場合に置換します。
|
|
509
|
+
|
|
510
|
+
```javascript
|
|
511
|
+
// aliasファイルかどうかの判定
|
|
512
|
+
const isAliasFile = customSmarthrUiAlias && isFileMatchingSmarthrUiAlias(
|
|
513
|
+
context.getFilename(),
|
|
514
|
+
customSmarthrUiAlias
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
const checkers = {
|
|
518
|
+
// ... 通常のチェッカー
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// aliasファイルの場合のみ、export変数名の置換を追加
|
|
522
|
+
if (isAliasFile) {
|
|
523
|
+
checkers['ExportNamedDeclaration > VariableDeclaration > VariableDeclarator'] = function(node) {
|
|
524
|
+
const variableName = node.id.name
|
|
525
|
+
const newName = DIALOG_COMPONENTS[variableName]
|
|
526
|
+
|
|
527
|
+
if (newName) {
|
|
528
|
+
context.report({
|
|
529
|
+
node: node.id,
|
|
530
|
+
messageId: 'renameDialog',
|
|
531
|
+
data: { old: variableName, new: newName, to: TARGET_VERSION },
|
|
532
|
+
fix(fixer) {
|
|
533
|
+
return fixer.replaceText(node.id, newName)
|
|
534
|
+
},
|
|
535
|
+
})
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
return checkers
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
#### ファイルパスのマッチング(ヘルパー関数)
|
|
544
|
+
|
|
545
|
+
```javascript
|
|
546
|
+
const { rootPath } = require('../../../../libs/common')
|
|
547
|
+
|
|
548
|
+
function isFileMatchingSmarthrUiAlias(filename, smarthrUiAlias) {
|
|
549
|
+
// rootPathを使って絶対パスで比較を試みる
|
|
550
|
+
const resolved = smarthrUiAlias.replace(/^@\//, `${rootPath}/`)
|
|
551
|
+
if (filename.includes(resolved)) {
|
|
552
|
+
return true
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// rootPathでマッチしない場合:
|
|
556
|
+
// パスの一部としてマッチング(テスト環境などで使用)
|
|
557
|
+
const pathPart = smarthrUiAlias.replace(/^@\//, '').replace(/^~\//, '')
|
|
558
|
+
|
|
559
|
+
// 以下のパターンにマッチング:
|
|
560
|
+
// 1. ディレクトリ形式: /components/parts/smarthr-ui/index.tsx
|
|
561
|
+
// 2. 個別ファイル: /components/parts/smarthr-ui/ActionDialog.tsx
|
|
562
|
+
// 3. 単一ファイル形式: /components/parts/smarthr-ui.tsx
|
|
563
|
+
return (
|
|
564
|
+
filename.includes(`/${pathPart}/`) ||
|
|
565
|
+
filename.endsWith(`/${pathPart}`) ||
|
|
566
|
+
filename.includes(`/${pathPart}.`)
|
|
567
|
+
)
|
|
568
|
+
}
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
**このパターンが適用されるケース:**
|
|
572
|
+
- **barrel import構造**: `@/components/parts/smarthr-ui/index.tsx` + 個別ファイル
|
|
573
|
+
- **個別ファイルのみ**: `@/components/parts/smarthr-ui/ActionDialog.tsx` など
|
|
574
|
+
- **単一ファイル形式**: `@/components/parts/smarthr-ui.tsx` (ディレクトリではなく1つのファイル)
|
|
575
|
+
|
|
576
|
+
**ポイント:**
|
|
577
|
+
- importチェックは`validSources`で拡張
|
|
578
|
+
- export変数名の置換は`isAliasFile`条件付きで追加
|
|
579
|
+
- サブディレクトリも含めてマッチング(`filename.includes(resolved)`)
|
|
580
|
+
- 単一ファイル形式にも対応(`filename.includes(\`/\${pathPart}.\`)`)
|
|
581
|
+
|
|
582
|
+
### パターン7: aliasファイル名の変更チェック
|
|
583
|
+
|
|
584
|
+
aliasファイルのファイル名が変更対象のコンポーネント名と一致する場合、ファイル名の変更を促すエラーを表示します。
|
|
585
|
+
|
|
586
|
+
```javascript
|
|
587
|
+
checkers.Program = function(node) {
|
|
588
|
+
if (!isAliasFile) return
|
|
589
|
+
|
|
590
|
+
// ファイル名からコンポーネント名を抽出(拡張子を除く)
|
|
591
|
+
const fileBasename = filename.split('/').pop() || ''
|
|
592
|
+
const componentName = fileBasename.replace(/\.(tsx?|jsx?)$/, '')
|
|
593
|
+
|
|
594
|
+
// Dialog系コンポーネント名と一致するかチェック
|
|
595
|
+
const newName = DIALOG_COMPONENTS[componentName]
|
|
596
|
+
if (newName) {
|
|
597
|
+
const oldFile = fileBasename
|
|
598
|
+
const newFile = fileBasename.replace(componentName, newName)
|
|
599
|
+
|
|
600
|
+
context.report({
|
|
601
|
+
node,
|
|
602
|
+
messageId: 'renameAliasFile',
|
|
603
|
+
data: {
|
|
604
|
+
old: componentName,
|
|
605
|
+
new: newName,
|
|
606
|
+
to: TARGET_VERSION,
|
|
607
|
+
oldFile,
|
|
608
|
+
newFile,
|
|
609
|
+
},
|
|
610
|
+
// fixは提供しない(ファイル名の変更はESLintでは不可能)
|
|
611
|
+
})
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
**このパターンが必要なケース:**
|
|
617
|
+
- aliasファイルのファイル名がコンポーネント名と一致している場合
|
|
618
|
+
- 例: `ActionDialog.tsx` → `ControlledActionDialog.tsx` に変更が必要
|
|
619
|
+
|
|
620
|
+
**ポイント:**
|
|
621
|
+
- `Program` ノードに対してチェック(ファイルごとに1回だけ実行)
|
|
622
|
+
- ファイル名の変更はESLintでは不可能なので、fix関数は提供しない
|
|
623
|
+
- エラーメッセージに新旧のファイル名を含める
|
|
624
|
+
- export変数名の置換とは別のエラーとして表示される
|
|
625
|
+
|
|
626
|
+
**エラーメッセージに含める情報:**
|
|
627
|
+
- ファイル名の変更手順(`git mv` の例)
|
|
628
|
+
- ファイル名変更後、import文も更新が必要であることを明記
|
|
629
|
+
|
|
630
|
+
**注意:**
|
|
631
|
+
- re-export(`export { ActionDialog } from 'smarthr-ui'`)にも対応するため、`ExportNamedDeclaration`チェッカーも実装が必要
|
|
632
|
+
- ファイル名変更後、そのファイルをimportしている箇所も手動で更新する必要がある
|
|
633
|
+
```typescript
|
|
634
|
+
// Before
|
|
635
|
+
import { FormDialog } from '@/components/parts/smarthr-ui/FormDialog'
|
|
636
|
+
|
|
637
|
+
// After (ファイル名変更後)
|
|
638
|
+
import { ControlledFormDialog } from '@/components/parts/smarthr-ui/ControlledFormDialog'
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
```javascript
|
|
642
|
+
ExportNamedDeclaration(node) {
|
|
643
|
+
// sourceがない場合(通常のexport)はスキップ
|
|
644
|
+
if (!node.source) return
|
|
645
|
+
if (!validSources.includes(node.source.value)) return
|
|
646
|
+
|
|
647
|
+
node.specifiers.forEach((specifier) => {
|
|
648
|
+
if (specifier.type !== 'ExportSpecifier') return
|
|
649
|
+
|
|
650
|
+
const exportedName = specifier.exported.name
|
|
651
|
+
const localName = specifier.local.name
|
|
652
|
+
const newName = DIALOG_COMPONENTS[localName]
|
|
653
|
+
|
|
654
|
+
if (newName) {
|
|
655
|
+
context.report({
|
|
656
|
+
node: specifier,
|
|
657
|
+
messageId: 'renameDialog',
|
|
658
|
+
data: { old: localName, new: newName, to: TARGET_VERSION },
|
|
659
|
+
fix(fixer) {
|
|
660
|
+
// export { ActionDialog } のように local === exported の場合
|
|
661
|
+
if (localName === exportedName) {
|
|
662
|
+
return fixer.replaceText(specifier, newName)
|
|
663
|
+
}
|
|
664
|
+
// export { ActionDialog as MyDialog } のような場合
|
|
665
|
+
return fixer.replaceText(specifier.local, newName)
|
|
666
|
+
},
|
|
667
|
+
})
|
|
668
|
+
}
|
|
669
|
+
})
|
|
670
|
+
}
|
|
671
|
+
```
|
|
672
|
+
|
|
440
673
|
## トラブルシューティング
|
|
441
674
|
|
|
442
675
|
### Fix objects must not be overlapped
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
* 参考: https://github.com/kufu/smarthr-ui/releases/tag/smarthr-ui-v91.0.0
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
const { rootPath } = require('../../../../libs/common')
|
|
17
|
+
|
|
16
18
|
// ============================================================
|
|
17
19
|
// 定数定義
|
|
18
20
|
// ============================================================
|
|
@@ -51,10 +53,56 @@ module.exports = {
|
|
|
51
53
|
migrateResponseMessage: '見出し/ラベル内の ResponseMessage は親コンポーネントの icon 属性に移行してください',
|
|
52
54
|
migrateResponseMessageWithUnknownAttrs: '見出し/ラベル内の ResponseMessage は親コンポーネントの icon 属性に移行してください。status/iconGap 以外の属性(id, onClick など)がある場合は手動で移行してください',
|
|
53
55
|
removeArbitraryDisplayName: 'AppHeader の arbitraryDisplayName 属性は削除されました。email, empCode, firstName, lastName から自動生成されます',
|
|
56
|
+
renameAliasFile: 'smarthr-ui {{to}} では {{old}} が {{new}} にリネームされました。以下の手順で対応してください: 1. ファイル名を変更(例: git mv {{oldFile}} {{newFile}})2. このファイルをimportしている箇所を更新(例: from \'@/path/{{old}}\' → from \'@/path/{{new}}\')',
|
|
54
57
|
},
|
|
55
58
|
|
|
56
|
-
createCheckers(context, sourceCode) {
|
|
57
|
-
|
|
59
|
+
createCheckers(context, sourceCode, options = {}) {
|
|
60
|
+
const customSmarthrUiAlias = options.smarthrUiAlias
|
|
61
|
+
const validSources = ['smarthr-ui']
|
|
62
|
+
if (customSmarthrUiAlias) {
|
|
63
|
+
validSources.push(customSmarthrUiAlias)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 現在のファイルがaliasファイルか判定
|
|
67
|
+
const filename = context.getFilename()
|
|
68
|
+
const isAliasFile = customSmarthrUiAlias && isFileMatchingSmarthrUiAlias(
|
|
69
|
+
filename,
|
|
70
|
+
customSmarthrUiAlias
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
const checkers = {
|
|
74
|
+
// ============================================================
|
|
75
|
+
// 0. aliasファイル名の変更チェック
|
|
76
|
+
// ============================================================
|
|
77
|
+
|
|
78
|
+
// aliasファイルの場合、ファイル名も変更を促す
|
|
79
|
+
Program(node) {
|
|
80
|
+
if (!isAliasFile) return
|
|
81
|
+
|
|
82
|
+
// ファイル名からコンポーネント名を抽出(拡張子を除く)
|
|
83
|
+
const fileBasename = filename.split('/').pop() || ''
|
|
84
|
+
const componentName = fileBasename.replace(/\.(tsx?|jsx?)$/, '')
|
|
85
|
+
|
|
86
|
+
// Dialog系コンポーネント名と一致するかチェック
|
|
87
|
+
const newName = DIALOG_COMPONENTS[componentName]
|
|
88
|
+
if (newName) {
|
|
89
|
+
const oldFile = fileBasename
|
|
90
|
+
const newFile = fileBasename.replace(componentName, newName)
|
|
91
|
+
|
|
92
|
+
context.report({
|
|
93
|
+
node,
|
|
94
|
+
messageId: 'renameAliasFile',
|
|
95
|
+
data: {
|
|
96
|
+
old: componentName,
|
|
97
|
+
new: newName,
|
|
98
|
+
to: TARGET_VERSION,
|
|
99
|
+
oldFile,
|
|
100
|
+
newFile,
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
|
|
58
106
|
// ============================================================
|
|
59
107
|
// 1. Dialogコンポーネントのリネーム
|
|
60
108
|
// ============================================================
|
|
@@ -63,7 +111,7 @@ module.exports = {
|
|
|
63
111
|
// 例: import { ActionDialog } from 'smarthr-ui'
|
|
64
112
|
// → import { ControlledActionDialog } from 'smarthr-ui'
|
|
65
113
|
ImportDeclaration(node) {
|
|
66
|
-
if (node.source.value
|
|
114
|
+
if (!validSources.includes(node.source.value)) return
|
|
67
115
|
|
|
68
116
|
node.specifiers.forEach((specifier) => {
|
|
69
117
|
if (specifier.type !== 'ImportSpecifier') return
|
|
@@ -84,6 +132,39 @@ module.exports = {
|
|
|
84
132
|
})
|
|
85
133
|
},
|
|
86
134
|
|
|
135
|
+
// export文での検出と修正(re-export)
|
|
136
|
+
// 例: export { ActionDialog } from 'smarthr-ui'
|
|
137
|
+
// → export { ControlledActionDialog } from 'smarthr-ui'
|
|
138
|
+
ExportNamedDeclaration(node) {
|
|
139
|
+
// sourceがない場合(通常のexport)はスキップ
|
|
140
|
+
if (!node.source) return
|
|
141
|
+
if (!validSources.includes(node.source.value)) return
|
|
142
|
+
|
|
143
|
+
node.specifiers.forEach((specifier) => {
|
|
144
|
+
if (specifier.type !== 'ExportSpecifier') return
|
|
145
|
+
|
|
146
|
+
const exportedName = specifier.exported.name
|
|
147
|
+
const localName = specifier.local.name
|
|
148
|
+
const newName = DIALOG_COMPONENTS[localName]
|
|
149
|
+
|
|
150
|
+
if (newName) {
|
|
151
|
+
context.report({
|
|
152
|
+
node: specifier,
|
|
153
|
+
messageId: 'renameDialog',
|
|
154
|
+
data: { old: localName, new: newName, to: TARGET_VERSION },
|
|
155
|
+
fix(fixer) {
|
|
156
|
+
// export { ActionDialog } のように local === exported の場合
|
|
157
|
+
if (localName === exportedName) {
|
|
158
|
+
return fixer.replaceText(specifier, newName)
|
|
159
|
+
}
|
|
160
|
+
// export { ActionDialog as MyDialog } のような場合
|
|
161
|
+
return fixer.replaceText(specifier.local, newName)
|
|
162
|
+
},
|
|
163
|
+
})
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
},
|
|
167
|
+
|
|
87
168
|
// JSX要素での検出と修正
|
|
88
169
|
// 例: <ActionDialog>...</ActionDialog>
|
|
89
170
|
// → <ControlledActionDialog>...</ControlledActionDialog>
|
|
@@ -467,5 +548,60 @@ module.exports = {
|
|
|
467
548
|
return fixer.replaceText(attr.value, newValue)
|
|
468
549
|
}
|
|
469
550
|
}
|
|
551
|
+
|
|
552
|
+
// ============================================================
|
|
553
|
+
// aliasファイル内のexport変数名の置換
|
|
554
|
+
// ============================================================
|
|
555
|
+
|
|
556
|
+
// aliasファイルの場合のみ、export変数名を置換
|
|
557
|
+
if (isAliasFile) {
|
|
558
|
+
checkers['ExportNamedDeclaration > VariableDeclaration > VariableDeclarator'] = function (node) {
|
|
559
|
+
const variableName = node.id.name
|
|
560
|
+
const newName = DIALOG_COMPONENTS[variableName]
|
|
561
|
+
|
|
562
|
+
if (newName) {
|
|
563
|
+
context.report({
|
|
564
|
+
node: node.id,
|
|
565
|
+
messageId: 'renameDialog',
|
|
566
|
+
data: { old: variableName, new: newName, to: TARGET_VERSION },
|
|
567
|
+
fix(fixer) {
|
|
568
|
+
return fixer.replaceText(node.id, newName)
|
|
569
|
+
},
|
|
570
|
+
})
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
return checkers
|
|
470
576
|
},
|
|
471
577
|
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* smarthrUiAliasで指定されたパスと現在のファイルパスがマッチするか判定
|
|
581
|
+
*
|
|
582
|
+
* @param {string} filename - 現在処理中のファイルパス
|
|
583
|
+
* @param {string} smarthrUiAlias - smarthrUiAliasオプションの値(例: '@/components/parts/smarthr-ui')
|
|
584
|
+
* @returns {boolean} マッチする場合true
|
|
585
|
+
*/
|
|
586
|
+
function isFileMatchingSmarthrUiAlias(filename, smarthrUiAlias) {
|
|
587
|
+
// rootPathを使って絶対パスで比較を試みる
|
|
588
|
+
const resolved = smarthrUiAlias.replace(/^@\//, `${rootPath}/`)
|
|
589
|
+
if (filename.includes(resolved)) {
|
|
590
|
+
return true
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// rootPathでマッチしない場合:
|
|
594
|
+
// パスの一部としてマッチング(テスト環境などで使用)
|
|
595
|
+
// 例: '@/components/parts/smarthr-ui' -> 'components/parts/smarthr-ui'
|
|
596
|
+
const pathPart = smarthrUiAlias.replace(/^@\//, '').replace(/^~\//, '')
|
|
597
|
+
|
|
598
|
+
// 以下のパターンにマッチング:
|
|
599
|
+
// 1. ディレクトリ形式: /components/parts/smarthr-ui/index.tsx
|
|
600
|
+
// 2. 個別ファイル: /components/parts/smarthr-ui/ActionDialog.tsx
|
|
601
|
+
// 3. 単一ファイル形式: /components/parts/smarthr-ui.tsx
|
|
602
|
+
return (
|
|
603
|
+
filename.includes(`/${pathPart}/`) ||
|
|
604
|
+
filename.endsWith(`/${pathPart}`) ||
|
|
605
|
+
filename.includes(`/${pathPart}.`)
|
|
606
|
+
)
|
|
607
|
+
}
|
|
@@ -48,6 +48,43 @@ module.exports = {
|
|
|
48
48
|
{ code: `<ResponseMessage status="success">Xxxx</ResponseMessage>`, options: v90ToV91Options },
|
|
49
49
|
{ code: `<Heading icon={{ prefix: <FaCheckIcon />, gap: 0.5 }}>Xxxx</Heading>`, options: v90ToV91Options },
|
|
50
50
|
{ code: `<AppHeader email="test@example.com" />`, options: v90ToV91Options },
|
|
51
|
+
|
|
52
|
+
// smarthrUiAliasオプション: aliasファイル外では置換しない
|
|
53
|
+
{
|
|
54
|
+
code: `export const ActionDialog = (props) => <div>{props.children}</div>`,
|
|
55
|
+
filename: '/Users/test/src/features/custom/ActionDialog.tsx',
|
|
56
|
+
options: [{ from: '90', to: '91', smarthrUiAlias: '@/components/parts/smarthr-ui' }],
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
// smarthrUiAliasオプション: v91形式は正常
|
|
60
|
+
{
|
|
61
|
+
code: `import { ControlledActionDialog } from '@/components/parts/smarthr-ui'`,
|
|
62
|
+
options: [{ from: '90', to: '91', smarthrUiAlias: '@/components/parts/smarthr-ui' }],
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
code: `export const ControlledActionDialog = (props) => <div>{props.children}</div>`,
|
|
66
|
+
filename: '/Users/test/src/components/parts/smarthr-ui/ControlledActionDialog.tsx',
|
|
67
|
+
options: [{ from: '90', to: '91', smarthrUiAlias: '@/components/parts/smarthr-ui' }],
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
// smarthrUiAliasオプション: カスタム名のexportは置換しない
|
|
71
|
+
{
|
|
72
|
+
code: `export const MyActionDialog = (props) => <div>{props.children}</div>`,
|
|
73
|
+
filename: '/Users/test/src/components/parts/smarthr-ui/MyActionDialog.tsx',
|
|
74
|
+
options: [{ from: '90', to: '91', smarthrUiAlias: '@/components/parts/smarthr-ui' }],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
code: `export const CustomDialog = (props) => <div>{props.children}</div>`,
|
|
78
|
+
filename: '/Users/test/src/components/parts/smarthr-ui/CustomDialog.tsx',
|
|
79
|
+
options: [{ from: '90', to: '91', smarthrUiAlias: '@/components/parts/smarthr-ui' }],
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
// smarthrUiAliasオプション: 単一ファイル形式でもv91形式は正常
|
|
83
|
+
{
|
|
84
|
+
code: `export const ControlledFormDialog = (props) => <div>{props.children}</div>`,
|
|
85
|
+
filename: '/Users/test/src/components/parts/smarthr-ui.tsx',
|
|
86
|
+
options: [{ from: '90', to: '91', smarthrUiAlias: '@/components/parts/smarthr-ui' }],
|
|
87
|
+
},
|
|
51
88
|
],
|
|
52
89
|
|
|
53
90
|
invalid: [
|
|
@@ -217,5 +254,80 @@ module.exports = {
|
|
|
217
254
|
options: v90ToV91Options,
|
|
218
255
|
errors: [{ messageId: 'removeArbitraryDisplayName' }],
|
|
219
256
|
},
|
|
257
|
+
|
|
258
|
+
// ============================================================
|
|
259
|
+
// 6. smarthrUiAliasオプション
|
|
260
|
+
// ============================================================
|
|
261
|
+
// aliasからのimport
|
|
262
|
+
{
|
|
263
|
+
code: `import { ActionDialog } from '@/components/parts/smarthr-ui'`,
|
|
264
|
+
output: `import { ControlledActionDialog } from '@/components/parts/smarthr-ui'`,
|
|
265
|
+
options: [{ from: '90', to: '91', smarthrUiAlias: '@/components/parts/smarthr-ui' }],
|
|
266
|
+
errors: [{ messageId: 'renameDialog', data: { old: 'ActionDialog', new: 'ControlledActionDialog', to: 'v91' } }],
|
|
267
|
+
},
|
|
268
|
+
// aliasファイル内のexport変数名置換(index.tsx)
|
|
269
|
+
{
|
|
270
|
+
code: `export const FormDialog = (props) => <div>{props.children}</div>`,
|
|
271
|
+
output: `export const ControlledFormDialog = (props) => <div>{props.children}</div>`,
|
|
272
|
+
filename: '/Users/test/src/components/parts/smarthr-ui/index.tsx',
|
|
273
|
+
options: [{ from: '90', to: '91', smarthrUiAlias: '@/components/parts/smarthr-ui' }],
|
|
274
|
+
errors: [{ messageId: 'renameDialog', data: { old: 'FormDialog', new: 'ControlledFormDialog', to: 'v91' } }],
|
|
275
|
+
},
|
|
276
|
+
// barrel import: smarthr-uiからimport + export(個別ファイル)
|
|
277
|
+
{
|
|
278
|
+
code: `import { ActionDialog as ShrActionDialog } from 'smarthr-ui'\nexport const ActionDialog = (props) => <ShrActionDialog {...props} />`,
|
|
279
|
+
output: `import { ControlledActionDialog as ShrActionDialog } from 'smarthr-ui'\nexport const ControlledActionDialog = (props) => <ShrActionDialog {...props} />`,
|
|
280
|
+
filename: '/Users/test/src/components/parts/smarthr-ui/ActionDialog.tsx',
|
|
281
|
+
options: [{ from: '90', to: '91', smarthrUiAlias: '@/components/parts/smarthr-ui' }],
|
|
282
|
+
errors: [
|
|
283
|
+
{ messageId: 'renameAliasFile', data: { old: 'ActionDialog', new: 'ControlledActionDialog', to: 'v91', oldFile: 'ActionDialog.tsx', newFile: 'ControlledActionDialog.tsx' } },
|
|
284
|
+
{ messageId: 'renameDialog', data: { old: 'ActionDialog', new: 'ControlledActionDialog', to: 'v91' } },
|
|
285
|
+
{ messageId: 'renameDialog', data: { old: 'ActionDialog', new: 'ControlledActionDialog', to: 'v91' } },
|
|
286
|
+
],
|
|
287
|
+
},
|
|
288
|
+
// サブディレクトリ内のファイルも対象
|
|
289
|
+
{
|
|
290
|
+
code: `export const MessageDialog = (props) => <div>{props.children}</div>`,
|
|
291
|
+
output: `export const ControlledMessageDialog = (props) => <div>{props.children}</div>`,
|
|
292
|
+
filename: '/Users/test/src/components/parts/smarthr-ui/dialogs/MessageDialog.tsx',
|
|
293
|
+
options: [{ from: '90', to: '91', smarthrUiAlias: '@/components/parts/smarthr-ui' }],
|
|
294
|
+
errors: [
|
|
295
|
+
{ messageId: 'renameAliasFile', data: { old: 'MessageDialog', new: 'ControlledMessageDialog', to: 'v91', oldFile: 'MessageDialog.tsx', newFile: 'ControlledMessageDialog.tsx' } },
|
|
296
|
+
{ messageId: 'renameDialog', data: { old: 'MessageDialog', new: 'ControlledMessageDialog', to: 'v91' } },
|
|
297
|
+
],
|
|
298
|
+
},
|
|
299
|
+
// 単一ファイル形式も対象
|
|
300
|
+
{
|
|
301
|
+
code: `export const FormDialog = (props) => <div>{props.children}</div>`,
|
|
302
|
+
output: `export const ControlledFormDialog = (props) => <div>{props.children}</div>`,
|
|
303
|
+
filename: '/Users/test/src/components/parts/smarthr-ui.tsx',
|
|
304
|
+
options: [{ from: '90', to: '91', smarthrUiAlias: '@/components/parts/smarthr-ui' }],
|
|
305
|
+
errors: [{ messageId: 'renameDialog', data: { old: 'FormDialog', new: 'ControlledFormDialog', to: 'v91' } }],
|
|
306
|
+
},
|
|
307
|
+
|
|
308
|
+
// ============================================================
|
|
309
|
+
// 7. aliasファイル名の変更
|
|
310
|
+
// ============================================================
|
|
311
|
+
// ファイル名がDialog系コンポーネント名の場合、ファイル名変更を促す
|
|
312
|
+
{
|
|
313
|
+
code: `// ActionDialog.tsx\nexport { ActionDialog } from 'smarthr-ui'`,
|
|
314
|
+
output: `// ActionDialog.tsx\nexport { ControlledActionDialog } from 'smarthr-ui'`,
|
|
315
|
+
filename: '/Users/test/src/components/parts/smarthr-ui/ActionDialog.tsx',
|
|
316
|
+
options: [{ from: '90', to: '91', smarthrUiAlias: '@/components/parts/smarthr-ui' }],
|
|
317
|
+
errors: [
|
|
318
|
+
{ messageId: 'renameAliasFile', data: { old: 'ActionDialog', new: 'ControlledActionDialog', to: 'v91', oldFile: 'ActionDialog.tsx', newFile: 'ControlledActionDialog.tsx' } },
|
|
319
|
+
{ messageId: 'renameDialog', data: { old: 'ActionDialog', new: 'ControlledActionDialog', to: 'v91' } },
|
|
320
|
+
],
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
code: `// FormDialog.tsx\nconst foo = 'bar'`,
|
|
324
|
+
output: null,
|
|
325
|
+
filename: '/Users/test/src/components/parts/smarthr-ui/FormDialog.tsx',
|
|
326
|
+
options: [{ from: '90', to: '91', smarthrUiAlias: '@/components/parts/smarthr-ui' }],
|
|
327
|
+
errors: [
|
|
328
|
+
{ messageId: 'renameAliasFile', data: { old: 'FormDialog', new: 'ControlledFormDialog', to: 'v91', oldFile: 'FormDialog.tsx', newFile: 'ControlledFormDialog.tsx' } },
|
|
329
|
+
],
|
|
330
|
+
},
|
|
331
|
+
|
|
220
332
|
],
|
|
221
333
|
}
|
|
@@ -177,16 +177,37 @@ module.exports = {
|
|
|
177
177
|
// パターン2-1: classNameのみ(asなし)、Text属性なし、shr-クラスあり
|
|
178
178
|
[SELECTOR_CONVERTIBLE_SHR_TO_PROPS]: (classNameAttrNode) => {
|
|
179
179
|
const { nonConvertible, propSuggestions, convertible } = categorizeClassNames(classNameAttrNode)
|
|
180
|
+
const openingElement = classNameAttrNode.parent
|
|
181
|
+
const jsxElement = openingElement.parent
|
|
182
|
+
|
|
183
|
+
// 変換可能なクラスが0個の場合、spanに変換(パターン1-3と同じ動作)
|
|
184
|
+
if (!propSuggestions) {
|
|
185
|
+
const classNameText = `className="${classNameAttrNode.value.value}"`
|
|
186
|
+
context.report({
|
|
187
|
+
node: openingElement,
|
|
188
|
+
message: `Textコンポーネントの機能を使用していないため、ネイティブHTML要素(<span>)に置き換えてください。
|
|
189
|
+
- 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-text-component
|
|
190
|
+
- 推奨: <span ${classNameText}>
|
|
191
|
+
- Textコンポーネントの機能(weight、size、color等)を使用しない場合は、直接HTML要素を使用することでシンプルになります`,
|
|
192
|
+
fix(fixer) {
|
|
193
|
+
return [
|
|
194
|
+
fixer.replaceText(openingElement.name, 'span'),
|
|
195
|
+
fixer.replaceText(jsxElement.closingElement.name, 'span')
|
|
196
|
+
]
|
|
197
|
+
},
|
|
198
|
+
})
|
|
199
|
+
return
|
|
200
|
+
}
|
|
180
201
|
|
|
202
|
+
// 変換可能なクラスがある場合、属性に変換
|
|
181
203
|
context.report({
|
|
182
|
-
node:
|
|
204
|
+
node: openingElement,
|
|
183
205
|
message: `classNameで指定されたshr-プレフィックスのクラスは、Textコンポーネントの属性に置き換えてください。
|
|
184
206
|
- 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-text-component
|
|
185
207
|
- 推奨: <Text ${propSuggestions}${nonConvertible ? ` className="${nonConvertible}"` : ''}>
|
|
186
208
|
- 変換可能なクラス: ${convertible}
|
|
187
209
|
- shr-プレフィックスのクラスをTextの属性に置き換えることで、型安全性が向上し、意図がより明確になります`,
|
|
188
210
|
fix(fixer) {
|
|
189
|
-
const openingElement = classNameAttrNode.parent
|
|
190
211
|
const sourceCode = context.sourceCode || context.getSourceCode()
|
|
191
212
|
const fixes = []
|
|
192
213
|
|
|
@@ -235,8 +256,39 @@ module.exports = {
|
|
|
235
256
|
[SELECTOR_CONVERTIBLE_SHR_TO_PROPS_WITH_AS]: (classNameAttrNode) => {
|
|
236
257
|
const { nonConvertible, propSuggestions, convertible } = categorizeClassNames(classNameAttrNode)
|
|
237
258
|
const openingElement = classNameAttrNode.parent
|
|
259
|
+
const jsxElement = openingElement.parent
|
|
238
260
|
const asValue = getAttributeLiteralValue(openingElement, 'as')
|
|
239
261
|
|
|
262
|
+
// 変換可能なクラスが0個の場合、as属性で指定されたタグに変換(パターン1-4と同じ動作)
|
|
263
|
+
if (!propSuggestions) {
|
|
264
|
+
const classNameValue = getAttributeLiteralValue(openingElement, 'className')
|
|
265
|
+
const classNameText = `className="${classNameValue}"`
|
|
266
|
+
context.report({
|
|
267
|
+
node: openingElement,
|
|
268
|
+
message: `Textコンポーネントの機能を使用していないため、ネイティブHTML要素に置き換えてください。
|
|
269
|
+
- 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/best-practice-for-text-component
|
|
270
|
+
- <${asValue}>要素にclassNameを移動してください
|
|
271
|
+
- Textコンポーネントの機能(weight、size、color等)を使用しない場合は、直接HTML要素を使用することでシンプルになります`,
|
|
272
|
+
fix(fixer) {
|
|
273
|
+
const sourceCode = context.sourceCode || context.getSourceCode()
|
|
274
|
+
const asAttrNode = getAttributeNode(openingElement, 'as')
|
|
275
|
+
|
|
276
|
+
// 属性とその前のスペースを含めて削除
|
|
277
|
+
const tokenBefore = sourceCode.getTokenBefore(asAttrNode)
|
|
278
|
+
const rangeStart = tokenBefore.range[1]
|
|
279
|
+
const rangeEnd = asAttrNode.range[1]
|
|
280
|
+
|
|
281
|
+
return [
|
|
282
|
+
fixer.removeRange([rangeStart, rangeEnd]),
|
|
283
|
+
fixer.replaceText(openingElement.name, asValue),
|
|
284
|
+
fixer.replaceText(jsxElement.closingElement.name, asValue)
|
|
285
|
+
]
|
|
286
|
+
},
|
|
287
|
+
})
|
|
288
|
+
return
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// 変換可能なクラスがある場合、属性に変換
|
|
240
292
|
context.report({
|
|
241
293
|
node: openingElement,
|
|
242
294
|
message: `classNameで指定されたshr-プレフィックスのクラスは、Textコンポーネントの属性に置き換えてください。
|
|
@@ -105,6 +105,11 @@ ruleTester.run('best-practice-for-text-component', rule, {
|
|
|
105
105
|
{ code: `<Text as="p" className="shr-text-sm custom-class">text</Text>`, output: `<Text as="p" size="S" className="custom-class">text</Text>`, errors: [{ message: errorConvertibleShr('size="S" className="custom-class"', 'shr-text-sm', 'p') }] },
|
|
106
106
|
{ code: `<Text className="shr-text-lg shr-font-bold custom-one custom-two">text</Text>`, output: `<Text size="L" weight="bold" className="custom-one custom-two">text</Text>`, errors: [{ message: errorConvertibleShr('size="L" weight="bold" className="custom-one custom-two"', 'shr-text-lg, shr-font-bold') }] },
|
|
107
107
|
|
|
108
|
+
// パターン2-4: shr-プレフィックスがあるが変換不可能なクラスのみ(spanに変換)
|
|
109
|
+
{ code: `<Text className="shr-w-[10rem]">text</Text>`, output: `<span className="shr-w-[10rem]">text</span>`, errors: [{ message: errorUnnecessaryClassName('shr-w-[10rem]') }] },
|
|
110
|
+
{ code: `<Text className="shr-inline-block shr-mr-0.5">text</Text>`, output: `<span className="shr-inline-block shr-mr-0.5">text</span>`, errors: [{ message: errorUnnecessaryClassName('shr-inline-block shr-mr-0.5') }] },
|
|
111
|
+
{ code: `<Text as="p" className="shr-bg-background shr-block">text</Text>`, output: `<p className="shr-bg-background shr-block">text</p>`, errors: [{ message: errorUnnecessaryAsClassName('p') }] },
|
|
112
|
+
|
|
108
113
|
// パターン3: 属性とclassNameの矛盾
|
|
109
114
|
{ code: `<Text size="M" className="shr-text-sm">text</Text>`, errors: [{ message: errorConflictingProps('shr-text-sm') }] },
|
|
110
115
|
{ code: `<Text weight="bold" className="shr-font-normal">text</Text>`, errors: [{ message: errorConflictingProps('shr-font-normal') }] },
|