@trigen/oxlint-config 9.3.0 → 10.1.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@trigen/oxlint-config",
3
3
  "type": "module",
4
- "version": "9.3.0",
4
+ "version": "10.1.1",
5
5
  "description": "Trigen's Oxlint config.",
6
6
  "author": "dangreen",
7
7
  "license": "MIT",
@@ -23,7 +23,8 @@
23
23
  "exports": {
24
24
  ".": "./src/index.js",
25
25
  "./plugin": "./src/plugin/index.js",
26
- "./*": "./src/*.js"
26
+ "./*": "./src/*.js",
27
+ "./package.json": "./package.json"
27
28
  },
28
29
  "peerDependencies": {
29
30
  "oxlint": ">=1.0.0",
@@ -0,0 +1,154 @@
1
+ function isFunctionExpression(node) {
2
+ return node?.type === 'ArrowFunctionExpression'
3
+ || node?.type === 'FunctionExpression'
4
+ }
5
+
6
+ function pushComputedKeys(stack, classBody) {
7
+ classBody.body.forEach((member) => {
8
+ if (member.computed) {
9
+ stack.push(member.key)
10
+ }
11
+ })
12
+ }
13
+
14
+ function pushChildren(stack, node) {
15
+ Object.entries(node).forEach(([key, value]) => {
16
+ if (key === 'parent' || typeof value !== 'object' || value === null) {
17
+ return
18
+ }
19
+
20
+ if (Array.isArray(value)) {
21
+ value.forEach(item => stack.push(item))
22
+ } else {
23
+ stack.push(value)
24
+ }
25
+ })
26
+ }
27
+
28
+ /**
29
+ * Check if a function references `this`, `arguments` or `new.target` from its own scope,
30
+ * so it can't be converted to an arrow function.
31
+ * @param {object} functionNode
32
+ * @returns {boolean} Whether the function uses its own bindings.
33
+ */
34
+ function usesFunctionBindings(functionNode) {
35
+ const stack = [...functionNode.params, functionNode.body]
36
+
37
+ while (stack.length > 0) {
38
+ const node = stack.pop()
39
+
40
+ if (!node || typeof node.type !== 'string') {
41
+ continue
42
+ }
43
+
44
+ switch (node.type) {
45
+ case 'ThisExpression':
46
+ return true
47
+ case 'MetaProperty':
48
+ if (node.meta.name === 'new') {
49
+ return true
50
+ }
51
+
52
+ continue
53
+ case 'Identifier':
54
+ if (node.name === 'arguments') {
55
+ return true
56
+ }
57
+
58
+ continue
59
+ // nested regular functions and class members rebind `this` and `arguments`
60
+ case 'FunctionDeclaration':
61
+ case 'FunctionExpression':
62
+ continue
63
+ case 'ClassBody':
64
+ pushComputedKeys(stack, node)
65
+ continue
66
+ case 'MemberExpression':
67
+ stack.push(node.object)
68
+
69
+ if (node.computed) {
70
+ stack.push(node.property)
71
+ }
72
+
73
+ continue
74
+ case 'Property':
75
+ case 'PropertyDefinition':
76
+ case 'MethodDefinition':
77
+ if (node.computed) {
78
+ stack.push(node.key)
79
+ }
80
+
81
+ stack.push(node.value)
82
+ continue
83
+ default:
84
+ pushChildren(stack, node)
85
+ }
86
+ }
87
+
88
+ return false
89
+ }
90
+
91
+ function checkDeclarator(context, declarator) {
92
+ if (
93
+ isFunctionExpression(declarator.init)
94
+ && declarator.id.type === 'Identifier'
95
+ && !declarator.id.typeAnnotation
96
+ ) {
97
+ context.report({
98
+ node: declarator.id,
99
+ message: 'Use a function declaration instead of assigning a function to a constant at the top level.'
100
+ })
101
+ }
102
+ }
103
+
104
+ export default {
105
+ meta: {
106
+ type: 'suggestion',
107
+ docs: {
108
+ description: 'Enforce function declarations at the top level and arrow functions assigned to constants inside functions.'
109
+ },
110
+ schema: []
111
+ },
112
+ create(context) {
113
+ let depth = 0
114
+
115
+ return {
116
+ FunctionDeclaration(node) {
117
+ if (depth > 0 && !node.generator && !usesFunctionBindings(node)) {
118
+ context.report({
119
+ node: node.id ?? node,
120
+ message: 'Use an arrow function assigned to a constant instead of a nested function declaration.'
121
+ })
122
+ }
123
+
124
+ depth++
125
+ },
126
+ 'FunctionDeclaration:exit'() {
127
+ depth--
128
+ },
129
+ FunctionExpression() {
130
+ depth++
131
+ },
132
+ 'FunctionExpression:exit'() {
133
+ depth--
134
+ },
135
+ ArrowFunctionExpression() {
136
+ depth++
137
+ },
138
+ 'ArrowFunctionExpression:exit'() {
139
+ depth--
140
+ },
141
+ StaticBlock() {
142
+ depth++
143
+ },
144
+ 'StaticBlock:exit'() {
145
+ depth--
146
+ },
147
+ VariableDeclaration(node) {
148
+ if (depth === 0 && node.kind === 'const') {
149
+ node.declarations.forEach(declarator => checkDeclarator(context, declarator))
150
+ }
151
+ }
152
+ }
153
+ }
154
+ }
@@ -1,4 +1,5 @@
1
1
  import extensionsRule from './extensions.js'
2
+ import funcStyleRule from './func-style.js'
2
3
  import importOrderRule from './import-order.js'
3
4
  import memberOrderingRule from './member-ordering.js'
4
5
  import namedExportOrderRule from './named-export-order.js'
@@ -12,6 +13,7 @@ export default {
12
13
  },
13
14
  rules: {
14
15
  'extensions': extensionsRule,
16
+ 'func-style': funcStyleRule,
15
17
  'import-order': importOrderRule,
16
18
  'member-ordering': memberOrderingRule,
17
19
  'named-export-order': namedExportOrderRule,
@@ -13,11 +13,15 @@ const defaultOrder = [
13
13
  'private-instance-field',
14
14
  'public-abstract-field',
15
15
  'protected-abstract-field',
16
+ 'field',
16
17
  'signature',
18
+ 'call-signature',
17
19
  'public-constructor',
18
20
  'protected-constructor',
19
21
  'private-constructor',
20
- 'instance-method'
22
+ 'constructor',
23
+ 'instance-method',
24
+ 'method'
21
25
  ]
22
26
 
23
27
  function getDefaultOptions(context) {
@@ -93,10 +97,43 @@ function getMemberType(member) {
93
97
  return null
94
98
  }
95
99
 
96
- function getRank(memberType, memberTypes) {
97
- const rank = memberTypes.indexOf(memberType)
100
+ function getClassMemberTypes(member) {
101
+ const memberType = getMemberType(member)
98
102
 
99
- return rank === -1 ? Number.POSITIVE_INFINITY : rank
103
+ return memberType === null ? null : [memberType]
104
+ }
105
+
106
+ function getSignatureMemberTypes(member) {
107
+ switch (member.type) {
108
+ case 'TSPropertySignature':
109
+ return member.readonly
110
+ ? ['readonly-field', 'field']
111
+ : ['field']
112
+ case 'TSMethodSignature':
113
+ return ['method']
114
+ case 'TSIndexSignature':
115
+ return member.readonly
116
+ ? ['readonly-signature', 'signature']
117
+ : ['signature']
118
+ case 'TSConstructSignatureDeclaration':
119
+ return ['constructor']
120
+ case 'TSCallSignatureDeclaration':
121
+ return ['call-signature']
122
+ default:
123
+ return null
124
+ }
125
+ }
126
+
127
+ function getRank(candidateTypes, memberTypes) {
128
+ for (const candidateType of candidateTypes) {
129
+ const rank = memberTypes.indexOf(candidateType)
130
+
131
+ if (rank !== -1) {
132
+ return rank
133
+ }
134
+ }
135
+
136
+ return -1
100
137
  }
101
138
 
102
139
  function getMemberName(member) {
@@ -125,11 +162,43 @@ function getMemberName(member) {
125
162
  return member.type
126
163
  }
127
164
 
165
+ function checkMembers(context, memberTypes, members, getCandidateTypes) {
166
+ let previousMember = null
167
+
168
+ for (const member of members) {
169
+ const candidateTypes = getCandidateTypes(member)
170
+
171
+ if (candidateTypes === null) {
172
+ continue
173
+ }
174
+
175
+ const rank = getRank(candidateTypes, memberTypes)
176
+
177
+ if (rank === -1) {
178
+ continue
179
+ }
180
+
181
+ if (previousMember && previousMember.rank > rank) {
182
+ context.report({
183
+ node: member,
184
+ message: `Member "${getMemberName(member)}" should be declared before "${getMemberName(previousMember.node)}".`
185
+ })
186
+
187
+ return
188
+ }
189
+
190
+ previousMember = {
191
+ node: member,
192
+ rank
193
+ }
194
+ }
195
+ }
196
+
128
197
  export default {
129
198
  meta: {
130
199
  type: 'suggestion',
131
200
  docs: {
132
- description: 'Enforce configured class member ordering.'
201
+ description: 'Enforce configured class, interface and type literal member ordering.'
133
202
  },
134
203
  schema: [
135
204
  {
@@ -160,31 +229,13 @@ export default {
160
229
 
161
230
  return {
162
231
  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
- }
232
+ checkMembers(context, memberTypes, node.body, getClassMemberTypes)
233
+ },
234
+ TSInterfaceBody(node) {
235
+ checkMembers(context, memberTypes, node.body, getSignatureMemberTypes)
236
+ },
237
+ TSTypeLiteral(node) {
238
+ checkMembers(context, memberTypes, node.members, getSignatureMemberTypes)
188
239
  }
189
240
  }
190
241
  }
@@ -224,8 +224,7 @@ export default {
224
224
  },
225
225
  create(context) {
226
226
  const options = context.options
227
-
228
- function check(name, node, selector, modifiers = []) {
227
+ const check = (name, node, selector, modifiers = []) => {
229
228
  const option = getSelectorOptions(options, selector, modifiers)
230
229
 
231
230
  if (!option || isValidName(name, option)) {
@@ -237,14 +236,12 @@ export default {
237
236
  message: `Name "${name}" must match one of these formats: ${getExpectedFormats(option.format)}.`
238
237
  })
239
238
  }
240
-
241
- function checkPattern(pattern, selector) {
239
+ const checkPattern = (pattern, selector) => {
242
240
  for (const item of getIdentifierNames(pattern)) {
243
241
  check(item.name, item.node, selector)
244
242
  }
245
243
  }
246
-
247
- function checkProperty(node, selector) {
244
+ const checkProperty = (node, selector) => {
248
245
  const name = getKeyName(node.key)
249
246
 
250
247
  if (name === null) {
@@ -223,13 +223,6 @@ export default {
223
223
  'eslint/prefer-template': 'error',
224
224
  'eslint/symbol-description': 'error',
225
225
  'eslint/func-names': 'error',
226
- 'eslint/func-style': [
227
- 'error',
228
- 'declaration',
229
- {
230
- allowArrowFunctions: true
231
- }
232
- ],
233
226
  'eslint/max-nested-callbacks': ['error', 4],
234
227
  'eslint/max-params': ['error', 6],
235
228
  'eslint/new-cap': [
@@ -245,6 +238,7 @@ export default {
245
238
  'eslint/operator-assignment': ['error', 'always'],
246
239
  'eslint/prefer-object-spread': 'error',
247
240
  'eslint/unicode-bom': 'error',
241
+ 'trigen/func-style': 'error',
248
242
  'trigen/naming-convention': [
249
243
  'error',
250
244
  {
@@ -103,11 +103,15 @@ export default {
103
103
  'private-instance-field',
104
104
  'public-abstract-field',
105
105
  'protected-abstract-field',
106
+ 'field',
106
107
  'signature',
108
+ 'call-signature',
107
109
  'public-constructor',
108
110
  'protected-constructor',
109
111
  'private-constructor',
110
- 'instance-method'
112
+ 'constructor',
113
+ 'instance-method',
114
+ 'method'
111
115
  ]
112
116
  }
113
117
  }