Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(eslint-plugin): sort members alphabetically
- Loading branch information
Showing
3 changed files
with
379 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
199 changes: 199 additions & 0 deletions
199
packages/eslint-plugin/src/rules/sort-interface-members.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
/** | ||
* @fileoverview Forbids unsorted interface members | ||
*/ | ||
|
||
import * as util from '../util'; | ||
import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/typescript-estree'; | ||
|
||
type Options = []; | ||
type MessageIds = 'notSorted'; | ||
|
||
function isPropertySignature( | ||
member: TSESTree.TypeElement, | ||
): member is TSESTree.TSPropertySignature { | ||
return member.type === AST_NODE_TYPES.TSPropertySignature; | ||
} | ||
|
||
function isMethodSignature( | ||
member: TSESTree.TypeElement, | ||
): member is TSESTree.TSMethodSignature { | ||
return member.type === AST_NODE_TYPES.TSMethodSignature; | ||
} | ||
|
||
function isIndexSignature( | ||
member: TSESTree.TypeElement, | ||
): member is TSESTree.TSIndexSignature { | ||
return member.type === AST_NODE_TYPES.TSIndexSignature; | ||
} | ||
|
||
function isConstructSignatureDeclaration( | ||
member: TSESTree.TypeElement, | ||
): member is TSESTree.TSConstructSignatureDeclaration { | ||
return member.type === AST_NODE_TYPES.TSConstructSignatureDeclaration; | ||
} | ||
|
||
function isCallSignatureDeclaration( | ||
member: TSESTree.TypeElement, | ||
): member is TSESTree.TSCallSignatureDeclaration { | ||
return member.type === AST_NODE_TYPES.TSCallSignatureDeclaration; | ||
} | ||
|
||
export default util.createRule<Options, MessageIds>({ | ||
name: 'sort-interface-members', | ||
meta: { | ||
type: 'suggestion', | ||
docs: { | ||
description: 'Forbids unsorted interface members', | ||
category: 'Stylistic Issues', | ||
recommended: false, | ||
}, | ||
schema: [], | ||
messages: { | ||
notSorted: 'The interface members are not sorted alphabetically.', | ||
}, | ||
}, | ||
defaultOptions: [], | ||
create(context) { | ||
return { | ||
TSInterfaceBody(node) { | ||
const members = node.body; | ||
const propertySignatures: TSESTree.TSPropertySignature[] = []; | ||
const methodSignatures: TSESTree.TSMethodSignature[] = []; | ||
const indexSignatures: TSESTree.TSIndexSignature[] = []; | ||
const constructSignatureDeclarations: TSESTree.TSConstructSignatureDeclaration[] = []; | ||
const callSignatureDeclarations: TSESTree.TSCallSignatureDeclaration[] = []; | ||
|
||
// TODO This algorithm assumes an order of TSESTree.TSPropertySignature > TSMethodSignature > TSIndexSignature > TSConstructSignatureDeclaration > TSCallSignatureDeclaration - it is only used to evaluate if it works alongside the member-ordering rule | ||
for (let i = 0; i < members.length; i++) { | ||
if (isPropertySignature(members[i])) { | ||
if ( | ||
methodSignatures.length > 0 || | ||
indexSignatures.length > 0 || | ||
constructSignatureDeclarations.length > 0 || | ||
callSignatureDeclarations.length > 0 | ||
) { | ||
return context.report({ | ||
messageId: 'notSorted', | ||
node: members[i], | ||
}); | ||
} | ||
|
||
propertySignatures.push(members[i] as TSESTree.TSPropertySignature); | ||
} else if (isMethodSignature(members[i])) { | ||
if ( | ||
indexSignatures.length > 0 || | ||
constructSignatureDeclarations.length > 0 || | ||
callSignatureDeclarations.length > 0 | ||
) { | ||
return context.report({ | ||
messageId: 'notSorted', | ||
node: members[i], | ||
}); | ||
} | ||
|
||
methodSignatures.push(members[i] as TSESTree.TSMethodSignature); | ||
} else if (isIndexSignature(members[i])) { | ||
if ( | ||
constructSignatureDeclarations.length > 0 || | ||
callSignatureDeclarations.length > 0 | ||
) { | ||
return context.report({ | ||
messageId: 'notSorted', | ||
node: members[i], | ||
}); | ||
} | ||
|
||
indexSignatures.push(members[i] as TSESTree.TSIndexSignature); | ||
} else if (isConstructSignatureDeclaration(members[i])) { | ||
if (callSignatureDeclarations.length > 0) { | ||
return context.report({ | ||
messageId: 'notSorted', | ||
node: members[i], | ||
}); | ||
} | ||
|
||
constructSignatureDeclarations.push(members[ | ||
i | ||
] as TSESTree.TSConstructSignatureDeclaration); | ||
} else if (isCallSignatureDeclaration(members[i])) { | ||
callSignatureDeclarations.push(members[ | ||
i | ||
] as TSESTree.TSCallSignatureDeclaration); | ||
} | ||
} | ||
|
||
for (let i = 0; i < propertySignatures.length - 1; i++) { | ||
const currentItem = propertySignatures[i].key as TSESTree.Identifier; | ||
const nextItem = propertySignatures[i + 1].key as TSESTree.Identifier; | ||
|
||
if (currentItem.name > nextItem.name) { | ||
return context.report({ | ||
messageId: 'notSorted', | ||
node: currentItem, | ||
}); | ||
} | ||
} | ||
|
||
for (let i = 0; i < methodSignatures.length - 1; i++) { | ||
const currentItem = methodSignatures[i].key as TSESTree.Identifier; | ||
const nextItem = methodSignatures[i + 1].key as TSESTree.Identifier; | ||
|
||
if (currentItem.name > nextItem.name) { | ||
return context.report({ | ||
messageId: 'notSorted', | ||
node: currentItem, | ||
}); | ||
} | ||
} | ||
|
||
for (let i = 0; i < indexSignatures.length - 1; i++) { | ||
const currentItem = indexSignatures[i] | ||
.parameters[0] as TSESTree.Identifier; | ||
const nextItem = indexSignatures[i + 1] | ||
.parameters[0] as TSESTree.Identifier; | ||
|
||
if (currentItem.name > nextItem.name) { | ||
return context.report({ | ||
messageId: 'notSorted', | ||
node: currentItem, | ||
}); | ||
} | ||
} | ||
|
||
for (let i = 0; i < constructSignatureDeclarations.length - 1; i++) { | ||
const currentItem = ((constructSignatureDeclarations[i] | ||
.returnType as TSESTree.TSTypeAnnotation) | ||
.typeAnnotation as TSESTree.TSTypeReference).typeName; | ||
const nextItem = ((constructSignatureDeclarations[i + 1] | ||
.returnType as TSESTree.TSTypeAnnotation) | ||
.typeAnnotation as TSESTree.TSTypeReference).typeName; | ||
|
||
if (currentItem > nextItem) { | ||
return context.report({ | ||
messageId: 'notSorted', | ||
node: currentItem, | ||
}); | ||
} | ||
} | ||
|
||
for (let i = 0; i < callSignatureDeclarations.length - 1; i++) { | ||
const currentItem = ((callSignatureDeclarations[i] | ||
.returnType as TSESTree.TSTypeAnnotation) | ||
.typeAnnotation as TSESTree.TSTypeReference).typeName; | ||
const nextItem = ((callSignatureDeclarations[i + 1] | ||
.returnType as TSESTree.TSTypeAnnotation) | ||
.typeAnnotation as TSESTree.TSTypeReference).typeName; | ||
|
||
if (currentItem > nextItem) { | ||
return context.report({ | ||
messageId: 'notSorted', | ||
node: currentItem, | ||
}); | ||
} | ||
} | ||
|
||
return; // No rule violation found | ||
}, | ||
}; | ||
}, | ||
}); |
Oops, something went wrong.