eslint-plugin-smarthr 0.0.0 → 0.0.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 CHANGED
@@ -2,6 +2,14 @@
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
+ ### [0.0.1](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.0.0...v0.0.1) (2022-02-08)
6
+
7
+
8
+ ### Features
9
+
10
+ * add type property function params redundant ([758df90](https://github.com/kufu/eslint-plugin-smarthr/commit/758df90f89bd27dd589aeeb55165e27c8e072b08))
11
+ * redundant-name の修正候補を操作できるように改修 ([20991e8](https://github.com/kufu/eslint-plugin-smarthr/commit/20991e874890556e84e7c682e789e4b2650a85b0))
12
+
5
13
  ### [0.0.2](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.0.1...v0.0.2) (2022-01-26)
6
14
 
7
15
  ### [0.0.1](https://github.com/kufu/eslint-plugin-smarthr/compare/v0.0.0...v0.0.1) (2022-01-26)
package/README.md CHANGED
@@ -305,47 +305,14 @@ import { PrimaryButton, SecondaryButton } from 'smarthr-ui'
305
305
  ### config
306
306
 
307
307
  - tsconfig.json の compilerOptions.pathsに '@/*' としてroot path を指定する必要があります
308
- - 以下の設定を記述する必要があります
308
+ - 以下の設定を行えます。全て省略可能です。
309
309
  - ignoreKeywords
310
310
  - ディレクトリ名から生成されるキーワードに含めたくない文字列を指定します
311
- - keywordGenerator
312
- - ディレクトリ名から生成されるキーワードを元に最終的なキーワードを生成します
313
- - suffixGenerator:
311
+ - betterNames
312
+ - 対象の名前を修正する候補を指定します
313
+ - suffix:
314
314
  - type のみ指定出来ます
315
- - type のsuffixを生成します
316
-
317
- #### 指定例
318
- ```
319
- const ignorekeywords = ['views', 'parts']
320
- const keywordGenerator = ({ keywords }) => (
321
- keywords.reduce((prev, keyword, index) => {
322
- switch (keyword) {
323
- case 'repositories':
324
- return [...prev, keyword, 'repository']
325
- }
326
-
327
- const replacedKeyword = keyword.replace(/(repository|s)$/, '')
328
- if (keyword !== replacedKeyword) {
329
- return [...prev, keyword, replacedKeyword]
330
- }
331
-
332
- return [...prev, keyword]
333
- }, [])
334
- )
335
- // 例: actions 以下の場合だけ 'Action' もしくは `Actions` のSuffixを許可する
336
- const suffixGenerator = ({ node, filename }) => {
337
- let suffix = ['Props', 'Type']
338
-
339
- if (filename.match(/\/actions\//)) {
340
- suffix = [
341
- isUnionType || (node.typeAnnotation.type === 'TSTypeReference' && node.id.name.match(/Actions$/))
342
- ? 'Actions'
343
- : 'Action',
344
- ...suffix,
345
- ]
346
- }
347
- }
348
- ```
315
+ - type のsuffixを指定します
349
316
 
350
317
  #### ファイル例
351
318
  - `@/crews/index/views/page.tsx` の場合
@@ -359,17 +326,33 @@ const suffixGenerator = ({ node, filename }) => {
359
326
  ### rules
360
327
 
361
328
  ```js
329
+ const ignorekeywords = ['views', 'parts']
330
+ const betterNames = {
331
+ '\/repositories\/': {
332
+ operator: '-',
333
+ names: ['repository', 'Repository'],
334
+ },
335
+ '\/entities\/': {
336
+ operator: '+',
337
+ names: ['entity'],
338
+ },
339
+ '\/slices\/': {
340
+ operator: '=',
341
+ names: ['index'],
342
+ },
343
+ }
344
+
362
345
  {
363
346
  rules: {
364
347
  'smarthr/redundant-name': [
365
348
  'error', // 'warn', 'off'
366
349
  {
367
- type: { ignorekeywords, keywordGenerator, suffixGenerator },
368
- file: { ignorekeywords, keywordGenerator },
369
- // property: { ignorekeywords, keywordGenerator },
370
- // function: { ignorekeywords, keywordGenerator },
371
- // variable: { ignorekeywords, keywordGenerator },
372
- // class: { ignorekeywords, keywordGenerator },
350
+ type: { ignorekeywords, suffix: ['Props', 'Type'] },
351
+ file: { ignorekeywords, betternames },
352
+ // property: { ignorekeywords },
353
+ // function: { ignorekeywords },
354
+ // variable: { ignorekeywords },
355
+ // class: { ignorekeywords },
373
356
  }
374
357
  ]
375
358
  },
package/jest.config.js ADDED
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ moduleFileExtensions: ['js'],
3
+ testMatch: ['**/test/**/*.+(js)'],
4
+ roots: ['<rootDir>/']
5
+ }
@@ -6,7 +6,6 @@ const BASE_SCHEMA_PROPERTIES = {
6
6
  globalModuleDir: { type: 'array', items: { type: 'string' } },
7
7
  domainModuleDir: { type: 'array', items: { type: 'string' }, default: [] },
8
8
  domainConstituteDir: { type: 'array', items: { type: 'string' } },
9
- isDomain: { type: 'function' },
10
9
  }
11
10
 
12
11
  const calculateDomainContext = (context) => {
@@ -87,13 +86,14 @@ const calculateDomainNode = (calclatedContext, node) => {
87
86
  let filteredDirs = dirs
88
87
  let filteredPaths = paths
89
88
 
89
+ const deductedNames = []
90
90
  const recursiveDeductionEq = () => {
91
91
  if (dirs.length === 0 || paths.length === 0) {
92
92
  return
93
93
  }
94
94
 
95
95
  if (dirs[0] === paths[0]) {
96
- dirs.shift()
96
+ deductedNames.push(dirs.shift())
97
97
  paths.shift()
98
98
  recursiveDeductionEq()
99
99
  }
@@ -102,21 +102,22 @@ const calculateDomainNode = (calclatedContext, node) => {
102
102
  filteredDirs = dirs
103
103
  filteredPaths = paths
104
104
 
105
+ let isDomainConstitute = false
106
+
105
107
  if (option.domainConstituteDir) {
108
+ const { domainConstituteDir } = option
109
+ isDomainConstitute =
110
+ !!deductedNames.find((d) => domainConstituteDir.includes(d)) || // 同一dirを削り、その中にconstitute dir があれば同一ドメイン
111
+ domainConstituteDir.includes(dirs[0]) && domainConstituteDir.includes(paths[0]) // 同一を削りきった先頭が両方constitute dirならば同一ドメイン
112
+
106
113
  // HINT: 同一ドメイン内(例: workflows/index)で定形で利用されるディレクトリ名を省くことで
107
114
  // ドメインの識別に利用される文字を抽出する
108
115
  dirs = dirs.filter((k) => !option.domainConstituteDir.includes(k))
109
116
  paths = paths.filter((k) => !option.domainConstituteDir.includes(k))
110
117
  }
111
118
 
112
- if (option.isDomain) {
113
- // HINT: ドメインの識別に利用される文字を抽出する
114
- dirs = dirs.filter((k) => option.isDomain(k))
115
- paths = paths.filter((k) => option.isDomain(k))
116
- }
117
-
118
119
  const isLowerImport = filteredDirs.length === 0 // 同一階層、もしくは下層からのimport
119
- const isDomainImport = dirs.length === 0 // 同一ドメイン内、もしくは同一階層・下層からのimport
120
+ const isDomainImport = dirs.length === 0 || isDomainConstitute // 同一ドメイン内、もしくは同一階層・下層からのimport
120
121
  const isModuleImport = paths.length > 0 && option.domainModuleDir.includes(paths[0]) // ドメイン内共通パーツ
121
122
 
122
123
  return {
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "eslint-plugin-smarthr",
3
- "version": "0.0.0",
3
+ "version": "0.0.1",
4
4
  "author": "SmartHR",
5
5
  "license": "MIT",
6
6
  "description": "A sharable ESLint plugin for SmartHR",
7
7
  "main": "index.js",
8
8
  "scripts": {
9
+ "test": "jest",
9
10
  "release:dryrun": "standard-version --dry-run",
10
11
  "release": "standard-version"
11
12
  },
@@ -21,9 +22,12 @@
21
22
  "url": "https://github.com/kufu/eslint-plugin-smarthr/issues"
22
23
  },
23
24
  "dependencies": {
25
+ "inflected": "^2.1.0",
24
26
  "json5": "^2.2.0"
25
27
  },
26
28
  "devDependencies": {
29
+ "eslint": "^8.8.0",
30
+ "jest": "^27.4.7",
27
31
  "standard-version": "^9.3.2"
28
32
  },
29
33
  "peerDependencies": {
@@ -1,15 +1,30 @@
1
- const SCHEMA = [
2
- {
3
- type: 'object',
4
- properties: {
5
- targets: { type: 'object', default: {} },
6
- generateReportMessage: { type: 'function' },
1
+ const SCHEMA = [{
2
+ type: "object",
3
+ patternProperties: {
4
+ ".+": {
5
+ type: "object",
6
+ required: [
7
+ "imported",
8
+ ],
9
+ properties: {
10
+ imported: {
11
+ type: ["boolean", "array"],
12
+ items: {
13
+ type: "string"
14
+ }
15
+ },
16
+ reportMessage: {
17
+ type: "string"
18
+ }
19
+ },
20
+ additionalProperties: false
21
+ }
7
22
  },
8
- additionalProperties: false,
23
+ additionalProperties: true,
9
24
  }
10
25
  ]
11
26
 
12
- const generateDefaultReportMessage = (source, imported) => `${source}${imported && `/${imported}`} は利用しないでください`
27
+ const defaultReportMessage = (moduleName, exportName) => `${moduleName}${typeof exportName == 'string' ? `/${exportName}`: ''} は利用しないでください`
13
28
 
14
29
  module.exports = {
15
30
  meta: {
@@ -20,32 +35,32 @@ module.exports = {
20
35
  schema: SCHEMA,
21
36
  },
22
37
  create(context) {
23
- const option = context.options[0]
24
- const parsedOption = Object.entries(option.targets)
25
- const generateReportMessage = option.generateReportMessage || generateDefaultReportMessage
26
-
38
+ const options = context.options[0]
39
+ const targetModules = Object.keys(options)
27
40
  return {
28
41
  ImportDeclaration: (node) => {
29
- parsedOption.forEach(([matchText, importNames]) => {
30
- if (!node.source.value.match(new RegExp(matchText))) {
42
+ targetModules.forEach((targetModule) => {
43
+ if (!node.source.value.match(new RegExp(targetModule))) {
31
44
  return
32
45
  }
33
-
34
- const imported = (() => {
35
- if (!Array.isArray(importNames)) {
36
- return !!importNames
46
+
47
+ const {imported, reportMessage} = Object.assign({imported: true}, options[targetModule])
48
+ const useImported = (() => {
49
+ if (!Array.isArray(imported)) {
50
+ return !!imported
37
51
  }
38
- const specifier = node.specifiers.find((s) => importNames.includes(s.imported.name))
52
+
53
+ const specifier = node.specifiers.find((s) => s.imported && imported.includes(s.imported.name))
39
54
 
40
55
  return specifier ? specifier.imported.name : false
41
56
  })()
42
57
 
43
- if (imported) {
58
+ if (useImported) {
44
59
  context.report({
45
60
  node,
46
61
  messageId: 'prohibit_import',
47
62
  data: {
48
- message: generateReportMessage(node.source.value, imported === true ? '' : imported),
63
+ message: reportMessage ? reportMessage.replace('{{module}}', node.source.value).replace('{{export}}', useImported) : defaultReportMessage(node.source.value, useImported)
49
64
  },
50
65
  });
51
66
  }
@@ -54,4 +69,5 @@ module.exports = {
54
69
  }
55
70
  },
56
71
  }
72
+
57
73
  module.exports.schema = SCHEMA
@@ -1,8 +1,13 @@
1
1
  const path = require('path')
2
+ const Inflector = require('inflected')
3
+
2
4
  const { rootPath } = require('../libs/common')
3
5
 
4
6
  const uniq = (array) => array.filter((elem, index, self) => self.indexOf(elem) === index)
5
7
 
8
+ const COMMON_DEFAULT_CONFIG = {
9
+ IGNORE_KEYWORDS: ['redux', 'views', 'pages', 'parts'],
10
+ }
6
11
  const DEFAULT_CONFIG = {
7
12
  type: {
8
13
  IGNORE_KEYWORDS: [
@@ -11,44 +16,56 @@ const DEFAULT_CONFIG = {
11
16
  ],
12
17
  SUFFIX: ['Props', 'Type'],
13
18
  },
14
- file: {
15
- IGNORE_KEYWORDS: ['redux', 'views', 'pages', 'parts'],
16
- },
17
- property: {
18
- IGNORE_KEYWORDS: ['redux', 'views', 'pages', 'parts'],
19
- },
20
- function: {
21
- IGNORE_KEYWORDS: ['redux', 'views', 'pages', 'parts'],
22
- },
23
- variable: {
24
- IGNORE_KEYWORDS: ['redux', 'views', 'pages', 'parts'],
25
- },
26
- class: {
27
- IGNORE_KEYWORDS: ['redux', 'views', 'pages', 'parts'],
28
- },
29
- method: {
30
- IGNORE_KEYWORDS: ['redux', 'views', 'pages', 'parts'],
31
- },
19
+ typeProperty: COMMON_DEFAULT_CONFIG,
20
+ file: COMMON_DEFAULT_CONFIG,
21
+ property: COMMON_DEFAULT_CONFIG,
22
+ function: COMMON_DEFAULT_CONFIG,
23
+ functionParams: COMMON_DEFAULT_CONFIG,
24
+ variable: COMMON_DEFAULT_CONFIG,
25
+ class: COMMON_DEFAULT_CONFIG,
26
+ method: COMMON_DEFAULT_CONFIG,
32
27
  }
33
28
 
29
+ const BETTER_NAMES_CALCULATER_PROPERTY = {
30
+ type: 'object',
31
+ properties: {
32
+ operator: ['-', '+', '='],
33
+ names: {
34
+ type: 'array',
35
+ items: 'string',
36
+ },
37
+ },
38
+ }
34
39
  const DEFAULT_SCHEMA_PROPERTY = {
35
40
  ignoreKeywords: { type: 'array', items: { type: 'string' } },
36
- keywordGenerator: { type: 'function' },
41
+ betterNames: {
42
+ type: 'object',
43
+ properties: {
44
+ operator: ['-', '+', '='],
45
+ names: {
46
+ type: 'array',
47
+ items: 'string',
48
+ },
49
+ },
50
+ },
37
51
  }
38
52
 
39
53
  const SCHEMA = [
40
54
  {
41
55
  type: 'object',
42
56
  properties: {
43
- file: DEFAULT_SCHEMA_PROPERTY,
44
57
  type: {
45
58
  ...DEFAULT_SCHEMA_PROPERTY,
46
- suffixGenerator: { type: 'function' },
59
+ suffix: { type: 'array', items: { type: 'string' } },
47
60
  },
61
+ typeProperty: DEFAULT_SCHEMA_PROPERTY,
62
+ file: DEFAULT_SCHEMA_PROPERTY,
48
63
  property: DEFAULT_SCHEMA_PROPERTY,
49
64
  function: DEFAULT_SCHEMA_PROPERTY,
65
+ functionParams: DEFAULT_SCHEMA_PROPERTY,
50
66
  variable: DEFAULT_SCHEMA_PROPERTY,
51
67
  class: DEFAULT_SCHEMA_PROPERTY,
68
+ method: DEFAULT_SCHEMA_PROPERTY,
52
69
  },
53
70
  additionalProperties: false,
54
71
  }
@@ -68,21 +85,22 @@ const generateRedundantKeywords = ({ args, key, terminalImportName }) => {
68
85
  const option = args.option[key] || {}
69
86
  const ignoreKeywords = option.ignoreKeywords || DEFAULT_CONFIG[key].IGNORE_KEYWORDS
70
87
  const terminalImportKeyword = terminalImportName ? terminalImportName.toLowerCase() : ''
71
- const filterKeywords = (keys) => keys.filter((k) => k !== terminalImportKeyword && !ignoreKeywords.includes(k))
72
88
 
73
- let redundantKeywords = filterKeywords(args.keywords)
74
- if (option.keywordGenerator) {
75
- redundantKeywords = option.keywordGenerator({
76
- ...args,
77
- redundantKeywords,
78
- })
79
- }
89
+ return args.keywords.reduce((prev, keyword) => {
90
+ if (keyword === terminalImportKeyword || ignoreKeywords.includes(keyword)) {
91
+ return prev
92
+ }
93
+
94
+ const singularized = Inflector.singularize(keyword)
80
95
 
81
- return redundantKeywords
96
+ return singularized === keyword ? [...prev, keyword] : [...prev, keyword, singularized]
97
+ }, [])
82
98
  }
83
99
  const handleReportBetterName = ({
84
100
  key,
85
101
  context,
102
+ option,
103
+ filename,
86
104
  redundantKeywords,
87
105
  defaultBetterName,
88
106
  fetchName,
@@ -99,40 +117,75 @@ const handleReportBetterName = ({
99
117
  return
100
118
  }
101
119
 
102
- let hitCount = 0
103
- let betterName = redundantKeywords.reduce((prev, keyword) => {
104
- const replaced = prev.replace(new RegExp(keyword, 'i'), '')
120
+ let candidates = []
121
+ let conciseName = redundantKeywords.reduce((prev, keyword) => {
122
+ const regex = new RegExp(`(${keyword})`, 'i')
123
+ const matcher = prev.match(regex)
124
+
125
+ if (matcher) {
126
+ candidates.push(matcher[1])
105
127
 
106
- if (prev !== replaced) {
107
- hitCount++
128
+ return prev.replace(regex, '')
108
129
  }
109
130
 
110
- return replaced
131
+ return prev
111
132
  }, name)
112
133
 
113
- if (name !== betterName) {
114
- betterName = betterName
134
+ if (name !== conciseName) {
135
+ conciseName = conciseName
115
136
  .replace(/^_+/, '')
116
137
  .replace(/_+$/, '')
117
138
  .replace(/_+/, '_')
139
+ let fullRedundant = false
118
140
 
119
- if (!betterName) {
141
+ if (!conciseName) {
142
+ fullRedundant = true
120
143
  // HINT: 1keywordで構成されている名称はそのままにする
121
- betterName = hitCount === 1 ? name : defaultBetterName
144
+ conciseName = candidates.length === 1 ? name : defaultBetterName
122
145
  }
123
146
 
124
147
  // HINT: camelCase、lower_snake_case の場合、keywordが取り除かれた結果違うケースになってしまう場合があるので対応する
125
- if (name.match(/^[a-z]/) && betterName.match(/^[A-Z]/)) {
126
- betterName = `${betterName[0].toLowerCase()}${betterName.slice(1)}`
148
+ if (name.match(/^[a-z]/) && conciseName.match(/^[A-Z]/)) {
149
+ conciseName = `${conciseName[0].toLowerCase()}${conciseName.slice(1)}`
150
+ }
151
+
152
+ if (fullRedundant) {
153
+ if (name.match(/^[A-Z]/)) {
154
+ candidates = candidates.map((k) => `${k[0].toUpperCase()}${k.slice(1)}`)
155
+ }
156
+ } else {
157
+ candidates = []
127
158
  }
159
+
160
+ candidates = uniq([conciseName, ...candidates].filter((k) => !!k))
161
+
162
+ if (option.betterNames) {
163
+ Object.entries(option.betterNames).forEach(([regex, calc]) => {
164
+ if (calc && filename.match(new RegExp(regex))) {
165
+ switch(calc.operator) {
166
+ case '=':
167
+ candidates = calc.names
168
+ break
169
+ case '-':
170
+ candidates = candidates.filter((c) => !calc.names.includes(c))
171
+ break
172
+ case '+':
173
+ candidates = uniq([...candidates, ...calc.names])
174
+ break
175
+ }
176
+ }
177
+ })
178
+ }
179
+
180
+ candidates = candidates.filter((c) => c !== name)
128
181
  }
129
182
 
130
- if (name !== betterName) {
183
+ if (candidates.length > 0) {
131
184
  context.report({
132
185
  node,
133
186
  messageId: `${key}-name`,
134
187
  data: {
135
- message: generateMessage({ name, betterName }),
188
+ message: generateMessage({ name, betterName: candidates.join(', ') }),
136
189
  },
137
190
  });
138
191
  }
@@ -145,17 +198,10 @@ const generateTypeRedundant = (args) => {
145
198
  const redundantKeywords = generateRedundantKeywords({ args, key })
146
199
  const option = args.option[key]
147
200
  const defaultConfig = DEFAULT_CONFIG[key]
148
- const actualArgs = {
149
- ...args,
150
- redundantKeywords,
151
- }
152
201
 
153
202
  return (node) => {
154
203
  const typeName = node.id.name
155
- const suffix = option.suffixGenerator ? option.suffixGenerator({
156
- ...actualArgs,
157
- node,
158
- }) : defaultConfig.SUFFIX
204
+ const suffix = option.suffix || defaultConfig.SUFFIX
159
205
 
160
206
  let SuffixedName = typeName
161
207
  let report = null
@@ -197,12 +243,41 @@ const generateTypeRedundant = (args) => {
197
243
  }
198
244
  }
199
245
 
246
+ const generateTypePropertyRedundant = (args) => {
247
+ const key = 'typeProperty'
248
+
249
+ return handleReportBetterName({
250
+ ...args,
251
+ key,
252
+ option: args.option[key],
253
+ redundantKeywords: generateRedundantKeywords({ args, key }),
254
+ defaultBetterName: '',
255
+ fetchName: (node) => node.key.name,
256
+ })
257
+ }
258
+ const generateTypePropertyFunctionParamsRedundant = (args) => {
259
+ const key = 'typeProperty'
260
+ const redundant = handleReportBetterName({
261
+ ...args,
262
+ key,
263
+ option: args.option[key],
264
+ redundantKeywords: generateRedundantKeywords({ args, key }),
265
+ defaultBetterName: '',
266
+ fetchName: (node) => node.name,
267
+ })
268
+
269
+ return (node) => {
270
+ node.params.forEach((param) => redundant(param))
271
+ }
272
+ }
273
+
200
274
  const generatePropertyRedundant = (args) => {
201
275
  const key = 'property'
202
276
 
203
277
  return handleReportBetterName({
278
+ ...args,
204
279
  key,
205
- context: args.context,
280
+ option: args.option[key],
206
281
  redundantKeywords: generateRedundantKeywords({ args, key }),
207
282
  defaultBetterName: 'item',
208
283
  fetchName: (node) => node.key.name,
@@ -214,8 +289,9 @@ const generateFileRedundant = (args) => {
214
289
  const terminalImportName = fetchTerminalImportName(args.filename)
215
290
 
216
291
  return handleReportBetterName({
292
+ ...args,
217
293
  key,
218
- context: args.context,
294
+ option: args.option[key],
219
295
  redundantKeywords: generateRedundantKeywords({ args, key, terminalImportName }),
220
296
  defaultBetterName: 'index',
221
297
  fetchName: () => terminalImportName,
@@ -227,20 +303,37 @@ const generateFunctionRedundant = (args) => {
227
303
  const key = 'function'
228
304
 
229
305
  return handleReportBetterName({
306
+ ...args,
230
307
  key,
231
- context: args.context,
308
+ option: args.option[key],
232
309
  redundantKeywords: generateRedundantKeywords({ args, key, terminalImportName: fetchTerminalImportName(args.filename) }),
233
310
  defaultBetterName: '',
234
311
  fetchName: (node) => node.id.name,
235
312
  })
236
313
  }
314
+ const generateFunctionParamsRedundant = (args) => {
315
+ const key = 'functionParams'
316
+ const redundant = handleReportBetterName({
317
+ ...args,
318
+ key,
319
+ option: args.option[key],
320
+ redundantKeywords: generateRedundantKeywords({ args, key }),
321
+ defaultBetterName: '',
322
+ fetchName: (node) => node.name,
323
+ })
324
+
325
+ return (node) => {
326
+ node.params.forEach((param) => redundant(param))
327
+ }
328
+ }
237
329
 
238
330
  const generateVariableRedundant = (args) => {
239
331
  const key = 'variable'
240
332
 
241
333
  return handleReportBetterName({
334
+ ...args,
242
335
  key,
243
- context: args.context,
336
+ option: args.option[key],
244
337
  redundantKeywords: generateRedundantKeywords({ args, key, terminalImportName: fetchTerminalImportName(args.filename) }),
245
338
  defaultBetterName: '',
246
339
  fetchName: (node) => node.id.name,
@@ -251,8 +344,9 @@ const generateClassRedundant = (args) => {
251
344
  const key = 'class'
252
345
 
253
346
  return handleReportBetterName({
347
+ ...args,
254
348
  key,
255
- context: args.context,
349
+ option: args.option[key],
256
350
  redundantKeywords: generateRedundantKeywords({ args, key, terminalImportName: fetchTerminalImportName(args.filename) }),
257
351
  defaultBetterName: '',
258
352
  fetchName: (node) => node.id.name,
@@ -263,8 +357,9 @@ const generateMethodRedundant = (args) => {
263
357
  const key = 'method'
264
358
 
265
359
  return handleReportBetterName({
360
+ ...args,
266
361
  key,
267
- context: args.context,
362
+ option: args.option[key],
268
363
  redundantKeywords: generateRedundantKeywords({ args, key }),
269
364
  defaultBetterName: 'item',
270
365
  fetchName: (node) => node.key.name,
@@ -278,8 +373,10 @@ module.exports = {
278
373
  'file-name': ' {{ message }}',
279
374
  'type-name': '{{ message }}',
280
375
  'type-name/invalid-suffix': '{{ message }}',
376
+ 'typeProperty-name': '{{ message }}',
281
377
  'property-name': ' {{ message }}',
282
378
  'function-name': ' {{ message }}',
379
+ 'functionParams-name': ' {{ message }}',
283
380
  'variable-name': ' {{ message }}',
284
381
  'class-name': ' {{ message }}',
285
382
  'method-name': ' {{ message }}',
@@ -324,50 +421,73 @@ module.exports = {
324
421
  keywords,
325
422
  }
326
423
 
424
+ const addRule = (key, redundant) => {
425
+ const addedRules = rules[key] || []
426
+
427
+ rules[key] = [...addedRules, redundant]
428
+ }
429
+
327
430
  if (option.type) {
328
- rules = {
329
- ...rules,
330
- TSTypeAliasDeclaration: generateTypeRedundant(args),
331
- }
431
+ addRule('TSTypeAliasDeclaration', generateTypeRedundant(args))
432
+ // addRule('TSInterfaceDeclaration', generateTypeRedundant(args)) // 必要になったら実装する
433
+ }
434
+ if (option.typeProperty) {
435
+ const typePropRedundant = generateTypePropertyRedundant(args)
436
+ const typeFuncParamRedundant = generateTypePropertyFunctionParamsRedundant(args)
437
+
438
+ addRule('TSPropertySignature', (node) => {
439
+ typePropRedundant(node)
440
+
441
+ if (node.typeAnnotation.typeAnnotation.type === 'TSFunctionType') {
442
+ typeFuncParamRedundant(node.typeAnnotation.typeAnnotation)
443
+ }
444
+ })
332
445
  }
333
446
  if (option.property) {
334
- propRedundant = generatePropertyRedundant(args)
447
+ const redundant = generatePropertyRedundant(args)
335
448
 
336
- rules = {
337
- ...rules,
338
- Property: propRedundant,
339
- PropertyDefinition: propRedundant,
340
- }
449
+ addRule('Property', redundant)
450
+ addRule('PropertyDefinition', redundant)
341
451
  }
342
452
  if (option.file) {
343
- rules = {
344
- ...rules,
345
- Program: generateFileRedundant(args),
346
- }
453
+ addRule('Program', generateFileRedundant(args))
347
454
  }
348
455
  if (option.function) {
349
- rules = {
350
- ...rules,
351
- FunctionDeclaration: generateFunctionRedundant(args),
352
- }
456
+ addRule('FunctionDeclaration', generateFunctionRedundant(args))
457
+ }
458
+ if (option.functionParams) {
459
+ const redundant = generateFunctionParamsRedundant(args)
460
+
461
+ addRule('FunctionDeclaration', redundant)
462
+ addRule('ArrowFunctionExpression', redundant)
463
+ addRule('MethodDefinition', (node) => {
464
+ if (node.value.type === 'FunctionExpression') {
465
+ redundant(node.value)
466
+ }
467
+ })
353
468
  }
354
469
  if (option.variable) {
355
470
  const redundant = generateVariableRedundant(args)
356
471
 
357
- rules = {
358
- ...rules,
359
- VariableDeclarator: redundant,
360
- TSEnumDeclaration: redundant,
361
- }
472
+ addRule('VariableDeclarator', redundant)
473
+ addRule('TSEnumDeclaration', redundant)
362
474
  }
363
475
  if (option.class) {
364
- rules = {
365
- ...rules,
366
- ClassDeclaration: generateClassRedundant(args),
367
- MethodDefinition: generateMethodRedundant(args) ,
368
- }
476
+ addRule('ClassDeclaration', generateClassRedundant(args))
477
+ }
478
+ if (option.method) {
479
+ addRule('MethodDefinition', generateMethodRedundant(args))
369
480
  }
370
481
 
482
+ Object.keys(rules).forEach((key) => {
483
+ const redundants = rules[key]
484
+ rules[key] = (node) => {
485
+ redundants.forEach((redundant) => {
486
+ redundant(node)
487
+ })
488
+ }
489
+ })
490
+
371
491
  return rules
372
492
  },
373
493
  }
@@ -0,0 +1,116 @@
1
+ const rule = require("../rules/prohibit-import")
2
+ const RuleTester = require("eslint").RuleTester
3
+
4
+ const ruleTester = new RuleTester({
5
+ parserOptions: {
6
+ sourceType: "module",
7
+ ecmaVersion: 2015
8
+ },
9
+ })
10
+
11
+ ruleTester.run("prohibit-import-lodash", rule, {
12
+ valid: [
13
+ {
14
+ code: `import _ from 'lodash-es'`,
15
+ options: [
16
+ {
17
+ '^lodash$': {
18
+ imported: true,
19
+ },
20
+ }
21
+ ]
22
+ },
23
+ {
24
+ code: `import { isEqual } from 'lodash-es'`,
25
+ options: [
26
+ {
27
+ '^lodash$': {
28
+ imported: ['isEqual']
29
+ },
30
+ }
31
+ ]
32
+ },
33
+ {
34
+ code: `import { isEqaul } from 'lodash'`,
35
+ options: [
36
+ {
37
+ '^lodash$': {
38
+ imported: ['isEqual']
39
+ },
40
+ }
41
+ ]
42
+ },
43
+ {
44
+ code: `import _ from 'lodash'`,
45
+ options: [
46
+ {
47
+ '^lodash$': {
48
+ imported: ['isEqual']
49
+ },
50
+ }
51
+ ]
52
+ }
53
+ ],
54
+ invalid: [
55
+ {
56
+ code: `import _ from 'lodash'`,
57
+ options: [
58
+ {
59
+ '^lodash$': {
60
+ imported: true
61
+ },
62
+ }
63
+ ],
64
+ errors: [{ message: 'lodash は利用しないでください' }]
65
+ },
66
+ {
67
+ code: `import { isEqual } from 'lodash'`,
68
+ options: [
69
+ {
70
+ '^lodash$': {
71
+ imported: true
72
+ },
73
+ }
74
+ ],
75
+ errors: [{ message: 'lodash は利用しないでください' }]
76
+ },
77
+ {
78
+ code: `import { isEqual } from 'lodash'`,
79
+ options: [
80
+ {
81
+ '^lodash$': {
82
+ imported: ['isEqual']
83
+ },
84
+ }
85
+ ],
86
+ errors: [{message: 'lodash/isEqual は利用しないでください'}]
87
+ },
88
+ {
89
+ code: `import { isEqual } from 'lodash'`,
90
+ options: [
91
+ {
92
+ '^lodash$': {
93
+ imported: ['isEqual'],
94
+ "reportMessage": "must not use {{module}}/{{export}}"
95
+ },
96
+ }
97
+ ],
98
+ errors: [{message: 'must not use lodash/isEqual'}]
99
+ },
100
+ {
101
+ code: `import { isEqual } from 'lodash'`,
102
+ options: [
103
+ {
104
+ 'example': {
105
+ imported: true,
106
+ },
107
+ '^lodash$': {
108
+ imported: ['isEqual'],
109
+ reportMessage: "must not use {{module}}/{{export}}",
110
+ },
111
+ }
112
+ ],
113
+ errors: [{message: 'must not use lodash/isEqual'}]
114
+ }
115
+ ]
116
+ })