From 4e20b1bacd2aa34219edecb3a0883f99fe542913 Mon Sep 17 00:00:00 2001 From: paibamboo Date: Fri, 11 Oct 2019 14:28:12 +0700 Subject: [PATCH] feat: interface sort keys rule --- src/interfaceSortKeysRule.ts | 50 +++++++++++++++++++ .../interface-sort-keys/default/test.ts.lint | 49 ++++++++++++++++++ .../interface-sort-keys/default/tslint.json | 8 +++ 3 files changed, 107 insertions(+) create mode 100644 src/interfaceSortKeysRule.ts create mode 100644 test/rules/interface-sort-keys/default/test.ts.lint create mode 100644 test/rules/interface-sort-keys/default/tslint.json diff --git a/src/interfaceSortKeysRule.ts b/src/interfaceSortKeysRule.ts new file mode 100644 index 0000000..0775d22 --- /dev/null +++ b/src/interfaceSortKeysRule.ts @@ -0,0 +1,50 @@ +import * as Lint from "tslint"; +import {Identifier} from "typescript"; +import * as ts from "typescript"; + +class InterfaceSortKeysRule extends Lint.RuleWalker { + constructor( + sourceFile: ts.SourceFile, + options: Lint.IOptions + ) { + super(sourceFile, options); + } + + public visitInterfaceDeclaration(node: ts.InterfaceDeclaration): void { + super.visitInterfaceDeclaration(node); + this.checkAlphabetical(node); + } + + public visitTypeLiteral(node: ts.TypeLiteralNode): void { + super.visitTypeLiteral(node); + this.checkAlphabetical(node); + } + + private checkAlphabetical(node: ts.InterfaceDeclaration | ts.TypeLiteralNode): void { + const properties = node.members.map((member) => (member.name as Identifier).escapedText.toString()); + const unsortedIndex = this.getUnsortedIndex(properties); + if (unsortedIndex !== -1) { + this.addFailureAtNode( + node.members[unsortedIndex].name as Identifier, + `The key '${(node.members[unsortedIndex].name as Identifier).escapedText}' is not sorted alphabetically` + ); + } + } + + private getUnsortedIndex(strs: string[], startIndex: number = 0): number { + if (strs.length < 2 || strs.length === startIndex + 1) { + return -1; + } + if (strs[startIndex] > strs[startIndex + 1]) { + return startIndex; + } + return this.getUnsortedIndex(strs, startIndex + 1); + } +} + +// tslint:disable-next-line:export-name max-classes-per-file +export class Rule extends Lint.Rules.AbstractRule { + public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithWalker(new InterfaceSortKeysRule(sourceFile, this.getOptions())); + } +} diff --git a/test/rules/interface-sort-keys/default/test.ts.lint b/test/rules/interface-sort-keys/default/test.ts.lint new file mode 100644 index 0000000..be5fa0f --- /dev/null +++ b/test/rules/interface-sort-keys/default/test.ts.lint @@ -0,0 +1,49 @@ +export interface IAlphabet { + aaa: string; + bbb: string; + ccc: string; + cccAaa: string; + cccc: string; + ddd: { + dddAaa: string; + dddBbb: string; + } +} + +export interface IAlphabet { + aaa: string; + bbb: string; + cccAaa: string; + ~~~~~~ [The key 'cccAaa' is not sorted alphabetically] + ccc: string; + cccc: string; +} + +export interface IAlphabet { + aaa: string; + bbb: string; + ccc: string; + cccc: string; + ~~~~ [The key 'cccc' is not sorted alphabetically] + cccAaa: string; +} + +export interface IAlphabet { + aaa: string; + ddd: { + ~~~ [The key 'ddd' is not sorted alphabetically] + dddAaa: string; + dddBbb: string; + } + bbb: string; +} + +export interface IAlphabet { + aaa: string; + bbb: string; + ddd: { + dddBbb: string; + ~~~~~~ [The key 'dddBbb' is not sorted alphabetically] + dddAaa: string; + } +} diff --git a/test/rules/interface-sort-keys/default/tslint.json b/test/rules/interface-sort-keys/default/tslint.json new file mode 100644 index 0000000..cae8167 --- /dev/null +++ b/test/rules/interface-sort-keys/default/tslint.json @@ -0,0 +1,8 @@ +{ + "rulesDirectory": [ + "../../../../lib" + ], + "rules": { + "interface-sort-keys": true + } +}