@@ -28,6 +28,7 @@ import { sortNodesByDependencies } from '../utils/sort-nodes-by-dependencies'
28
28
import { getEslintDisabledLines } from '../utils/get-eslint-disabled-lines'
29
29
import { isNodeEslintDisabled } from '../utils/is-node-eslint-disabled'
30
30
import { doesCustomGroupMatch } from '../utils/does-custom-group-match'
31
+ import { UnreachableCaseError } from '../utils/unreachable-case-error'
31
32
import { sortNodesByGroups } from '../utils/sort-nodes-by-groups'
32
33
import { singleCustomGroupJsonSchema } from './sort-enums/types'
33
34
import { createEslintRule } from '../utils/create-eslint-rule'
@@ -55,13 +56,12 @@ interface SortEnumsSortingNode
55
56
56
57
let defaultOptions : Required < Options [ number ] > = {
57
58
fallbackSort : { type : 'unsorted' } ,
59
+ sortByValue : 'ifNumericEnum' ,
58
60
partitionByComment : false ,
59
61
partitionByNewLine : false ,
60
62
specialCharacters : 'keep' ,
61
63
newlinesBetween : 'ignore' ,
62
- forceNumericSort : false ,
63
64
type : 'alphabetical' ,
64
- sortByValue : false ,
65
65
ignoreCase : true ,
66
66
locales : 'en-US' ,
67
67
customGroups : [ ] ,
@@ -202,9 +202,7 @@ export default createEslintRule<Options, MessageId>({
202
202
let nodes = formattedMembers . flat ( )
203
203
204
204
let isNumericEnum = nodes . every (
205
- sortingNode =>
206
- sortingNode . numericValue !== null &&
207
- ! Number . isNaN ( sortingNode . numericValue ) ,
205
+ sortingNode => sortingNode . numericValue !== null ,
208
206
)
209
207
210
208
let nodeValueGetter = computeNodeValueGetter ( {
@@ -262,14 +260,10 @@ export default createEslintRule<Options, MessageId>({
262
260
{
263
261
properties : {
264
262
...commonJsonSchemas ,
265
- forceNumericSort : {
266
- description :
267
- 'Will always sort numeric enums by their value regardless of the sort type specified.' ,
268
- type : 'boolean' ,
269
- } ,
270
263
sortByValue : {
271
- description : 'Compare enum values instead of names.' ,
272
- type : 'boolean' ,
264
+ description : 'Specifies whether to sort enums by value.' ,
265
+ enum : [ 'always' , 'ifNumericEnum' , 'never' ] ,
266
+ type : 'string' ,
273
267
} ,
274
268
customGroups : buildCustomGroupsArrayJsonSchema ( {
275
269
singleCustomGroupJsonSchema,
@@ -306,9 +300,12 @@ function getBinaryExpressionNumberValue(
306
300
leftExpression : TSESTree . PrivateIdentifier | TSESTree . Expression ,
307
301
rightExpression : TSESTree . Expression ,
308
302
operator : string ,
309
- ) : number {
303
+ ) : number | null {
310
304
let left = getExpressionNumberValue ( leftExpression )
311
305
let right = getExpressionNumberValue ( rightExpression )
306
+ if ( left === null || right === null ) {
307
+ return null
308
+ }
312
309
switch ( operator ) {
313
310
case '**' :
314
311
return left ** right
@@ -334,11 +331,40 @@ function getBinaryExpressionNumberValue(
334
331
return left ^ right
335
332
/* v8 ignore next 2 - Unsure if we can reach it */
336
333
default :
337
- return Number . NaN
334
+ return null
335
+ }
336
+ }
337
+
338
+ function computeNodeValueGetter ( {
339
+ isNumericEnum,
340
+ options,
341
+ } : {
342
+ options : Pick < Required < Options [ number ] > , 'sortByValue' >
343
+ isNumericEnum : boolean
344
+ } ) : NodeValueGetterFunction < SortEnumsSortingNode > | null {
345
+ switch ( options . sortByValue ) {
346
+ case 'ifNumericEnum' :
347
+ if ( ! isNumericEnum ) {
348
+ return null
349
+ }
350
+ break
351
+ case 'always' :
352
+ break
353
+ case 'never' :
354
+ return null
355
+ /* v8 ignore next 2 */
356
+ default :
357
+ throw new UnreachableCaseError ( options . sortByValue )
358
+ }
359
+ return sortingNode => {
360
+ if ( isNumericEnum ) {
361
+ return sortingNode . numericValue ! . toString ( )
362
+ }
363
+ return sortingNode . value ?? ''
338
364
}
339
365
}
340
366
341
- function getExpressionNumberValue ( expression : TSESTree . Node ) : number {
367
+ function getExpressionNumberValue ( expression : TSESTree . Node ) : number | null {
342
368
switch ( expression . type ) {
343
369
case 'BinaryExpression' :
344
370
return getBinaryExpressionNumberValue (
@@ -352,55 +378,20 @@ function getExpressionNumberValue(expression: TSESTree.Node): number {
352
378
expression . operator ,
353
379
)
354
380
case 'Literal' :
355
- return typeof expression . value === 'number'
356
- ? expression . value
357
- : Number . NaN
381
+ return typeof expression . value === 'number' ? expression . value : null
358
382
default :
359
- return Number . NaN
383
+ return null
360
384
}
361
385
}
362
386
363
- function computeNodeValueGetter ( {
364
- isNumericEnum,
365
- options,
366
- } : {
367
- options : Pick < Required < Options [ number ] > , 'forceNumericSort' | 'sortByValue' >
368
- isNumericEnum : boolean
369
- } ) : NodeValueGetterFunction < SortEnumsSortingNode > | null {
370
- return options . sortByValue || ( isNumericEnum && options . forceNumericSort )
371
- ? sortingNode => {
372
- if ( isNumericEnum ) {
373
- return sortingNode . numericValue ! . toString ( )
374
- }
375
- return sortingNode . value ?? ''
376
- }
377
- : null
378
- }
379
-
380
- function computeOptionType ( {
381
- isNumericEnum,
382
- options,
383
- } : {
384
- options : Pick <
385
- Required < Options [ number ] > ,
386
- 'forceNumericSort' | 'sortByValue' | 'type'
387
- >
388
- isNumericEnum : boolean
389
- } ) : TypeOption {
390
- /**
391
- * If the enum is numeric, and we sort by value, always use the `natural` sort
392
- * type, which will correctly sort them.
393
- */
394
- return isNumericEnum && ( options . forceNumericSort || options . sortByValue )
395
- ? 'natural'
396
- : options . type
397
- }
398
-
399
387
function getUnaryExpressionNumberValue (
400
388
argumentExpression : TSESTree . Expression ,
401
389
operator : string ,
402
- ) : number {
390
+ ) : number | null {
403
391
let argument = getExpressionNumberValue ( argumentExpression )
392
+ if ( argument === null ) {
393
+ return null
394
+ }
404
395
switch ( operator ) {
405
396
case '+' :
406
397
return argument
@@ -410,6 +401,26 @@ function getUnaryExpressionNumberValue(
410
401
return ~ argument
411
402
/* v8 ignore next 2 - Unsure if we can reach it */
412
403
default :
413
- return Number . NaN
404
+ return null
405
+ }
406
+ }
407
+
408
+ function computeOptionType ( {
409
+ isNumericEnum,
410
+ options,
411
+ } : {
412
+ options : Pick < Required < Options [ number ] > , 'sortByValue' | 'type' >
413
+ isNumericEnum : boolean
414
+ } ) : TypeOption {
415
+ /**
416
+ * If the enum is numeric, and we sort by value, always use the `natural` sort
417
+ * type, which will correctly sort them.
418
+ */
419
+ if ( ! isNumericEnum ) {
420
+ return options . type
421
+ }
422
+ if ( options . sortByValue === 'never' ) {
423
+ return options . type
414
424
}
425
+ return 'natural'
415
426
}
0 commit comments