@trigen/oxlint-config 9.0.0
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/LICENSE +21 -0
- package/README.md +81 -0
- package/oxlint.config.ts +15 -0
- package/package.json +40 -0
- package/src/bundler.js +27 -0
- package/src/commonjs.js +13 -0
- package/src/index.js +19 -0
- package/src/module.js +22 -0
- package/src/plugin/import-order.js +357 -0
- package/src/plugin/index.js +16 -0
- package/src/plugin/member-ordering.js +191 -0
- package/src/plugin/named-import-order.js +229 -0
- package/src/plugin/naming-convention.js +300 -0
- package/src/react.js +13 -0
- package/src/storybook.js +26 -0
- package/src/subconfigs/base.stylistic.js +232 -0
- package/src/subconfigs/basic.js +242 -0
- package/src/subconfigs/configs.js +17 -0
- package/src/subconfigs/files.js +23 -0
- package/src/subconfigs/import.js +88 -0
- package/src/subconfigs/jsdoc.js +41 -0
- package/src/subconfigs/react.js +76 -0
- package/src/subconfigs/react.stylistic.js +66 -0
- package/src/subconfigs/typescript-type-checked.js +71 -0
- package/src/subconfigs/typescript.js +175 -0
- package/src/subconfigs/typescript.stylistic.js +37 -0
- package/src/test.js +35 -0
- package/src/typescript-type-checked.js +13 -0
- package/src/typescript.js +13 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
const defaultOrder = [
|
|
2
|
+
'public-static-method',
|
|
3
|
+
'protected-static-method',
|
|
4
|
+
'private-static-method',
|
|
5
|
+
'public-static-field',
|
|
6
|
+
'protected-static-field',
|
|
7
|
+
'private-static-field',
|
|
8
|
+
'public-decorated-field',
|
|
9
|
+
'protected-decorated-field',
|
|
10
|
+
'private-decorated-field',
|
|
11
|
+
'public-instance-field',
|
|
12
|
+
'protected-instance-field',
|
|
13
|
+
'private-instance-field',
|
|
14
|
+
'public-abstract-field',
|
|
15
|
+
'protected-abstract-field',
|
|
16
|
+
'signature',
|
|
17
|
+
'public-constructor',
|
|
18
|
+
'protected-constructor',
|
|
19
|
+
'private-constructor',
|
|
20
|
+
'instance-method'
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
function getDefaultOptions(context) {
|
|
24
|
+
return context.options[0]?.default ?? {}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getMemberTypes(context) {
|
|
28
|
+
return getDefaultOptions(context).memberTypes ?? defaultOrder
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getAccessibility(member) {
|
|
32
|
+
return member.accessibility ?? 'public'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function hasDecorators(member) {
|
|
36
|
+
return (member.decorators?.length ?? 0) > 0
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function isAbstract(member) {
|
|
40
|
+
return member.abstract === true
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function isSignature(member) {
|
|
44
|
+
return member.type.includes('Signature')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isMethod(member) {
|
|
48
|
+
return member.type === 'MethodDefinition'
|
|
49
|
+
|| member.type === 'TSAbstractMethodDefinition'
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function isField(member) {
|
|
53
|
+
return member.type === 'PropertyDefinition'
|
|
54
|
+
|| member.type === 'AccessorProperty'
|
|
55
|
+
|| member.type === 'TSAbstractPropertyDefinition'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getMemberType(member) {
|
|
59
|
+
const accessibility = getAccessibility(member)
|
|
60
|
+
|
|
61
|
+
if (isSignature(member)) {
|
|
62
|
+
return 'signature'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (member.kind === 'constructor') {
|
|
66
|
+
return `${accessibility}-constructor`
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (isMethod(member)) {
|
|
70
|
+
if (member.static) {
|
|
71
|
+
return `${accessibility}-static-method`
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return 'instance-method'
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (isField(member)) {
|
|
78
|
+
if (isAbstract(member)) {
|
|
79
|
+
return `${accessibility}-abstract-field`
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (hasDecorators(member)) {
|
|
83
|
+
return `${accessibility}-decorated-field`
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (member.static) {
|
|
87
|
+
return `${accessibility}-static-field`
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return `${accessibility}-instance-field`
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return null
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function getRank(memberType, memberTypes) {
|
|
97
|
+
const rank = memberTypes.indexOf(memberType)
|
|
98
|
+
|
|
99
|
+
return rank === -1 ? Number.POSITIVE_INFINITY : rank
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function getMemberName(member) {
|
|
103
|
+
if (member.kind === 'constructor') {
|
|
104
|
+
return 'constructor'
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const key = member.key
|
|
108
|
+
|
|
109
|
+
if (!key) {
|
|
110
|
+
return member.type
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (key.type === 'Identifier' || key.type === 'PrivateIdentifier') {
|
|
114
|
+
return key.name
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (
|
|
118
|
+
key.type === 'Literal'
|
|
119
|
+
|| key.type === 'StringLiteral'
|
|
120
|
+
|| key.type === 'NumericLiteral'
|
|
121
|
+
) {
|
|
122
|
+
return String(key.value)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return member.type
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export default {
|
|
129
|
+
meta: {
|
|
130
|
+
type: 'suggestion',
|
|
131
|
+
docs: {
|
|
132
|
+
description: 'Enforce configured class member ordering.'
|
|
133
|
+
},
|
|
134
|
+
schema: [
|
|
135
|
+
{
|
|
136
|
+
type: 'object',
|
|
137
|
+
properties: {
|
|
138
|
+
default: {
|
|
139
|
+
type: 'object',
|
|
140
|
+
properties: {
|
|
141
|
+
order: {
|
|
142
|
+
enum: ['as-written']
|
|
143
|
+
},
|
|
144
|
+
memberTypes: {
|
|
145
|
+
type: 'array',
|
|
146
|
+
items: {
|
|
147
|
+
type: 'string'
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
additionalProperties: true
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
additionalProperties: true
|
|
155
|
+
}
|
|
156
|
+
]
|
|
157
|
+
},
|
|
158
|
+
create(context) {
|
|
159
|
+
const memberTypes = getMemberTypes(context)
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
ClassBody(node) {
|
|
163
|
+
let previousMember = null
|
|
164
|
+
|
|
165
|
+
for (const member of node.body) {
|
|
166
|
+
const memberType = getMemberType(member)
|
|
167
|
+
|
|
168
|
+
if (memberType === null) {
|
|
169
|
+
continue
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const rank = getRank(memberType, memberTypes)
|
|
173
|
+
|
|
174
|
+
if (previousMember && previousMember.rank > rank) {
|
|
175
|
+
context.report({
|
|
176
|
+
node: member,
|
|
177
|
+
message: `Member "${getMemberName(member)}" should be declared before "${getMemberName(previousMember.node)}".`
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
return
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
previousMember = {
|
|
184
|
+
node: member,
|
|
185
|
+
rank
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
const defaultOptions = {
|
|
2
|
+
typeImports: 'none',
|
|
3
|
+
patterns: []
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
function getOptions(context) {
|
|
7
|
+
return {
|
|
8
|
+
...defaultOptions,
|
|
9
|
+
...context.options[0],
|
|
10
|
+
patterns: context.options[0]?.patterns ?? []
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function getName(specifier) {
|
|
15
|
+
const imported = specifier.imported ?? specifier.local
|
|
16
|
+
|
|
17
|
+
if (imported.type === 'Identifier') {
|
|
18
|
+
return imported.name
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return String(imported.value)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function isTypeSpecifier(node, specifier) {
|
|
25
|
+
return node.importKind === 'type'
|
|
26
|
+
|| specifier.importKind === 'type'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getTypeRank(node, specifier, options) {
|
|
30
|
+
if (options.typeImports === 'first') {
|
|
31
|
+
return isTypeSpecifier(node, specifier) ? 0 : 1
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (options.typeImports === 'last') {
|
|
35
|
+
return isTypeSpecifier(node, specifier) ? 1 : 0
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return 0
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function getPatternRank(name, patterns) {
|
|
42
|
+
const rank = patterns.findIndex(pattern => new RegExp(pattern).test(name))
|
|
43
|
+
|
|
44
|
+
return rank === -1 ? Number.POSITIVE_INFINITY : rank
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getRank(node, specifier, options) {
|
|
48
|
+
const name = getName(specifier)
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
name,
|
|
52
|
+
typeRank: getTypeRank(node, specifier, options),
|
|
53
|
+
patternRank: getPatternRank(name, options.patterns)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function compareRanks(left, right) {
|
|
58
|
+
return left.typeRank - right.typeRank
|
|
59
|
+
|| left.patternRank - right.patternRank
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function getItems(node, options) {
|
|
63
|
+
return node.specifiers
|
|
64
|
+
.filter(specifier => specifier.type === 'ImportSpecifier')
|
|
65
|
+
.map((specifier, index) => ({
|
|
66
|
+
index,
|
|
67
|
+
specifier,
|
|
68
|
+
rank: getRank(node, specifier, options)
|
|
69
|
+
}))
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function compareItems(left, right) {
|
|
73
|
+
return compareRanks(left.rank, right.rank)
|
|
74
|
+
|| left.index - right.index
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function getFirstUnorderedPair(items) {
|
|
78
|
+
for (let index = 1; index < items.length; index++) {
|
|
79
|
+
const previousItem = items[index - 1]
|
|
80
|
+
const item = items[index]
|
|
81
|
+
|
|
82
|
+
if (compareItems(previousItem, item) > 0) {
|
|
83
|
+
return [
|
|
84
|
+
previousItem,
|
|
85
|
+
item
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return null
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function getLinebreak(text) {
|
|
94
|
+
return text.includes('\r\n') ? '\r\n' : '\n'
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function getLineIndent(text, index) {
|
|
98
|
+
const lineStart = text.lastIndexOf('\n', index - 1) + 1
|
|
99
|
+
const indentMatch = /^[ \t]*/.exec(text.slice(lineStart, index))
|
|
100
|
+
|
|
101
|
+
return indentMatch[0]
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function getBracesRange(node, items, sourceCode) {
|
|
105
|
+
const text = sourceCode.text
|
|
106
|
+
const firstSpecifier = items[0].specifier
|
|
107
|
+
const lastSpecifier = items.at(-1).specifier
|
|
108
|
+
const openingBrace = text.lastIndexOf('{', firstSpecifier.range[0])
|
|
109
|
+
const closingBrace = text.indexOf('}', lastSpecifier.range[1])
|
|
110
|
+
const searchEnd = node.source?.range?.[0] ?? node.range[1]
|
|
111
|
+
|
|
112
|
+
if (
|
|
113
|
+
openingBrace < node.range[0]
|
|
114
|
+
|| closingBrace === -1
|
|
115
|
+
|| closingBrace > searchEnd
|
|
116
|
+
) {
|
|
117
|
+
return null
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return [
|
|
121
|
+
openingBrace + 1,
|
|
122
|
+
closingBrace
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function hasInnerComments(sourceCode, range) {
|
|
127
|
+
return sourceCode.getAllComments().some(comment => comment.range[0] > range[0]
|
|
128
|
+
&& comment.range[1] < range[1])
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function getFixedText(node, items, options, sourceCode, range) {
|
|
132
|
+
const text = sourceCode.text
|
|
133
|
+
const content = text.slice(range[0], range[1])
|
|
134
|
+
const sortedSpecifiers = [...items]
|
|
135
|
+
.sort(compareItems)
|
|
136
|
+
.map(({ specifier }) => sourceCode.getText(specifier))
|
|
137
|
+
|
|
138
|
+
if (!content.includes('\n')) {
|
|
139
|
+
return ` ${sortedSpecifiers.join(', ')} `
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const linebreak = getLinebreak(text)
|
|
143
|
+
const firstSpecifier = items[0].specifier
|
|
144
|
+
const indent = getLineIndent(text, firstSpecifier.range[0])
|
|
145
|
+
const closingIndent = getLineIndent(text, node.range[0])
|
|
146
|
+
|
|
147
|
+
return `${linebreak}${indent}${sortedSpecifiers.join(`,${linebreak}${indent}`)}${linebreak}${closingIndent}`
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function getMessage(left, right) {
|
|
151
|
+
if (left.rank.typeRank !== right.rank.typeRank) {
|
|
152
|
+
return `Expected ${right.rank.name} to come before ${left.rank.name} because of import kind.`
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (left.rank.patternRank !== right.rank.patternRank) {
|
|
156
|
+
return `Expected ${right.rank.name} to come before ${left.rank.name} because of naming pattern.`
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return `Expected ${right.rank.name} to come before ${left.rank.name}.`
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export default {
|
|
163
|
+
meta: {
|
|
164
|
+
type: 'layout',
|
|
165
|
+
fixable: 'code',
|
|
166
|
+
docs: {
|
|
167
|
+
description: 'Enforce named import specifier order.'
|
|
168
|
+
},
|
|
169
|
+
schema: [
|
|
170
|
+
{
|
|
171
|
+
type: 'object',
|
|
172
|
+
properties: {
|
|
173
|
+
typeImports: {
|
|
174
|
+
enum: [
|
|
175
|
+
'first',
|
|
176
|
+
'last',
|
|
177
|
+
'none'
|
|
178
|
+
]
|
|
179
|
+
},
|
|
180
|
+
patterns: {
|
|
181
|
+
type: 'array',
|
|
182
|
+
items: {
|
|
183
|
+
type: 'string'
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
additionalProperties: true
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
},
|
|
191
|
+
create(context) {
|
|
192
|
+
const options = getOptions(context)
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
ImportDeclaration(node) {
|
|
196
|
+
const items = getItems(node, options)
|
|
197
|
+
|
|
198
|
+
if (items.length < 2) {
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const unorderedPair = getFirstUnorderedPair(items)
|
|
203
|
+
|
|
204
|
+
if (!unorderedPair) {
|
|
205
|
+
return
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const sourceCode = context.sourceCode
|
|
209
|
+
const range = getBracesRange(node, items, sourceCode)
|
|
210
|
+
const fix = range && !hasInnerComments(sourceCode, range)
|
|
211
|
+
? fixer => fixer.replaceTextRange(
|
|
212
|
+
range,
|
|
213
|
+
getFixedText(node, items, options, sourceCode, range)
|
|
214
|
+
)
|
|
215
|
+
: null
|
|
216
|
+
const [
|
|
217
|
+
previousItem,
|
|
218
|
+
item
|
|
219
|
+
] = unorderedPair
|
|
220
|
+
|
|
221
|
+
context.report({
|
|
222
|
+
node: item.specifier,
|
|
223
|
+
message: getMessage(previousItem, item),
|
|
224
|
+
fix
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
const formatPatterns = {
|
|
2
|
+
camelCase: /^[a-z][a-zA-Z0-9]*$/,
|
|
3
|
+
PascalCase: /^[A-Z][a-zA-Z0-9]*$/,
|
|
4
|
+
UPPER_CASE: /^[A-Z][A-Z0-9_]*$/
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function toArray(value) {
|
|
8
|
+
return Array.isArray(value) ? value : [value]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function getSelectorOptions(options, selector, modifiers = []) {
|
|
12
|
+
return options
|
|
13
|
+
.filter((option) => {
|
|
14
|
+
const selectors = toArray(option.selector)
|
|
15
|
+
const optionModifiers = option.modifiers ?? []
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
selectors.includes(selector)
|
|
19
|
+
|| selectors.includes('default')
|
|
20
|
+
)
|
|
21
|
+
&& optionModifiers.every(modifier => modifiers.includes(modifier))
|
|
22
|
+
})
|
|
23
|
+
.sort((left, right) => {
|
|
24
|
+
const leftSelectors = toArray(left.selector)
|
|
25
|
+
const rightSelectors = toArray(right.selector)
|
|
26
|
+
const leftScore = leftSelectors.includes(selector) ? 1 : 0
|
|
27
|
+
const rightScore = rightSelectors.includes(selector) ? 1 : 0
|
|
28
|
+
|
|
29
|
+
return leftScore - rightScore
|
|
30
|
+
|| (left.modifiers?.length ?? 0) - (right.modifiers?.length ?? 0)
|
|
31
|
+
})
|
|
32
|
+
.at(-1)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getNormalizedName(name, option) {
|
|
36
|
+
if (option.leadingUnderscore === 'allow') {
|
|
37
|
+
return name.replace(/^_+/, '')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return name
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function matchesFormat(name, format) {
|
|
44
|
+
return formatPatterns[format]?.test(name) ?? true
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getExpectedFormats(format) {
|
|
48
|
+
return format.join(', ')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isValidName(name, option) {
|
|
52
|
+
if (option?.format === null) {
|
|
53
|
+
return true
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!option?.format) {
|
|
57
|
+
return true
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const normalizedName = getNormalizedName(name, option)
|
|
61
|
+
|
|
62
|
+
return normalizedName === ''
|
|
63
|
+
|| option.format.some(format => matchesFormat(normalizedName, format))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getKeyName(key) {
|
|
67
|
+
if (!key) {
|
|
68
|
+
return null
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (key.type === 'Identifier' || key.type === 'PrivateIdentifier') {
|
|
72
|
+
return key.name
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (
|
|
76
|
+
key.type === 'Literal'
|
|
77
|
+
|| key.type === 'StringLiteral'
|
|
78
|
+
|| key.type === 'NumericLiteral'
|
|
79
|
+
) {
|
|
80
|
+
return String(key.value)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return null
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function isIdentifierName(name) {
|
|
87
|
+
return /^[A-Za-z_$][\w$]*$/.test(name)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function requiresQuotes(node) {
|
|
91
|
+
if (node.computed) {
|
|
92
|
+
return false
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const name = getKeyName(node.key)
|
|
96
|
+
|
|
97
|
+
return name !== null && !isIdentifierName(name)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function getPropertyModifiers(node) {
|
|
101
|
+
const modifiers = []
|
|
102
|
+
|
|
103
|
+
if (node.static) {
|
|
104
|
+
modifiers.push('static')
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (requiresQuotes(node)) {
|
|
108
|
+
modifiers.push('requiresQuotes')
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return modifiers
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function getIdentifierNames(pattern) {
|
|
115
|
+
if (!pattern) {
|
|
116
|
+
return []
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (pattern.type === 'Identifier') {
|
|
120
|
+
return [
|
|
121
|
+
{
|
|
122
|
+
name: pattern.name,
|
|
123
|
+
node: pattern
|
|
124
|
+
}
|
|
125
|
+
]
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (pattern.type === 'RestElement') {
|
|
129
|
+
return getIdentifierNames(pattern.argument)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (pattern.type === 'AssignmentPattern') {
|
|
133
|
+
return getIdentifierNames(pattern.left)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (pattern.type === 'ArrayPattern') {
|
|
137
|
+
return pattern.elements.flatMap(getIdentifierNames)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (pattern.type === 'ObjectPattern') {
|
|
141
|
+
return pattern.properties.flatMap(property => (
|
|
142
|
+
property.type === 'Property'
|
|
143
|
+
? getIdentifierNames(property.value)
|
|
144
|
+
: getIdentifierNames(property.argument)
|
|
145
|
+
))
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return []
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function getParameterNode(parameter) {
|
|
152
|
+
return parameter.type === 'TSParameterProperty'
|
|
153
|
+
? parameter.parameter
|
|
154
|
+
: parameter
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export default {
|
|
158
|
+
meta: {
|
|
159
|
+
type: 'suggestion',
|
|
160
|
+
docs: {
|
|
161
|
+
description: 'Enforce configured naming conventions.'
|
|
162
|
+
},
|
|
163
|
+
schema: {
|
|
164
|
+
type: 'array',
|
|
165
|
+
items: {
|
|
166
|
+
type: 'object',
|
|
167
|
+
properties: {
|
|
168
|
+
selector: {
|
|
169
|
+
anyOf: [
|
|
170
|
+
{
|
|
171
|
+
type: 'string'
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
type: 'array',
|
|
175
|
+
items: {
|
|
176
|
+
type: 'string'
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
]
|
|
180
|
+
},
|
|
181
|
+
format: {
|
|
182
|
+
anyOf: [
|
|
183
|
+
{
|
|
184
|
+
type: 'array',
|
|
185
|
+
items: {
|
|
186
|
+
type: 'string'
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
type: 'null'
|
|
191
|
+
}
|
|
192
|
+
]
|
|
193
|
+
},
|
|
194
|
+
leadingUnderscore: {
|
|
195
|
+
type: 'string'
|
|
196
|
+
},
|
|
197
|
+
modifiers: {
|
|
198
|
+
type: 'array',
|
|
199
|
+
items: {
|
|
200
|
+
type: 'string'
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
required: ['selector'],
|
|
205
|
+
additionalProperties: true
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
create(context) {
|
|
210
|
+
const options = context.options
|
|
211
|
+
|
|
212
|
+
function check(name, node, selector, modifiers = []) {
|
|
213
|
+
const option = getSelectorOptions(options, selector, modifiers)
|
|
214
|
+
|
|
215
|
+
if (!option || isValidName(name, option)) {
|
|
216
|
+
return
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
context.report({
|
|
220
|
+
node,
|
|
221
|
+
message: `Name "${name}" must match one of these formats: ${getExpectedFormats(option.format)}.`
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function checkPattern(pattern, selector) {
|
|
226
|
+
for (const item of getIdentifierNames(pattern)) {
|
|
227
|
+
check(item.name, item.node, selector)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function checkProperty(node, selector) {
|
|
232
|
+
const name = getKeyName(node.key)
|
|
233
|
+
|
|
234
|
+
if (name === null) {
|
|
235
|
+
return
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
check(name, node.key, selector, getPropertyModifiers(node))
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
VariableDeclarator(node) {
|
|
243
|
+
checkPattern(node.id, 'variable')
|
|
244
|
+
},
|
|
245
|
+
FunctionDeclaration(node) {
|
|
246
|
+
if (node.id) {
|
|
247
|
+
check(node.id.name, node.id, 'function')
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
for (const parameter of node.params) {
|
|
251
|
+
checkPattern(getParameterNode(parameter), 'parameter')
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
FunctionExpression(node) {
|
|
255
|
+
if (node.id) {
|
|
256
|
+
check(node.id.name, node.id, 'function')
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
for (const parameter of node.params) {
|
|
260
|
+
checkPattern(getParameterNode(parameter), 'parameter')
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
ArrowFunctionExpression(node) {
|
|
264
|
+
for (const parameter of node.params) {
|
|
265
|
+
checkPattern(getParameterNode(parameter), 'parameter')
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
ClassDeclaration(node) {
|
|
269
|
+
if (node.id) {
|
|
270
|
+
check(node.id.name, node.id, 'typeLike')
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
TSTypeAliasDeclaration(node) {
|
|
274
|
+
check(node.id.name, node.id, 'typeLike')
|
|
275
|
+
},
|
|
276
|
+
TSInterfaceDeclaration(node) {
|
|
277
|
+
check(node.id.name, node.id, 'interface')
|
|
278
|
+
},
|
|
279
|
+
TSEnumDeclaration(node) {
|
|
280
|
+
check(node.id.name, node.id, 'typeLike')
|
|
281
|
+
},
|
|
282
|
+
TSEnumMember(node) {
|
|
283
|
+
const name = getKeyName(node.id)
|
|
284
|
+
|
|
285
|
+
if (name !== null) {
|
|
286
|
+
check(name, node.id, 'enumMember')
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
PropertyDefinition(node) {
|
|
290
|
+
checkProperty(node, 'classProperty')
|
|
291
|
+
},
|
|
292
|
+
MethodDefinition(node) {
|
|
293
|
+
checkProperty(node, 'classProperty')
|
|
294
|
+
},
|
|
295
|
+
Property(node) {
|
|
296
|
+
checkProperty(node, 'objectLiteralProperty')
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|