eslint-plugin-smarthr 6.10.1 → 6.10.3

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 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.3](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v6.10.2...eslint-plugin-smarthr-v6.10.3) (2026-04-10)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * **require-barrel-import:** commonParentのbarrelを除外して同一ツリー内の相対importを許可 ([#1222](https://github.com/kufu/tamatebako/issues/1222)) ([fe5a68c](https://github.com/kufu/tamatebako/commit/fe5a68cd50a6f76dde0c403dd1ec33e5b1dd5d57))
11
+ * trim-propsのセレクターをesquery互換へ修正し、ESLint 9.xでのクラッシュを解消 ([#1214](https://github.com/kufu/tamatebako/issues/1214)) ([d2345a0](https://github.com/kufu/tamatebako/commit/d2345a0623ebbf0f805c7c8a6b2b957ac21b0ffd))
12
+
13
+ ## [6.10.2](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v6.10.1...eslint-plugin-smarthr-v6.10.2) (2026-04-09)
14
+
15
+
16
+ ### Bug Fixes
17
+
18
+ * **test:** typescript-eslint v8のparserに更新 ([#1217](https://github.com/kufu/tamatebako/issues/1217)) ([e44ac27](https://github.com/kufu/tamatebako/commit/e44ac27e26afc9cbe26244afd38e62bb65aecaa4))
19
+
5
20
  ## [6.10.1](https://github.com/kufu/tamatebako/compare/eslint-plugin-smarthr-v6.10.0...eslint-plugin-smarthr-v6.10.1) (2026-04-08)
6
21
 
7
22
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-smarthr",
3
- "version": "6.10.1",
3
+ "version": "6.10.3",
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": "e2e55eea3e72809fab55082ad6ef4da0c6025ae8"
40
+ "gitHead": "e1a8159fdc6396bb83fdf7f616f52f3c714ff390"
41
41
  }
@@ -37,13 +37,25 @@ const REGEX_ROOT_PATH = new RegExp(`^${rootPath}/index\.`)
37
37
  const REGEX_INDEX_FILE = /\/index\.(ts|js)x?$/
38
38
  const TARGET_EXTS = ['ts', 'tsx', 'js', 'jsx']
39
39
 
40
- // Path aliasの正規表現を事前生成してキャッシュ
41
- const entriedReplacePathsWithRegex = Object.entries(replacePaths).map(([key, values]) => [
42
- key,
43
- values,
44
- new RegExp(`^${key}(.+)$`),
45
- values.map(v => new RegExp(`^${path.resolve(`${CWD}/${v}`)}(.+)$`))
46
- ])
40
+ // Path aliasの情報を事前計算してキャッシュ
41
+ const REPLACE_PATHS_INFO = Object.entries(replacePaths).map(([key, values]) => {
42
+ const resolvedPaths = values.map(v => path.resolve(`${CWD}/${v.replace(/\/\*$/, '')}`))
43
+ return {
44
+ key,
45
+ values,
46
+ keyRegex: new RegExp(`^${key}(.+)$`),
47
+ resolvedPaths,
48
+ valueRegexes: resolvedPaths.map(p => new RegExp(`^${p}(.+)$`))
49
+ }
50
+ })
51
+
52
+ // @/ と ~/ のパスのみをrootとする(READMEの仕様通り)
53
+ const ALL_ROOT_PATHS = (() => {
54
+ const rootKeys = ['@/', '~/']
55
+ return REPLACE_PATHS_INFO
56
+ .filter(info => rootKeys.includes(info.key))
57
+ .flatMap(info => info.resolvedPaths)
58
+ })()
47
59
 
48
60
  /**
49
61
  * Path aliasを絶対パスに変換する
@@ -55,17 +67,13 @@ const resolvePathAlias = (importPath) => {
55
67
  return importPath
56
68
  }
57
69
 
58
- return entriedReplacePathsWithRegex.reduce((result, [key, values, keyRegex]) => {
59
- if (result === importPath) {
60
- return values.reduce((resolved, value) => {
61
- if (resolved === result && keyRegex.test(result)) {
62
- return resolved.replace(keyRegex, `${path.resolve(`${CWD}/${value}`)}/$1`)
63
- }
64
- return resolved
65
- }, result)
70
+ for (const { keyRegex, resolvedPaths } of REPLACE_PATHS_INFO) {
71
+ if (keyRegex.test(importPath)) {
72
+ return importPath.replace(keyRegex, `${resolvedPaths[0]}/$1`)
66
73
  }
67
- return result
68
- }, importPath)
74
+ }
75
+
76
+ return importPath
69
77
  }
70
78
 
71
79
  /**
@@ -74,20 +82,15 @@ const resolvePathAlias = (importPath) => {
74
82
  * @returns {string} Path alias(例: '@/components/Button')
75
83
  */
76
84
  const convertToPathAlias = (absolutePath) => {
77
- return entriedReplacePathsWithRegex.reduce((result, [key, values, keyRegex, valueRegexes]) => {
78
- if (result === absolutePath) {
79
- return values.reduce((converted, value, index) => {
80
- if (converted === result) {
81
- const regexp = valueRegexes[index]
82
- if (regexp.test(converted)) {
83
- return converted.replace(regexp, `${key}/$1`).replace(REGEX_UNNECESSARY_SLASH, '/')
84
- }
85
- }
86
- return converted
87
- }, result)
85
+ for (const { key, valueRegexes } of REPLACE_PATHS_INFO) {
86
+ for (const regexp of valueRegexes) {
87
+ if (regexp.test(absolutePath)) {
88
+ return absolutePath.replace(regexp, `${key}/$1`).replace(REGEX_UNNECESSARY_SLASH, '/')
89
+ }
88
90
  }
89
- return result
90
- }, absolutePath)
91
+ }
92
+
93
+ return absolutePath
91
94
  }
92
95
 
93
96
  /**
@@ -101,6 +104,24 @@ const isImportedInsideImporter = (importerDir, importedPath) => {
101
104
  return importedPath === importerDir || importedPath.startsWith(importerDir + '/')
102
105
  }
103
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
+
104
125
  /**
105
126
  * allowedImportsオプションに基づいて、特定のimportが許可されているかチェックする
106
127
  * @param {object} node - ImportDeclaration node
@@ -164,6 +185,10 @@ const findBarrelFile = (importedPath, importerDir) => {
164
185
  let currentPath = importedPath
165
186
  let barrel = undefined
166
187
 
188
+ // import元とimport先の共通の親ディレクトリを見つける
189
+ // 共通の親のbarrelファイルは除外する(同じディレクトリツリー内の相対importには適用されない)
190
+ const commonParent = findCommonParent(importerDir, importedPath)
191
+
167
192
  // ディレクトリ指定の場合、そのindex.tsを指していることは自明なので一階層上から探索
168
193
  if (fs.existsSync(currentPath) && fs.statSync(currentPath).isDirectory()) {
169
194
  pathSegments.pop()
@@ -172,9 +197,10 @@ const findBarrelFile = (importedPath, importerDir) => {
172
197
 
173
198
  while (pathSegments.length > 0) {
174
199
  // 以下の場合は探索終了
175
- // 1. root pathに到達した場合
176
- // 2. import先がimport元の内部にある場合(同階層・サブディレクトリからのimport)
177
- if (importerDir === rootPath || isImportedInsideImporter(importerDir, currentPath)) {
200
+ // 1. 共通の親ディレクトリに到達した場合(commonParent自体のbarrelは除外)
201
+ // 2. いずれかのreplacePathsのルートに到達した場合
202
+ // 3. import先がimport元の内部にある場合(同階層・サブディレクトリからのimport)
203
+ if (currentPath === commonParent || ALL_ROOT_PATHS.includes(currentPath) || isImportedInsideImporter(importerDir, currentPath)) {
178
204
  break
179
205
  }
180
206
 
@@ -12,15 +12,23 @@ module.exports = {
12
12
  fixable: 'whitespace',
13
13
  },
14
14
  create(context) {
15
- return {
16
- ':matches(JSXAttribute > Literal[value=/(^ | $)/], JSXAttribute > JSXExpressionContainer > TemplateLiteral:has(> TemplateElement:matches(:first-child[value.raw=/^ /],:last-child[value.raw=/ $/])))': (node) => {
17
- context.report({
18
- node,
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
- fix: (fixer) => fixer.replaceText(node, context.sourceCode.getText(node).replace(TRIM_REGEX, '$1$2$3')),
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
  }
@@ -1,9 +1,10 @@
1
1
  const rule = require('../rules/prohibit-export-array-type')
2
2
  const RuleTester = require('eslint').RuleTester
3
+ const tseslint = require('typescript-eslint')
3
4
 
4
5
  const ruleTester = new RuleTester({
5
6
  languageOptions: {
6
- parser: require('@typescript-eslint/parser'),
7
+ parser: tseslint.parser,
7
8
  parserOptions: {
8
9
  ecmaFeatures: {
9
10
  jsx: true,
@@ -0,0 +1,395 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+ const rule = require('../rules/require-barrel-import')
4
+ const RuleTester = require('eslint').RuleTester
5
+
6
+ const ruleTester = new RuleTester({
7
+ languageOptions: {
8
+ parserOptions: {
9
+ ecmaFeatures: {
10
+ jsx: true,
11
+ },
12
+ },
13
+ },
14
+ })
15
+
16
+ // テストフィクスチャのルートディレクトリ
17
+ const fixturesRoot = path.join(__dirname, '..', 'test-fixtures')
18
+
19
+ /**
20
+ * テスト用のファイル構造を作成するヘルパー
21
+ * @param {string} testName - テスト名(ディレクトリ名として使用)
22
+ * @param {Object} structure - ファイル構造定義
23
+ * @returns {string} 作成したディレクトリのパス
24
+ */
25
+ function createFixture(testName, structure) {
26
+ const fixtureDir = path.join(fixturesRoot, testName)
27
+
28
+ // ディレクトリが既に存在する場合は削除
29
+ if (fs.existsSync(fixtureDir)) {
30
+ fs.rmSync(fixtureDir, { recursive: true, force: true })
31
+ }
32
+
33
+ // ディレクトリとファイルを再帰的に作成
34
+ function createStructure(dir, struct) {
35
+ fs.mkdirSync(dir, { recursive: true })
36
+
37
+ for (const [name, content] of Object.entries(struct)) {
38
+ const fullPath = path.join(dir, name)
39
+
40
+ if (typeof content === 'object' && content !== null) {
41
+ // ディレクトリ
42
+ createStructure(fullPath, content)
43
+ } else {
44
+ // ファイル
45
+ fs.writeFileSync(fullPath, content || '')
46
+ }
47
+ }
48
+ }
49
+
50
+ createStructure(fixtureDir, structure)
51
+ return fixtureDir
52
+ }
53
+
54
+ /**
55
+ * テスト終了後のクリーンアップ
56
+ */
57
+ function cleanupFixtures() {
58
+ if (fs.existsSync(fixturesRoot)) {
59
+ const entries = fs.readdirSync(fixturesRoot)
60
+ for (const entry of entries) {
61
+ const fullPath = path.join(fixturesRoot, entry)
62
+ if (fs.statSync(fullPath).isDirectory()) {
63
+ fs.rmSync(fullPath, { recursive: true, force: true })
64
+ }
65
+ }
66
+ }
67
+ }
68
+
69
+ // テスト終了後にクリーンアップ
70
+ afterAll(() => {
71
+ cleanupFixtures()
72
+ })
73
+
74
+ ruleTester.run('require-barrel-import', rule, {
75
+ valid: [
76
+ // 同階層・サブディレクトリからのimport(エラーにならない)
77
+ {
78
+ code: `import { useMenu } from './hooks/useMenu'`,
79
+ filename: (() => {
80
+ createFixture('same-level-import', {
81
+ 'Menu': {
82
+ 'MenuItem.tsx': '',
83
+ 'index.tsx': 'export {}',
84
+ 'hooks': {
85
+ 'useMenu.ts': '',
86
+ },
87
+ },
88
+ })
89
+ return `${fixturesRoot}/same-level-import/Menu/MenuItem.tsx`
90
+ })(),
91
+ },
92
+
93
+ // Next.js App Router特殊文字パス - 同階層import
94
+ {
95
+ code: `import { useUsers } from './hooks/useUsers'`,
96
+ filename: (() => {
97
+ createFixture('nextjs-special-chars', {
98
+ 'app': {
99
+ '(private)': {
100
+ 'settings': {
101
+ 'user_roles': {
102
+ '_components': {
103
+ 'index.tsx': 'export {}',
104
+ 'AddUserRoleDialog': {
105
+ 'index.tsx': 'export {}',
106
+ 'AddUserRoleDialog.tsx': '',
107
+ 'hooks': {
108
+ 'useUsers.ts': '',
109
+ },
110
+ },
111
+ },
112
+ },
113
+ },
114
+ },
115
+ },
116
+ })
117
+ return `${fixturesRoot}/nextjs-special-chars/app/(private)/settings/user_roles/_components/AddUserRoleDialog/AddUserRoleDialog.tsx`
118
+ })(),
119
+ },
120
+
121
+ // Dynamic Routes - [id]パス(同階層)
122
+ {
123
+ code: `import { useDetail } from './hooks/useDetail'`,
124
+ filename: (() => {
125
+ createFixture('nextjs-dynamic-route', {
126
+ 'app': {
127
+ 'items': {
128
+ '[id]': {
129
+ 'index.tsx': 'export {}',
130
+ 'DetailPage.tsx': '',
131
+ 'hooks': {
132
+ 'useDetail.ts': '',
133
+ },
134
+ },
135
+ },
136
+ },
137
+ })
138
+ return `${fixturesRoot}/nextjs-dynamic-route/app/items/[id]/DetailPage.tsx`
139
+ })(),
140
+ },
141
+
142
+ // barrel が存在しない場合(同階層サブディレクトリ)
143
+ {
144
+ code: `import { helper } from './utils/helper'`,
145
+ filename: (() => {
146
+ createFixture('no-barrel', {
147
+ 'components': {
148
+ 'Button': {
149
+ 'Button.tsx': '',
150
+ 'utils': {
151
+ 'helper.ts': '',
152
+ },
153
+ },
154
+ },
155
+ })
156
+ return `${fixturesRoot}/no-barrel/components/Button/Button.tsx`
157
+ })(),
158
+ },
159
+
160
+ // 親階層からのimport + barrelなし(エラーにならない)
161
+ {
162
+ code: `import { helper } from '../utils/helper'`,
163
+ filename: (() => {
164
+ createFixture('parent-import-no-barrel', {
165
+ // index.tsx なし(barrelなし)
166
+ 'Button': {
167
+ 'Button.tsx': '',
168
+ },
169
+ 'utils': {
170
+ 'helper.ts': '',
171
+ },
172
+ })
173
+ return `${fixturesRoot}/parent-import-no-barrel/Button/Button.tsx`
174
+ })(),
175
+ },
176
+
177
+ // Next.js特殊文字パス - 親階層からのimport + barrelなし
178
+ {
179
+ code: `import { createUserRole } from '../hooks/createUserRoleAction'`,
180
+ filename: (() => {
181
+ createFixture('nextjs-parent-no-barrel', {
182
+ // index.tsx なし(barrelなし)
183
+ 'AddUserRoleDialog': {
184
+ 'AddUserRoleDialog.tsx': '',
185
+ },
186
+ 'hooks': {
187
+ 'createUserRoleAction.ts': '',
188
+ },
189
+ })
190
+ return `${fixturesRoot}/nextjs-parent-no-barrel/AddUserRoleDialog/AddUserRoleDialog.tsx`
191
+ })(),
192
+ },
193
+
194
+ // Dynamic Routes - 親階層からのimport + barrelなし
195
+ {
196
+ code: `import { api } from '../api/client'`,
197
+ filename: (() => {
198
+ createFixture('dynamic-route-parent-no-barrel', {
199
+ // index.tsx なし(barrelなし)
200
+ '[id]': {
201
+ 'DetailPage.tsx': '',
202
+ },
203
+ 'api': {
204
+ 'client.ts': '',
205
+ },
206
+ })
207
+ return `${fixturesRoot}/dynamic-route-parent-no-barrel/[id]/DetailPage.tsx`
208
+ })(),
209
+ },
210
+
211
+ // ============================================================
212
+ // Path alias - 同階層からのimport(エラーにならない)
213
+ // ============================================================
214
+ {
215
+ code: `import { useMenu } from '~/path-alias-same-level/Menu/hooks/useMenu'`,
216
+ filename: (() => {
217
+ createFixture('path-alias-same-level', {
218
+ 'Menu': {
219
+ 'MenuItem.tsx': '',
220
+ 'index.tsx': 'export {}',
221
+ 'hooks': {
222
+ 'useMenu.ts': '',
223
+ },
224
+ },
225
+ })
226
+ return `${fixturesRoot}/path-alias-same-level/Menu/MenuItem.tsx`
227
+ })(),
228
+ },
229
+
230
+ // Path alias - 同階層サブディレクトリ + barrelなし
231
+ {
232
+ code: `import { helper } from '~/path-alias-no-barrel/components/Button/utils/helper'`,
233
+ filename: (() => {
234
+ createFixture('path-alias-no-barrel', {
235
+ 'components': {
236
+ 'Button': {
237
+ 'Button.tsx': '',
238
+ 'utils': {
239
+ 'helper.ts': '',
240
+ },
241
+ },
242
+ },
243
+ })
244
+ return `${fixturesRoot}/path-alias-no-barrel/components/Button/Button.tsx`
245
+ })(),
246
+ },
247
+
248
+ // Path alias - 親階層からのimport + barrelなし
249
+ {
250
+ code: `import { helper } from '~/path-alias-parent-no-barrel/utils/helper'`,
251
+ filename: (() => {
252
+ createFixture('path-alias-parent-no-barrel', {
253
+ // index.tsx なし(barrelなし)
254
+ 'Button': {
255
+ 'Button.tsx': '',
256
+ },
257
+ 'utils': {
258
+ 'helper.ts': '',
259
+ },
260
+ })
261
+ return `${fixturesRoot}/path-alias-parent-no-barrel/Button/Button.tsx`
262
+ })(),
263
+ },
264
+
265
+ // 親階層からのimport(commonParentにbarrelがある場合)
266
+ {
267
+ code: `import { createUserRole } from '../hooks/createUserRoleAction'`,
268
+ filename: (() => {
269
+ createFixture('parent-import-common-parent-barrel', {
270
+ 'components': {
271
+ 'index.tsx': 'export {}', // commonParentのbarrelは除外される
272
+ 'AddDialog': {
273
+ 'AddDialog.tsx': '',
274
+ },
275
+ 'hooks': {
276
+ 'createUserRoleAction.ts': '',
277
+ },
278
+ },
279
+ })
280
+ return `${fixturesRoot}/parent-import-common-parent-barrel/components/AddDialog/AddDialog.tsx`
281
+ })(),
282
+ },
283
+
284
+ // 複雑なネスト - commonParentのbarrelは除外される
285
+ {
286
+ code: `import type { RequestStepActionNotSkipped } from '../utils/withSkipped'`,
287
+ filename: (() => {
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)
300
+ },
301
+ },
302
+ },
303
+ },
304
+ },
305
+ })
306
+ return `${fixturesRoot}/nested-common-parent-barrel/Nodes/StepNodeRequestView/Approvers/ApproverRow/buildUserRowsProps.ts`
307
+ })(),
308
+ languageOptions: {
309
+ parser: require('typescript-eslint').parser,
310
+ },
311
+ },
312
+ ],
313
+
314
+ invalid: [
315
+ // 親階層からのimport(import先の親にbarrelがある場合)
316
+ {
317
+ code: `import { api } from '../api/client'`,
318
+ filename: (() => {
319
+ createFixture('parent-import-with-barrel', {
320
+ 'components': {
321
+ 'AddDialog': {
322
+ 'AddDialog.tsx': '',
323
+ },
324
+ 'api': {
325
+ 'index.tsx': 'export {}', // import先の親にbarrel
326
+ 'client.ts': '',
327
+ },
328
+ },
329
+ })
330
+ return `${fixturesRoot}/parent-import-with-barrel/components/AddDialog/AddDialog.tsx`
331
+ })(),
332
+ errors: [
333
+ {
334
+ message: /からimportするか、.*のbarrelファイルを削除して直接import可能にしてください/,
335
+ },
336
+ ],
337
+ },
338
+
339
+ // Next.js特殊文字パス - import先の親にbarrel
340
+ {
341
+ code: `import { api } from '../api/client'`,
342
+ filename: (() => {
343
+ createFixture('nextjs-import-parent-barrel', {
344
+ 'app': {
345
+ '(private)': {
346
+ 'settings': {
347
+ 'user_roles': {
348
+ '_components': {
349
+ 'AddUserRoleDialog': {
350
+ 'AddUserRoleDialog.tsx': '',
351
+ },
352
+ 'api': {
353
+ 'index.tsx': 'export {}', // import先の親にbarrel
354
+ 'client.ts': '',
355
+ },
356
+ },
357
+ },
358
+ },
359
+ },
360
+ },
361
+ })
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`
387
+ })(),
388
+ errors: [
389
+ {
390
+ message: /からimportするか、.*のbarrelファイルを削除して直接import可能にしてください/,
391
+ },
392
+ ],
393
+ },
394
+ ],
395
+ })
@@ -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
  })
package/tsconfig.json CHANGED
@@ -1,8 +1,10 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "paths": {
4
+ // ルール実装で使用
4
5
  "@/*": ["./src/*"],
5
- "~/*": ["./src/*"]
6
+ // テスト用
7
+ "~/*": ["./test-fixtures/*"]
6
8
  }
7
9
  }
8
10
  }