eslint-plugin-smarthr 6.10.2 → 6.10.4
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 +15 -0
- package/package.json +2 -2
- package/rules/require-barrel-import/index.js +26 -3
- package/rules/require-i18n-text/index.js +9 -4
- package/rules/trim-props/index.js +16 -8
- package/test/require-barrel-import.js +61 -73
- package/test/require-i18n-text.js +29 -33
- package/test/trim-props.js +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
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.10.4](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v6.10.3...eslint-plugin-smarthr-v6.10.4) (2026-04-13)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **require-i18n-text:** 許容する記号の拡充と、テストコードでの除外設定を追加 ([#1225](https://github.com/kufu/tamatebako/issues/1225)) ([705113f](https://github.com/kufu/tamatebako/commit/705113f9b599bb77bec35b2c9b28825603d581ea))
|
|
11
|
+
|
|
12
|
+
## [6.10.3](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v6.10.2...eslint-plugin-smarthr-v6.10.3) (2026-04-10)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* **require-barrel-import:** commonParentのbarrelを除外して同一ツリー内の相対importを許可 ([#1222](https://github.com/kufu/tamatebako/issues/1222)) ([fe5a68c](https://github.com/kufu/tamatebako/commit/fe5a68cd50a6f76dde0c403dd1ec33e5b1dd5d57))
|
|
18
|
+
* trim-propsのセレクターをesquery互換へ修正し、ESLint 9.xでのクラッシュを解消 ([#1214](https://github.com/kufu/tamatebako/issues/1214)) ([d2345a0](https://github.com/kufu/tamatebako/commit/d2345a0623ebbf0f805c7c8a6b2b957ac21b0ffd))
|
|
19
|
+
|
|
5
20
|
## [6.10.2](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v6.10.1...eslint-plugin-smarthr-v6.10.2) (2026-04-09)
|
|
6
21
|
|
|
7
22
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-smarthr",
|
|
3
|
-
"version": "6.10.
|
|
3
|
+
"version": "6.10.4",
|
|
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": "5e89902850d7084a12d50400dad9d1b0e5764e00"
|
|
41
41
|
}
|
|
@@ -104,6 +104,24 @@ const isImportedInsideImporter = (importerDir, importedPath) => {
|
|
|
104
104
|
return importedPath === importerDir || importedPath.startsWith(importerDir + '/')
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
/**
|
|
108
|
+
* 2つのパスの共通の親ディレクトリを見つける
|
|
109
|
+
* @param {string} path1 - パス1
|
|
110
|
+
* @param {string} path2 - パス2
|
|
111
|
+
* @returns {string} 共通の親ディレクトリの絶対パス
|
|
112
|
+
*/
|
|
113
|
+
const findCommonParent = (path1, path2) => {
|
|
114
|
+
const segments1 = path1.split('/')
|
|
115
|
+
const segments2 = path2.split('/')
|
|
116
|
+
|
|
117
|
+
let i = 0
|
|
118
|
+
while (i < segments1.length && i < segments2.length && segments1[i] === segments2[i]) {
|
|
119
|
+
i++
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return segments1.slice(0, i).join('/')
|
|
123
|
+
}
|
|
124
|
+
|
|
107
125
|
/**
|
|
108
126
|
* allowedImportsオプションに基づいて、特定のimportが許可されているかチェックする
|
|
109
127
|
* @param {object} node - ImportDeclaration node
|
|
@@ -167,6 +185,10 @@ const findBarrelFile = (importedPath, importerDir) => {
|
|
|
167
185
|
let currentPath = importedPath
|
|
168
186
|
let barrel = undefined
|
|
169
187
|
|
|
188
|
+
// import元とimport先の共通の親ディレクトリを見つける
|
|
189
|
+
// 共通の親のbarrelファイルは除外する(同じディレクトリツリー内の相対importには適用されない)
|
|
190
|
+
const commonParent = findCommonParent(importerDir, importedPath)
|
|
191
|
+
|
|
170
192
|
// ディレクトリ指定の場合、そのindex.tsを指していることは自明なので一階層上から探索
|
|
171
193
|
if (fs.existsSync(currentPath) && fs.statSync(currentPath).isDirectory()) {
|
|
172
194
|
pathSegments.pop()
|
|
@@ -175,9 +197,10 @@ const findBarrelFile = (importedPath, importerDir) => {
|
|
|
175
197
|
|
|
176
198
|
while (pathSegments.length > 0) {
|
|
177
199
|
// 以下の場合は探索終了
|
|
178
|
-
// 1.
|
|
179
|
-
// 2.
|
|
180
|
-
|
|
200
|
+
// 1. 共通の親ディレクトリに到達した場合(commonParent自体のbarrelは除外)
|
|
201
|
+
// 2. いずれかのreplacePathsのルートに到達した場合
|
|
202
|
+
// 3. import先がimport元の内部にある場合(同階層・サブディレクトリからのimport)
|
|
203
|
+
if (currentPath === commonParent || ALL_ROOT_PATHS.includes(currentPath) || isImportedInsideImporter(importerDir, currentPath)) {
|
|
181
204
|
break
|
|
182
205
|
}
|
|
183
206
|
|
|
@@ -28,8 +28,9 @@ const generateAttributeSelector = (attributes) =>
|
|
|
28
28
|
const generateTemplateLiteralSelector = (attributes) =>
|
|
29
29
|
`JSXAttribute[name.name=/^(${attributes.join('|')})$/][value.type="JSXExpressionContainer"][value.expression.type="TemplateLiteral"]`
|
|
30
30
|
|
|
31
|
-
const
|
|
32
|
-
const
|
|
31
|
+
const REGEX_IGNORE_FILENAME = /\.(spec|test|stories)\./
|
|
32
|
+
const REGEX_IGNORE_TEXT = /^\s*(\.|\+|\-|〜|:|:|(|)|\(|\)|,|\*|\/|[0-9]+)\s*$/
|
|
33
|
+
const checkIgnoreText = (text) => !REGEX_IGNORE_TEXT.test(text)
|
|
33
34
|
|
|
34
35
|
const someReportTemplateLiteralError = (quasi) => quasi.value.cooked && quasi.value.cooked.trim() !== '' && checkIgnoreText(quasi.value.cooked)
|
|
35
36
|
|
|
@@ -42,6 +43,10 @@ module.exports = {
|
|
|
42
43
|
schema: SCHEMA,
|
|
43
44
|
},
|
|
44
45
|
create(context) {
|
|
46
|
+
if (REGEX_IGNORE_FILENAME.test(context.getFilename())) {
|
|
47
|
+
return {}
|
|
48
|
+
}
|
|
49
|
+
|
|
45
50
|
const elementsObj = (context.options[0] || {}).elements || {}
|
|
46
51
|
// ユーザーが'*'を設定していない場合のみデフォルトを適用
|
|
47
52
|
const wildcardAttributes = elementsObj['*'] || DEFAULT_WILDCARD_ATTRIBUTES
|
|
@@ -57,7 +62,7 @@ module.exports = {
|
|
|
57
62
|
if (checkIgnoreText(node.value.value)) {
|
|
58
63
|
context.report({
|
|
59
64
|
node,
|
|
60
|
-
message: `${node.parent.name.name}の${node.name.name}
|
|
65
|
+
message: `${node.parent.name.name}の${node.name.name}属性に文字列リテラル "${node.value.value.trim()}" が指定されています。多言語化対応のため、翻訳関数を使用してください
|
|
61
66
|
- 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/require-i18n-text`,
|
|
62
67
|
})
|
|
63
68
|
}
|
|
@@ -101,7 +106,7 @@ module.exports = {
|
|
|
101
106
|
if (checkIgnoreText(node.value)) {
|
|
102
107
|
context.report({
|
|
103
108
|
node,
|
|
104
|
-
message:
|
|
109
|
+
message: `子要素に文字列リテラル "${node.value.trim()}" が指定されています。多言語化対応のため、翻訳関数を使用してください
|
|
105
110
|
- 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/require-i18n-text`,
|
|
106
111
|
})
|
|
107
112
|
}
|
|
@@ -12,15 +12,23 @@ module.exports = {
|
|
|
12
12
|
fixable: 'whitespace',
|
|
13
13
|
},
|
|
14
14
|
create(context) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
message: `属性に設定している文字列から先頭、末尾の空白文字を削除してください
|
|
15
|
+
const checker = (node) => {
|
|
16
|
+
context.report({
|
|
17
|
+
node,
|
|
18
|
+
message: `属性に設定している文字列から先頭、末尾の空白文字を削除してください
|
|
20
19
|
- 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/trim-props`,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
fix: (fixer) => fixer.replaceText(node, context.sourceCode.getText(node).replace(TRIM_REGEX, '$1$2$3')),
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
// esquery は :matches()・:has() 内での > (子結合子) を解釈できないため、
|
|
26
|
+
// 1つの複合セレクターではなく個別のセレクターに分割している
|
|
27
|
+
'JSXAttribute > Literal[value=/(^ | $)/]': checker,
|
|
28
|
+
'JSXAttribute > JSXExpressionContainer > TemplateLiteral > TemplateElement:matches(:first-child[value.raw=/^ /],:last-child[value.raw=/ $/])':
|
|
29
|
+
(node) => {
|
|
30
|
+
checker(node.parent)
|
|
31
|
+
},
|
|
24
32
|
}
|
|
25
33
|
},
|
|
26
34
|
}
|
|
@@ -261,16 +261,14 @@ ruleTester.run('require-barrel-import', rule, {
|
|
|
261
261
|
return `${fixturesRoot}/path-alias-parent-no-barrel/Button/Button.tsx`
|
|
262
262
|
})(),
|
|
263
263
|
},
|
|
264
|
-
],
|
|
265
264
|
|
|
266
|
-
|
|
267
|
-
// 親階層からのimport(barrelが存在する場合)
|
|
265
|
+
// 親階層からのimport(commonParentにbarrelがある場合)
|
|
268
266
|
{
|
|
269
267
|
code: `import { createUserRole } from '../hooks/createUserRoleAction'`,
|
|
270
268
|
filename: (() => {
|
|
271
|
-
createFixture('parent-import-
|
|
269
|
+
createFixture('parent-import-common-parent-barrel', {
|
|
272
270
|
'components': {
|
|
273
|
-
'index.tsx': 'export {}',
|
|
271
|
+
'index.tsx': 'export {}', // commonParentのbarrelは除外される
|
|
274
272
|
'AddDialog': {
|
|
275
273
|
'AddDialog.tsx': '',
|
|
276
274
|
},
|
|
@@ -279,91 +277,57 @@ ruleTester.run('require-barrel-import', rule, {
|
|
|
279
277
|
},
|
|
280
278
|
},
|
|
281
279
|
})
|
|
282
|
-
return `${fixturesRoot}/parent-import-
|
|
280
|
+
return `${fixturesRoot}/parent-import-common-parent-barrel/components/AddDialog/AddDialog.tsx`
|
|
283
281
|
})(),
|
|
284
|
-
errors: [
|
|
285
|
-
{
|
|
286
|
-
message: /からimportするか、.*のbarrelファイルを削除して直接import可能にしてください/,
|
|
287
|
-
},
|
|
288
|
-
],
|
|
289
282
|
},
|
|
290
283
|
|
|
291
|
-
//
|
|
284
|
+
// 複雑なネスト - commonParentのbarrelは除外される
|
|
292
285
|
{
|
|
293
|
-
code: `import {
|
|
286
|
+
code: `import type { RequestStepActionNotSkipped } from '../utils/withSkipped'`,
|
|
294
287
|
filename: (() => {
|
|
295
|
-
createFixture('
|
|
296
|
-
'
|
|
297
|
-
'
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
},
|
|
288
|
+
createFixture('nested-common-parent-barrel', {
|
|
289
|
+
'Nodes': {
|
|
290
|
+
'index.tsx': 'export {}', // commonParentより上のbarrel(除外される)
|
|
291
|
+
'StepNodeRequestView': {
|
|
292
|
+
'index.tsx': 'export {}', // commonParent(除外される)
|
|
293
|
+
'Approvers': {
|
|
294
|
+
'ApproverRow': {
|
|
295
|
+
'buildUserRowsProps.ts': '',
|
|
296
|
+
},
|
|
297
|
+
'utils': {
|
|
298
|
+
'withSkipped': {
|
|
299
|
+
'index.ts': 'export {}', // import先のbarrel(valid)
|
|
308
300
|
},
|
|
309
301
|
},
|
|
310
302
|
},
|
|
311
303
|
},
|
|
312
304
|
},
|
|
313
305
|
})
|
|
314
|
-
return `${fixturesRoot}/
|
|
306
|
+
return `${fixturesRoot}/nested-common-parent-barrel/Nodes/StepNodeRequestView/Approvers/ApproverRow/buildUserRowsProps.ts`
|
|
315
307
|
})(),
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
},
|
|
320
|
-
],
|
|
308
|
+
languageOptions: {
|
|
309
|
+
parser: require('typescript-eslint').parser,
|
|
310
|
+
},
|
|
321
311
|
},
|
|
312
|
+
],
|
|
322
313
|
|
|
323
|
-
|
|
314
|
+
invalid: [
|
|
315
|
+
// 親階層からのimport(import先の親にbarrelがある場合)
|
|
324
316
|
{
|
|
325
317
|
code: `import { api } from '../api/client'`,
|
|
326
318
|
filename: (() => {
|
|
327
|
-
createFixture('
|
|
328
|
-
'app': {
|
|
329
|
-
'items': {
|
|
330
|
-
'index.tsx': 'export {}',
|
|
331
|
-
'[id]': {
|
|
332
|
-
'DetailPage.tsx': '',
|
|
333
|
-
},
|
|
334
|
-
'api': {
|
|
335
|
-
'client.ts': '',
|
|
336
|
-
},
|
|
337
|
-
},
|
|
338
|
-
},
|
|
339
|
-
})
|
|
340
|
-
return `${fixturesRoot}/dynamic-route-parent-import/app/items/[id]/DetailPage.tsx`
|
|
341
|
-
})(),
|
|
342
|
-
errors: [
|
|
343
|
-
{
|
|
344
|
-
message: /からimportするか、.*のbarrelファイルを削除して直接import可能にしてください/,
|
|
345
|
-
},
|
|
346
|
-
],
|
|
347
|
-
},
|
|
348
|
-
|
|
349
|
-
// ============================================================
|
|
350
|
-
// Path alias - 親階層からのimport(barrelあり)
|
|
351
|
-
// ============================================================
|
|
352
|
-
{
|
|
353
|
-
code: `import { createUserRole } from '~/path-alias-parent-import-with-barrel/components/hooks/createUserRoleAction'`,
|
|
354
|
-
filename: (() => {
|
|
355
|
-
createFixture('path-alias-parent-import-with-barrel', {
|
|
319
|
+
createFixture('parent-import-with-barrel', {
|
|
356
320
|
'components': {
|
|
357
|
-
'index.tsx': 'export {}',
|
|
358
321
|
'AddDialog': {
|
|
359
322
|
'AddDialog.tsx': '',
|
|
360
323
|
},
|
|
361
|
-
'
|
|
362
|
-
'
|
|
324
|
+
'api': {
|
|
325
|
+
'index.tsx': 'export {}', // import先の親にbarrel
|
|
326
|
+
'client.ts': '',
|
|
363
327
|
},
|
|
364
328
|
},
|
|
365
329
|
})
|
|
366
|
-
return `${fixturesRoot}/
|
|
330
|
+
return `${fixturesRoot}/parent-import-with-barrel/components/AddDialog/AddDialog.tsx`
|
|
367
331
|
})(),
|
|
368
332
|
errors: [
|
|
369
333
|
{
|
|
@@ -372,22 +336,22 @@ ruleTester.run('require-barrel-import', rule, {
|
|
|
372
336
|
],
|
|
373
337
|
},
|
|
374
338
|
|
|
375
|
-
//
|
|
339
|
+
// Next.js特殊文字パス - import先の親にbarrel
|
|
376
340
|
{
|
|
377
|
-
code: `import {
|
|
341
|
+
code: `import { api } from '../api/client'`,
|
|
378
342
|
filename: (() => {
|
|
379
|
-
createFixture('
|
|
343
|
+
createFixture('nextjs-import-parent-barrel', {
|
|
380
344
|
'app': {
|
|
381
345
|
'(private)': {
|
|
382
346
|
'settings': {
|
|
383
347
|
'user_roles': {
|
|
384
348
|
'_components': {
|
|
385
|
-
'index.tsx': 'export {}',
|
|
386
349
|
'AddUserRoleDialog': {
|
|
387
350
|
'AddUserRoleDialog.tsx': '',
|
|
388
351
|
},
|
|
389
|
-
'
|
|
390
|
-
'
|
|
352
|
+
'api': {
|
|
353
|
+
'index.tsx': 'export {}', // import先の親にbarrel
|
|
354
|
+
'client.ts': '',
|
|
391
355
|
},
|
|
392
356
|
},
|
|
393
357
|
},
|
|
@@ -395,7 +359,31 @@ ruleTester.run('require-barrel-import', rule, {
|
|
|
395
359
|
},
|
|
396
360
|
},
|
|
397
361
|
})
|
|
398
|
-
return `${fixturesRoot}/
|
|
362
|
+
return `${fixturesRoot}/nextjs-import-parent-barrel/app/(private)/settings/user_roles/_components/AddUserRoleDialog/AddUserRoleDialog.tsx`
|
|
363
|
+
})(),
|
|
364
|
+
errors: [
|
|
365
|
+
{
|
|
366
|
+
message: /からimportするか、.*のbarrelファイルを削除して直接import可能にしてください/,
|
|
367
|
+
},
|
|
368
|
+
],
|
|
369
|
+
},
|
|
370
|
+
|
|
371
|
+
// Path alias - import先の親にbarrel
|
|
372
|
+
{
|
|
373
|
+
code: `import { api } from '~/path-alias-import-parent-barrel/components/api/client'`,
|
|
374
|
+
filename: (() => {
|
|
375
|
+
createFixture('path-alias-import-parent-barrel', {
|
|
376
|
+
'components': {
|
|
377
|
+
'AddDialog': {
|
|
378
|
+
'AddDialog.tsx': '',
|
|
379
|
+
},
|
|
380
|
+
'api': {
|
|
381
|
+
'index.tsx': 'export {}', // import先の親にbarrel
|
|
382
|
+
'client.ts': '',
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
})
|
|
386
|
+
return `${fixturesRoot}/path-alias-import-parent-barrel/components/AddDialog/AddDialog.tsx`
|
|
399
387
|
})(),
|
|
400
388
|
errors: [
|
|
401
389
|
{
|
|
@@ -11,9 +11,9 @@ const ruleTester = new RuleTester({
|
|
|
11
11
|
},
|
|
12
12
|
})
|
|
13
13
|
|
|
14
|
-
const attributeError = (element, attr) => `${element}の${attr}
|
|
14
|
+
const attributeError = (element, attr, text) => `${element}の${attr}属性に文字列リテラル "${text}" が指定されています。多言語化対応のため、翻訳関数を使用してください
|
|
15
15
|
- 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/require-i18n-text`
|
|
16
|
-
const childTextError =
|
|
16
|
+
const childTextError = (text) => `子要素に文字列リテラル "${text}" が指定されています。多言語化対応のため、翻訳関数を使用してください
|
|
17
17
|
- 詳細: https://github.com/kufu/tamatebako/tree/master/packages/eslint-plugin-smarthr/rules/require-i18n-text`
|
|
18
18
|
|
|
19
19
|
const options = [
|
|
@@ -61,6 +61,14 @@ ruleTester.run('require-i18n-text', rule, {
|
|
|
61
61
|
{ code: `<img alt="-" />` },
|
|
62
62
|
{ code: `<i>*</i>` },
|
|
63
63
|
{ code: `<i>/</i>` },
|
|
64
|
+
{ code: `<div>〜</div>` },
|
|
65
|
+
{ code: `<div>:</div>` },
|
|
66
|
+
{ code: `<div>:</div>` },
|
|
67
|
+
{ code: `<div>(</div>` },
|
|
68
|
+
{ code: `<div>)</div>` },
|
|
69
|
+
{ code: `<div>(</div>` },
|
|
70
|
+
{ code: `<div>)</div>` },
|
|
71
|
+
{ code: `<div>,</div>` },
|
|
64
72
|
|
|
65
73
|
// ワイルドカード - 空配列で除外
|
|
66
74
|
{
|
|
@@ -96,41 +104,41 @@ ruleTester.run('require-i18n-text', rule, {
|
|
|
96
104
|
// 属性エラー: デフォルト設定
|
|
97
105
|
{
|
|
98
106
|
code: `<img alt="Profile picture" />`,
|
|
99
|
-
errors: [{ message: attributeError('img', 'alt') }],
|
|
107
|
+
errors: [{ message: attributeError('img', 'alt', 'Profile picture') }],
|
|
100
108
|
},
|
|
101
109
|
{
|
|
102
110
|
code: `<CustomComponent aria-label="Label" />`,
|
|
103
|
-
errors: [{ message: attributeError('CustomComponent', 'aria-label') }],
|
|
111
|
+
errors: [{ message: attributeError('CustomComponent', 'aria-label', 'Label') }],
|
|
104
112
|
},
|
|
105
113
|
{
|
|
106
114
|
code: `<DefinitionListItem term="Label" />`,
|
|
107
|
-
errors: [{ message: attributeError('DefinitionListItem', 'term') }],
|
|
115
|
+
errors: [{ message: attributeError('DefinitionListItem', 'term', 'Label') }],
|
|
108
116
|
},
|
|
109
117
|
{
|
|
110
118
|
code: `<button title="Click me" />`,
|
|
111
|
-
errors: [{ message: attributeError('button', 'title') }],
|
|
119
|
+
errors: [{ message: attributeError('button', 'title', 'Click me') }],
|
|
112
120
|
},
|
|
113
121
|
|
|
114
122
|
// 数値、.と演算記号の場合でも他の文字列が含まれていればエラー
|
|
115
|
-
{ code: `<Any aria-label="1234 あ" />`, errors: [{ message: attributeError('Any', 'aria-label') }] },
|
|
116
|
-
{ code: `<div>a.</div>`, errors: [{ message: childTextError }] },
|
|
117
|
-
{ code: `<a> + b</a>`, errors: [{ message: childTextError }] },
|
|
118
|
-
{ code: `<img alt="-zod" />`, errors: [{ message: attributeError('img', 'alt') }] },
|
|
119
|
-
{ code: `<i>*1</i>`, errors: [{ message: childTextError }] },
|
|
120
|
-
{ code: `<i>a/</i>`, errors: [{ message: childTextError }] },
|
|
123
|
+
{ code: `<Any aria-label="1234 あ" />`, errors: [{ message: attributeError('Any', 'aria-label', '1234 あ') }] },
|
|
124
|
+
{ code: `<div>a.</div>`, errors: [{ message: childTextError('a.') }] },
|
|
125
|
+
{ code: `<a> + b</a>`, errors: [{ message: childTextError('+ b') }] },
|
|
126
|
+
{ code: `<img alt="-zod" />`, errors: [{ message: attributeError('img', 'alt', '-zod') }] },
|
|
127
|
+
{ code: `<i>*1</i>`, errors: [{ message: childTextError('*1') }] },
|
|
128
|
+
{ code: `<i>a/</i>`, errors: [{ message: childTextError('a/') }] },
|
|
121
129
|
|
|
122
130
|
// 属性エラー: カスタムオプション
|
|
123
131
|
{
|
|
124
132
|
code: `<img alt="Profile picture" />`,
|
|
125
133
|
options,
|
|
126
|
-
errors: [{ message: attributeError('img', 'alt') }],
|
|
134
|
+
errors: [{ message: attributeError('img', 'alt', 'Profile picture') }],
|
|
127
135
|
},
|
|
128
136
|
|
|
129
137
|
// 属性エラー: 同一要素の複数属性
|
|
130
138
|
{
|
|
131
139
|
code: `<img alt="Profile" title="User profile" />`,
|
|
132
140
|
options,
|
|
133
|
-
errors: [{ message: attributeError('img', 'alt') }, { message: attributeError('img', 'title') }],
|
|
141
|
+
errors: [{ message: attributeError('img', 'alt', 'Profile') }, { message: attributeError('img', 'title', 'User profile') }],
|
|
134
142
|
},
|
|
135
143
|
|
|
136
144
|
// 属性エラー: ワイルドカード
|
|
@@ -143,7 +151,7 @@ ruleTester.run('require-i18n-text', rule, {
|
|
|
143
151
|
},
|
|
144
152
|
},
|
|
145
153
|
],
|
|
146
|
-
errors: [{ message: attributeError('CustomComponent', 'label') }],
|
|
154
|
+
errors: [{ message: attributeError('CustomComponent', 'label', 'Text') }],
|
|
147
155
|
},
|
|
148
156
|
|
|
149
157
|
// 属性エラー: 個別設定がワイルドカードより優先
|
|
@@ -157,20 +165,20 @@ ruleTester.run('require-i18n-text', rule, {
|
|
|
157
165
|
},
|
|
158
166
|
},
|
|
159
167
|
],
|
|
160
|
-
errors: [{ message: attributeError('Button', 'label') }, { message: attributeError('Button', 'helperText') }],
|
|
168
|
+
errors: [{ message: attributeError('Button', 'label', 'Submit') }, { message: attributeError('Button', 'helperText', 'Help') }],
|
|
161
169
|
},
|
|
162
170
|
|
|
163
171
|
// 子要素エラー(オプション未設定時でもチェックされる)
|
|
164
172
|
{
|
|
165
173
|
code: `<div>Hello World</div>`,
|
|
166
|
-
errors: [{ message: childTextError }],
|
|
174
|
+
errors: [{ message: childTextError('Hello World') }],
|
|
167
175
|
},
|
|
168
176
|
|
|
169
177
|
// 複合エラー: 属性と子要素
|
|
170
178
|
{
|
|
171
179
|
code: `<Button label="Submit">Click here</Button>`,
|
|
172
180
|
options,
|
|
173
|
-
errors: [{ message: attributeError('Button', 'label') }, { message: childTextError }],
|
|
181
|
+
errors: [{ message: attributeError('Button', 'label', 'Submit') }, { message: childTextError('Click here') }],
|
|
174
182
|
},
|
|
175
183
|
|
|
176
184
|
// 複合エラー: 入れ子構造
|
|
@@ -178,22 +186,10 @@ ruleTester.run('require-i18n-text', rule, {
|
|
|
178
186
|
code: `<div title="Parent"><Button label="Child">Grandchild text</Button></div>`,
|
|
179
187
|
options,
|
|
180
188
|
errors: [
|
|
181
|
-
{ message: attributeError('div', 'title') },
|
|
182
|
-
{ message: attributeError('Button', 'label') },
|
|
183
|
-
{ message: childTextError },
|
|
189
|
+
{ message: attributeError('div', 'title', 'Parent') },
|
|
190
|
+
{ message: attributeError('Button', 'label', 'Child') },
|
|
191
|
+
{ message: childTextError('Grandchild text') },
|
|
184
192
|
],
|
|
185
193
|
},
|
|
186
|
-
|
|
187
|
-
// TemplateLiteral - 変数を含み、文字列リテラル部分がある
|
|
188
|
-
{
|
|
189
|
-
code: `<img alt={\`Profile \${name}\`} />`,
|
|
190
|
-
options,
|
|
191
|
-
errors: [{ message: attributeError('img', 'alt') }],
|
|
192
|
-
},
|
|
193
|
-
{
|
|
194
|
-
code: `<div title={\`\${prefix} title\`} />`,
|
|
195
|
-
options,
|
|
196
|
-
errors: [{ message: attributeError('div', 'title') }],
|
|
197
|
-
},
|
|
198
194
|
],
|
|
199
195
|
})
|
package/test/trim-props.js
CHANGED
|
@@ -55,12 +55,12 @@ ruleTester.run('trim-props', rule, {
|
|
|
55
55
|
{
|
|
56
56
|
code: '<div data-spec={` a${b} c `}>....</div>',
|
|
57
57
|
output: '<div data-spec={`a${b} c`}>....</div>',
|
|
58
|
-
errors: [{ message: ERROR_MESSAGE }],
|
|
58
|
+
errors: [{ message: ERROR_MESSAGE }, { message: ERROR_MESSAGE }],
|
|
59
59
|
},
|
|
60
60
|
{
|
|
61
61
|
code: '<div data-spec={` a${b ? ` ${c} ` : " "} d `}>....</div>',
|
|
62
62
|
output: '<div data-spec={`a${b ? ` ${c} ` : " "} d`}>....</div>',
|
|
63
|
-
errors: [{ message: ERROR_MESSAGE }],
|
|
63
|
+
errors: [{ message: ERROR_MESSAGE }, { message: ERROR_MESSAGE }],
|
|
64
64
|
},
|
|
65
65
|
],
|
|
66
66
|
})
|