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 +8 -0
- package/README.md +27 -44
- package/jest.config.js +5 -0
- package/libs/common_domain.js +10 -9
- package/package.json +5 -1
- package/rules/prohibit-import.js +37 -21
- package/rules/redundant-name.js +206 -86
- package/test/prohibit-import.js +116 -0
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
|
-
-
|
|
312
|
-
-
|
|
313
|
-
-
|
|
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,
|
|
368
|
-
file: { ignorekeywords,
|
|
369
|
-
// property: { ignorekeywords
|
|
370
|
-
// function: { ignorekeywords
|
|
371
|
-
// variable: { ignorekeywords
|
|
372
|
-
// class: { ignorekeywords
|
|
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
package/libs/common_domain.js
CHANGED
|
@@ -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.
|
|
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": {
|
package/rules/prohibit-import.js
CHANGED
|
@@ -1,15 +1,30 @@
|
|
|
1
|
-
const SCHEMA = [
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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:
|
|
23
|
+
additionalProperties: true,
|
|
9
24
|
}
|
|
10
25
|
]
|
|
11
26
|
|
|
12
|
-
const
|
|
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
|
|
24
|
-
const
|
|
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
|
-
|
|
30
|
-
if (!node.source.value.match(new RegExp(
|
|
42
|
+
targetModules.forEach((targetModule) => {
|
|
43
|
+
if (!node.source.value.match(new RegExp(targetModule))) {
|
|
31
44
|
return
|
|
32
45
|
}
|
|
33
|
-
|
|
34
|
-
const imported = (
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
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 (
|
|
58
|
+
if (useImported) {
|
|
44
59
|
context.report({
|
|
45
60
|
node,
|
|
46
61
|
messageId: 'prohibit_import',
|
|
47
62
|
data: {
|
|
48
|
-
message:
|
|
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
|
package/rules/redundant-name.js
CHANGED
|
@@ -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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
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
|
|
103
|
-
let
|
|
104
|
-
const
|
|
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
|
-
|
|
107
|
-
hitCount++
|
|
128
|
+
return prev.replace(regex, '')
|
|
108
129
|
}
|
|
109
130
|
|
|
110
|
-
return
|
|
131
|
+
return prev
|
|
111
132
|
}, name)
|
|
112
133
|
|
|
113
|
-
if (name !==
|
|
114
|
-
|
|
134
|
+
if (name !== conciseName) {
|
|
135
|
+
conciseName = conciseName
|
|
115
136
|
.replace(/^_+/, '')
|
|
116
137
|
.replace(/_+$/, '')
|
|
117
138
|
.replace(/_+/, '_')
|
|
139
|
+
let fullRedundant = false
|
|
118
140
|
|
|
119
|
-
if (!
|
|
141
|
+
if (!conciseName) {
|
|
142
|
+
fullRedundant = true
|
|
120
143
|
// HINT: 1keywordで構成されている名称はそのままにする
|
|
121
|
-
|
|
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]/) &&
|
|
126
|
-
|
|
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 (
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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
|
-
|
|
447
|
+
const redundant = generatePropertyRedundant(args)
|
|
335
448
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
Property: propRedundant,
|
|
339
|
-
PropertyDefinition: propRedundant,
|
|
340
|
-
}
|
|
449
|
+
addRule('Property', redundant)
|
|
450
|
+
addRule('PropertyDefinition', redundant)
|
|
341
451
|
}
|
|
342
452
|
if (option.file) {
|
|
343
|
-
|
|
344
|
-
...rules,
|
|
345
|
-
Program: generateFileRedundant(args),
|
|
346
|
-
}
|
|
453
|
+
addRule('Program', generateFileRedundant(args))
|
|
347
454
|
}
|
|
348
455
|
if (option.function) {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
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
|
-
|
|
358
|
-
|
|
359
|
-
VariableDeclarator: redundant,
|
|
360
|
-
TSEnumDeclaration: redundant,
|
|
361
|
-
}
|
|
472
|
+
addRule('VariableDeclarator', redundant)
|
|
473
|
+
addRule('TSEnumDeclaration', redundant)
|
|
362
474
|
}
|
|
363
475
|
if (option.class) {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
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
|
+
})
|