eslint-plugin-perfectionist 3.2.0 → 3.4.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.
Files changed (46) hide show
  1. package/dist/index.d.ts +242 -21
  2. package/dist/index.js +3 -1
  3. package/dist/index.mjs +2 -0
  4. package/dist/rules/sort-array-includes.js +108 -107
  5. package/dist/rules/sort-array-includes.mjs +100 -100
  6. package/dist/rules/sort-astro-attributes.js +6 -0
  7. package/dist/rules/sort-astro-attributes.mjs +6 -0
  8. package/dist/rules/sort-classes-utils.js +102 -0
  9. package/dist/rules/sort-classes-utils.mjs +105 -1
  10. package/dist/rules/sort-classes.js +263 -83
  11. package/dist/rules/sort-classes.mjs +263 -71
  12. package/dist/rules/sort-classes.types.js +71 -0
  13. package/dist/rules/sort-classes.types.mjs +71 -0
  14. package/dist/rules/sort-enums.js +41 -14
  15. package/dist/rules/sort-enums.mjs +41 -10
  16. package/dist/rules/sort-imports.js +28 -0
  17. package/dist/rules/sort-imports.mjs +28 -0
  18. package/dist/rules/sort-interfaces.js +6 -0
  19. package/dist/rules/sort-interfaces.mjs +6 -0
  20. package/dist/rules/sort-intersection-types.js +20 -0
  21. package/dist/rules/sort-intersection-types.mjs +20 -0
  22. package/dist/rules/sort-jsx-props.js +6 -0
  23. package/dist/rules/sort-jsx-props.mjs +6 -0
  24. package/dist/rules/sort-object-types.js +6 -0
  25. package/dist/rules/sort-object-types.mjs +6 -0
  26. package/dist/rules/sort-objects.js +34 -43
  27. package/dist/rules/sort-objects.mjs +28 -30
  28. package/dist/rules/sort-sets.js +48 -0
  29. package/dist/rules/sort-sets.mjs +47 -0
  30. package/dist/rules/sort-svelte-attributes.js +6 -0
  31. package/dist/rules/sort-svelte-attributes.mjs +6 -0
  32. package/dist/rules/sort-switch-case.js +6 -3
  33. package/dist/rules/sort-switch-case.mjs +6 -3
  34. package/dist/rules/sort-union-types.js +20 -0
  35. package/dist/rules/sort-union-types.mjs +20 -0
  36. package/dist/rules/sort-variable-declarations.js +8 -9
  37. package/dist/rules/sort-variable-declarations.mjs +6 -5
  38. package/dist/rules/sort-vue-attributes.js +6 -0
  39. package/dist/rules/sort-vue-attributes.mjs +6 -0
  40. package/dist/utils/compare.js +0 -6
  41. package/dist/utils/compare.mjs +0 -6
  42. package/dist/utils/sort-nodes-by-dependencies.js +30 -0
  43. package/dist/utils/sort-nodes-by-dependencies.mjs +28 -0
  44. package/dist/utils/validate-groups-configuration.js +19 -0
  45. package/dist/utils/validate-groups-configuration.mjs +17 -0
  46. package/package.json +4 -4
@@ -1,20 +1,20 @@
1
1
  'use strict'
2
+ const sortClassesUtils = require('./sort-classes-utils.js')
3
+ const sortClasses_types = require('./sort-classes.types.js')
4
+ const sortNodesByDependencies = require('../utils/sort-nodes-by-dependencies.js')
2
5
  const isPartitionComment = require('../utils/is-partition-comment.js')
3
6
  const getCommentBefore = require('../utils/get-comment-before.js')
4
7
  const createEslintRule = require('../utils/create-eslint-rule.js')
5
- const sortClassesUtils = require('./sort-classes-utils.js')
6
8
  const getGroupNumber = require('../utils/get-group-number.js')
7
9
  const getSourceCode = require('../utils/get-source-code.js')
8
10
  const toSingleLine = require('../utils/to-single-line.js')
9
11
  const rangeToDiff = require('../utils/range-to-diff.js')
10
12
  const getSettings = require('../utils/get-settings.js')
11
- const isPositive = require('../utils/is-positive.js')
12
13
  const useGroups = require('../utils/use-groups.js')
13
14
  const sortNodes = require('../utils/sort-nodes.js')
14
15
  const makeFixes = require('../utils/make-fixes.js')
15
16
  const complete = require('../utils/complete.js')
16
17
  const pairwise = require('../utils/pairwise.js')
17
- const compare = require('../utils/compare.js')
18
18
  const sortClasses = createEslintRule.createEslintRule({
19
19
  name: 'sort-classes',
20
20
  meta: {
@@ -80,20 +80,62 @@ const sortClasses = createEslintRule.createEslintRule({
80
80
  },
81
81
  customGroups: {
82
82
  description: 'Specifies custom groups.',
83
- type: 'object',
84
- additionalProperties: {
85
- oneOf: [
86
- {
87
- type: 'string',
83
+ oneOf: [
84
+ {
85
+ type: 'object',
86
+ additionalProperties: {
87
+ oneOf: [
88
+ {
89
+ type: 'string',
90
+ },
91
+ {
92
+ type: 'array',
93
+ items: {
94
+ type: 'string',
95
+ },
96
+ },
97
+ ],
88
98
  },
89
- {
90
- type: 'array',
91
- items: {
92
- type: 'string',
93
- },
99
+ },
100
+ {
101
+ type: 'array',
102
+ items: {
103
+ description: 'Advanced custom groups.',
104
+ oneOf: [
105
+ {
106
+ description: 'Custom group block.',
107
+ type: 'object',
108
+ additionalProperties: false,
109
+ properties: {
110
+ ...sortClasses_types.customGroupNameJsonSchema,
111
+ ...sortClasses_types.customGroupSortJsonSchema,
112
+ anyOf: {
113
+ type: 'array',
114
+ items: {
115
+ description: 'Custom group.',
116
+ type: 'object',
117
+ additionalProperties: false,
118
+ properties: {
119
+ ...sortClasses_types.singleCustomGroupJsonSchema,
120
+ },
121
+ },
122
+ },
123
+ },
124
+ },
125
+ {
126
+ description: 'Custom group.',
127
+ type: 'object',
128
+ additionalProperties: false,
129
+ properties: {
130
+ ...sortClasses_types.customGroupNameJsonSchema,
131
+ ...sortClasses_types.customGroupSortJsonSchema,
132
+ ...sortClasses_types.singleCustomGroupJsonSchema,
133
+ },
134
+ },
135
+ ],
94
136
  },
95
- ],
96
- },
137
+ },
138
+ ],
97
139
  },
98
140
  },
99
141
  additionalProperties: false,
@@ -114,9 +156,10 @@ const sortClasses = createEslintRule.createEslintRule({
114
156
  groups: [
115
157
  'index-signature',
116
158
  'static-property',
117
- 'protected-property',
118
- 'private-property',
119
- 'property',
159
+ 'static-block',
160
+ ['protected-property', 'protected-accessor-property'],
161
+ ['private-property', 'private-accessor-property'],
162
+ ['property', 'accessor-property'],
120
163
  'constructor',
121
164
  'static-method',
122
165
  'protected-method',
@@ -125,20 +168,22 @@ const sortClasses = createEslintRule.createEslintRule({
125
168
  ['get-method', 'set-method'],
126
169
  'unknown',
127
170
  ],
128
- customGroups: {},
171
+ customGroups: [],
129
172
  },
130
173
  ],
131
174
  create: context => ({
132
175
  ClassBody: node => {
176
+ var _a
133
177
  if (node.body.length > 1) {
134
178
  let settings = getSettings.getSettings(context.settings)
135
179
  let options = complete.complete(context.options.at(0), settings, {
136
180
  groups: [
137
181
  'index-signature',
138
182
  'static-property',
139
- 'protected-property',
140
- 'private-property',
141
- 'property',
183
+ 'static-block',
184
+ ['protected-property', 'protected-accessor-property'],
185
+ ['private-property', 'private-accessor-property'],
186
+ ['property', 'accessor-property'],
142
187
  'constructor',
143
188
  'static-method',
144
189
  'protected-method',
@@ -150,19 +195,76 @@ const sortClasses = createEslintRule.createEslintRule({
150
195
  partitionByComment: false,
151
196
  type: 'alphabetical',
152
197
  ignoreCase: true,
153
- customGroups: {},
198
+ customGroups: [],
154
199
  order: 'asc',
155
200
  })
156
201
  let sourceCode = getSourceCode.getSourceCode(context)
157
- let extractDependencies = expression => {
202
+ let className = (_a = node.parent.id) == null ? void 0 : _a.name
203
+ let getDependencyName = (nodeName, isStatic) =>
204
+ `${isStatic ? 'static ' : ''}${nodeName}`
205
+ let classMethodsDependencyNames = new Set(
206
+ node.body
207
+ .map(member => {
208
+ if (
209
+ (member.type === 'MethodDefinition' ||
210
+ member.type === 'TSAbstractMethodDefinition') &&
211
+ 'name' in member.key
212
+ ) {
213
+ return getDependencyName(member.key.name, member.static)
214
+ }
215
+ return null
216
+ })
217
+ .filter(m => m !== null),
218
+ )
219
+ let extractDependencies = (expression, isMemberStatic) => {
158
220
  let dependencies = []
159
221
  let checkNode = nodeValue => {
222
+ if (
223
+ nodeValue.type === 'ArrowFunctionExpression' ||
224
+ nodeValue.type === 'FunctionExpression'
225
+ ) {
226
+ return
227
+ }
160
228
  if (
161
229
  nodeValue.type === 'MemberExpression' &&
162
- nodeValue.object.type === 'ThisExpression' &&
230
+ (nodeValue.object.type === 'ThisExpression' ||
231
+ (nodeValue.object.type === 'Identifier' &&
232
+ nodeValue.object.name === className)) &&
163
233
  nodeValue.property.type === 'Identifier'
164
234
  ) {
165
- dependencies.push(nodeValue.property.name)
235
+ let isStaticDependency =
236
+ isMemberStatic || nodeValue.object.type === 'Identifier'
237
+ let dependencyName = getDependencyName(
238
+ nodeValue.property.name,
239
+ isStaticDependency,
240
+ )
241
+ if (!classMethodsDependencyNames.has(dependencyName)) {
242
+ dependencies.push(dependencyName)
243
+ }
244
+ }
245
+ if (nodeValue.type === 'Property') {
246
+ traverseNode(nodeValue.key)
247
+ traverseNode(nodeValue.value)
248
+ }
249
+ if (nodeValue.type === 'ConditionalExpression') {
250
+ traverseNode(nodeValue.test)
251
+ traverseNode(nodeValue.consequent)
252
+ traverseNode(nodeValue.alternate)
253
+ }
254
+ if (
255
+ 'expression' in nodeValue &&
256
+ typeof nodeValue.expression !== 'boolean'
257
+ ) {
258
+ traverseNode(nodeValue.expression)
259
+ }
260
+ if ('object' in nodeValue) {
261
+ traverseNode(nodeValue.object)
262
+ }
263
+ if ('callee' in nodeValue) {
264
+ traverseNode(nodeValue.callee)
265
+ }
266
+ if ('init' in nodeValue && nodeValue.init) {
267
+ traverseNode(nodeValue.init)
166
268
  }
167
269
  if ('body' in nodeValue && nodeValue.body) {
168
270
  traverseNode(nodeValue.body)
@@ -178,9 +280,18 @@ const sortClasses = createEslintRule.createEslintRule({
178
280
  .filter(currentNode => currentNode !== null)
179
281
  .forEach(traverseNode)
180
282
  }
283
+ if ('argument' in nodeValue && nodeValue.argument) {
284
+ traverseNode(nodeValue.argument)
285
+ }
181
286
  if ('arguments' in nodeValue) {
182
287
  nodeValue.arguments.forEach(traverseNode)
183
288
  }
289
+ if ('declarations' in nodeValue) {
290
+ nodeValue.declarations.forEach(traverseNode)
291
+ }
292
+ if ('properties' in nodeValue) {
293
+ nodeValue.properties.forEach(traverseNode)
294
+ }
184
295
  }
185
296
  let traverseNode = nodeValue => {
186
297
  if (Array.isArray(nodeValue)) {
@@ -192,9 +303,11 @@ const sortClasses = createEslintRule.createEslintRule({
192
303
  traverseNode(expression)
193
304
  return dependencies
194
305
  }
306
+ let overloadSignatureGroups =
307
+ sortClassesUtils.getOverloadSignatureGroups(node.body)
195
308
  let formattedNodes = node.body.reduce(
196
309
  (accumulator, member) => {
197
- var _a, _b, _c
310
+ var _a2, _b, _c, _d
198
311
  let comment = getCommentBefore.getCommentBefore(member, sourceCode)
199
312
  if (
200
313
  options.partitionByComment &&
@@ -215,9 +328,9 @@ const sortClasses = createEslintRule.createEslintRule({
215
328
  } else if (member.type === 'TSIndexSignature') {
216
329
  name = sourceCode.text.slice(
217
330
  member.range.at(0),
218
- ((_a = member.typeAnnotation) == null
331
+ ((_a2 = member.typeAnnotation) == null
219
332
  ? void 0
220
- : _a.range.at(0)) ?? member.range.at(1),
333
+ : _a2.range.at(0)) ?? member.range.at(1),
221
334
  )
222
335
  } else {
223
336
  if (member.key.type === 'Identifier') {
@@ -226,9 +339,23 @@ const sortClasses = createEslintRule.createEslintRule({
226
339
  name = sourceCode.text.slice(...member.key.range)
227
340
  }
228
341
  }
229
- let isPrivateName = name.startsWith('#')
230
- let decorated =
231
- 'decorators' in member && member.decorators.length > 0
342
+ let isPrivateHash =
343
+ 'key' in member && member.key.type === 'PrivateIdentifier'
344
+ let decorated = false
345
+ let decorators = []
346
+ if ('decorators' in member) {
347
+ decorated = member.decorators.length > 0
348
+ for (let decorator of member.decorators) {
349
+ if (decorator.expression.type === 'Identifier') {
350
+ decorators.push(decorator.expression.name)
351
+ } else if (
352
+ decorator.expression.type === 'CallExpression' &&
353
+ decorator.expression.callee.type === 'Identifier'
354
+ ) {
355
+ decorators.push(decorator.expression.callee.name)
356
+ }
357
+ }
358
+ }
232
359
  let modifiers = []
233
360
  let selectors = []
234
361
  if (
@@ -249,11 +376,14 @@ const sortClasses = createEslintRule.createEslintRule({
249
376
  }
250
377
  if (member.accessibility === 'protected') {
251
378
  modifiers.push('protected')
252
- } else if (member.accessibility === 'private' || isPrivateName) {
379
+ } else if (member.accessibility === 'private' || isPrivateHash) {
253
380
  modifiers.push('private')
254
381
  } else {
255
382
  modifiers.push('public')
256
383
  }
384
+ if (member.optional) {
385
+ modifiers.push('optional')
386
+ }
257
387
  if (member.kind === 'constructor') {
258
388
  selectors.push('constructor')
259
389
  }
@@ -274,6 +404,7 @@ const sortClasses = createEslintRule.createEslintRule({
274
404
  selectors.push('index-signature')
275
405
  } else if (member.type === 'StaticBlock') {
276
406
  selectors.push('static-block')
407
+ dependencies = extractDependencies(member, true)
277
408
  } else if (
278
409
  member.type === 'AccessorProperty' ||
279
410
  member.type === 'TSAbstractAccessorProperty'
@@ -292,7 +423,7 @@ const sortClasses = createEslintRule.createEslintRule({
292
423
  }
293
424
  if (member.accessibility === 'protected') {
294
425
  modifiers.push('protected')
295
- } else if (member.accessibility === 'private' || isPrivateName) {
426
+ } else if (member.accessibility === 'private' || isPrivateHash) {
296
427
  modifiers.push('private')
297
428
  } else {
298
429
  modifiers.push('public')
@@ -319,20 +450,30 @@ const sortClasses = createEslintRule.createEslintRule({
319
450
  }
320
451
  if (member.accessibility === 'protected') {
321
452
  modifiers.push('protected')
322
- } else if (member.accessibility === 'private' || isPrivateName) {
453
+ } else if (member.accessibility === 'private' || isPrivateHash) {
323
454
  modifiers.push('private')
324
455
  } else {
325
456
  modifiers.push('public')
326
457
  }
327
- if (
458
+ if (member.optional) {
459
+ modifiers.push('optional')
460
+ }
461
+ let isFunctionProperty =
328
462
  ((_b = member.value) == null ? void 0 : _b.type) ===
329
463
  'ArrowFunctionExpression' ||
330
464
  ((_c = member.value) == null ? void 0 : _c.type) ===
331
465
  'FunctionExpression'
332
- ) {
466
+ if (isFunctionProperty) {
333
467
  selectors.push('function-property')
334
468
  }
335
469
  selectors.push('property')
470
+ if (
471
+ member.type === 'PropertyDefinition' &&
472
+ member.value &&
473
+ !isFunctionProperty
474
+ ) {
475
+ dependencies = extractDependencies(member.value, member.static)
476
+ }
336
477
  }
337
478
  for (let officialGroup of sortClassesUtils.generateOfficialGroups(
338
479
  modifiers,
@@ -340,18 +481,46 @@ const sortClasses = createEslintRule.createEslintRule({
340
481
  )) {
341
482
  defineGroup(officialGroup)
342
483
  }
343
- setCustomGroups(options.customGroups, name, {
344
- override: true,
345
- })
346
- if (member.type === 'PropertyDefinition' && member.value) {
347
- dependencies = extractDependencies(member.value)
484
+ if (Array.isArray(options.customGroups)) {
485
+ for (let customGroup of options.customGroups) {
486
+ if (
487
+ sortClassesUtils.customGroupMatches({
488
+ customGroup,
489
+ elementName: name,
490
+ modifiers,
491
+ selectors,
492
+ decorators,
493
+ })
494
+ ) {
495
+ defineGroup(customGroup.groupName, true)
496
+ if (getGroup() === customGroup.groupName) {
497
+ break
498
+ }
499
+ }
500
+ }
501
+ } else {
502
+ setCustomGroups(options.customGroups, name, {
503
+ override: true,
504
+ })
348
505
  }
506
+ let overloadSignatureGroupMember =
507
+ (_d = overloadSignatureGroups.find(overloadSignatures =>
508
+ overloadSignatures.includes(member),
509
+ )) == null
510
+ ? void 0
511
+ : _d.at(-1)
349
512
  let value = {
350
- size: rangeToDiff.rangeToDiff(member.range),
513
+ size: overloadSignatureGroupMember
514
+ ? rangeToDiff.rangeToDiff(overloadSignatureGroupMember.range)
515
+ : rangeToDiff.rangeToDiff(member.range),
351
516
  group: getGroup(),
352
517
  node: member,
353
518
  dependencies,
354
519
  name,
520
+ dependencyName: getDependencyName(
521
+ name,
522
+ modifiers.includes('static'),
523
+ ),
355
524
  }
356
525
  accumulator.at(-1).push(value)
357
526
  return accumulator
@@ -359,15 +528,56 @@ const sortClasses = createEslintRule.createEslintRule({
359
528
  [[]],
360
529
  )
361
530
  for (let nodes of formattedNodes) {
531
+ let nodesByNonIgnoredGroupNumber = {}
532
+ let ignoredNodeIndices = []
533
+ for (let [index, sortingNode] of nodes.entries()) {
534
+ let groupNum = getGroupNumber.getGroupNumber(
535
+ options.groups,
536
+ sortingNode,
537
+ )
538
+ if (groupNum === options.groups.length) {
539
+ ignoredNodeIndices.push(index)
540
+ continue
541
+ }
542
+ nodesByNonIgnoredGroupNumber[groupNum] =
543
+ nodesByNonIgnoredGroupNumber[groupNum] ?? []
544
+ nodesByNonIgnoredGroupNumber[groupNum].push(sortingNode)
545
+ }
546
+ let sortedNodes = []
547
+ for (let groupNumber of Object.keys(
548
+ nodesByNonIgnoredGroupNumber,
549
+ ).sort((a, b) => Number(a) - Number(b))) {
550
+ let compareOptions = sortClassesUtils.getCompareOptions(
551
+ options,
552
+ Number(groupNumber),
553
+ )
554
+ if (!compareOptions) {
555
+ sortedNodes.push(
556
+ ...nodesByNonIgnoredGroupNumber[Number(groupNumber)],
557
+ )
558
+ } else {
559
+ sortedNodes.push(
560
+ ...sortNodes.sortNodes(
561
+ nodesByNonIgnoredGroupNumber[Number(groupNumber)],
562
+ compareOptions,
563
+ ),
564
+ )
565
+ }
566
+ }
567
+ for (let ignoredIndex of ignoredNodeIndices) {
568
+ sortedNodes.splice(ignoredIndex, 0, nodes[ignoredIndex])
569
+ }
570
+ sortedNodes =
571
+ sortNodesByDependencies.sortNodesByDependencies(sortedNodes)
362
572
  pairwise.pairwise(nodes, (left, right) => {
363
573
  let leftNum = getGroupNumber.getGroupNumber(options.groups, left)
364
574
  let rightNum = getGroupNumber.getGroupNumber(options.groups, right)
365
- if (
366
- left.name !== right.name &&
367
- (leftNum > rightNum ||
368
- (leftNum === rightNum &&
369
- isPositive.isPositive(compare.compare(left, right, options))))
370
- ) {
575
+ let isLeftOrRightIgnored =
576
+ leftNum === options.groups.length ||
577
+ rightNum === options.groups.length
578
+ let indexOfLeft = sortedNodes.indexOf(left)
579
+ let indexOfRight = sortedNodes.indexOf(right)
580
+ if (!isLeftOrRightIgnored && indexOfLeft > indexOfRight) {
371
581
  context.report({
372
582
  messageId:
373
583
  leftNum !== rightNum
@@ -380,40 +590,10 @@ const sortClasses = createEslintRule.createEslintRule({
380
590
  rightGroup: right.group,
381
591
  },
382
592
  node: right.node,
383
- fix: fixer => {
384
- let grouped = nodes.reduce((accumulator, sortingNode) => {
385
- let groupNum = getGroupNumber.getGroupNumber(
386
- options.groups,
387
- sortingNode,
388
- )
389
- if (!(groupNum in accumulator)) {
390
- accumulator[groupNum] = [sortingNode]
391
- } else {
392
- accumulator[groupNum] = sortNodes.sortNodes(
393
- [...accumulator[groupNum], sortingNode],
394
- options,
395
- )
396
- }
397
- return accumulator
398
- }, {})
399
- let sortedNodes = []
400
- for (let group of Object.keys(grouped).sort(
401
- (a, b) => Number(a) - Number(b),
402
- )) {
403
- sortedNodes.push(
404
- ...sortNodes.sortNodes(grouped[group], options),
405
- )
406
- }
407
- return makeFixes.makeFixes(
408
- fixer,
409
- nodes,
410
- sortedNodes,
411
- sourceCode,
412
- {
413
- partitionComment: options.partitionByComment,
414
- },
415
- )
416
- },
593
+ fix: fixer =>
594
+ makeFixes.makeFixes(fixer, nodes, sortedNodes, sourceCode, {
595
+ partitionComment: options.partitionByComment,
596
+ }),
417
597
  })
418
598
  }
419
599
  })