This repository has been archived by the owner on Apr 27, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Created a custom rule for import-type-order (#48)
* Added structured-imports rule ✨ * Clean up and tweaks * Add rule docs 📝 * Change error messages * Fix tlint error 🔨 * Enable custom rule * PR fixes 🔨 * Clean up getImportType() * Clean error wording and use footnote definition in test cases * Refactor rule for minor performance improvements with AbstractWalker * Utilize applyWithFunction() instead of applyWithWalker() * Add more speicific test cases * Add test case side-effects imports * PR Fixes, round 2 🔨 * Update CHANGELOG.md 📝 * PR fixes, round 3 🔨 * Fix test cases
- Loading branch information
1 parent
0506e0b
commit 60ec7b8
Showing
11 changed files
with
141 additions
and
0 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
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,26 @@ | ||
# Enforces order of import statement types. | ||
|
||
## Rationale | ||
- Improves readability and organization by grouping naturally related items together. | ||
|
||
## Rule Details | ||
Improves readability and organization by grouping related imports together. Imports should be listed in order of: external modules, absolute paths, relative paths, relative siblings. | ||
|
||
The following are considered warnings | ||
```js | ||
import Foo from './foo'; // Should come after external and parent imports. | ||
import Baz = '../baz'; // Should come after external imports | ||
import BarModule from 'BarModule'; | ||
``` | ||
|
||
The following import order is valid: | ||
|
||
```js | ||
import BarModule from 'BarModule'; | ||
import Baz = '../baz'; | ||
import Foo from './foo'; | ||
``` | ||
|
||
## When Not To Use It | ||
|
||
If you do not wish to enforce consistency in your import statements. |
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
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,68 @@ | ||
import * as ts from 'typescript'; | ||
import * as Lint from 'tslint'; | ||
|
||
|
||
export class Rule extends Lint.Rules.AbstractRule { | ||
/* tslint:disable:object-literal-sort-keys */ | ||
public static metadata: Lint.IRuleMetadata = { | ||
'ruleName': 'import-type-order', | ||
'description': 'Enforce structure to your imports. Import structure should be listed in the following order: modules, absolute imports, relative parent/ancestor directories, relative sibling directors.', | ||
'hasFix': false, | ||
'optionsDescription': 'Not configurable.', | ||
'options': null, | ||
'optionExamples': null, | ||
'type': 'style', | ||
'typescriptOnly': true, | ||
}; | ||
/* tslint:enable:object-literal-sort-keys */ | ||
|
||
public static IMPORT_TYPE_ORDER_ERROR = 'Imports should be listed in the following order: external imports, absolute imports, ancestor imports, sibling imports.'; | ||
|
||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { | ||
return this.applyWithFunction(sourceFile, walker); | ||
} | ||
} | ||
|
||
enum ImportType { | ||
External = 0, // 'external' | ||
Absolute = 1, // '/some/folder/file' | ||
Ancestor = 2, // '../parentFolder' | ||
Sibling = 3, // './siblingFolder' | ||
}; | ||
|
||
const importStuctureOrder = [ImportType.External, ImportType.Absolute, ImportType.Ancestor, ImportType.Sibling]; | ||
|
||
function walker(context: Lint.WalkContext<void>): void { | ||
const { sourceFile } = context; | ||
const importNodes = sourceFile.statements | ||
.filter((child) => child.kind === ts.SyntaxKind.ImportDeclaration) | ||
.map((child) => child as ts.ImportDeclaration); | ||
|
||
if (importNodes.length === 0) { return; } | ||
|
||
let lastValidType = getImportType(importNodes.shift()); | ||
while (importNodes.length) { | ||
const currentNode = importNodes.shift(); | ||
const currentType = getImportType(currentNode); | ||
if (lastValidType > currentType) { | ||
const {moduleSpecifier} = currentNode; | ||
const errorStart = moduleSpecifier.getStart(); | ||
const errorWidth = moduleSpecifier.getEnd() - errorStart; | ||
context.addFailureAt(errorStart, errorWidth, Rule.IMPORT_TYPE_ORDER_ERROR); | ||
} else { | ||
lastValidType = currentType; | ||
} | ||
} | ||
} | ||
|
||
function getImportType({moduleSpecifier}: ts.ImportDeclaration): ImportType { | ||
const path = moduleSpecifier.getText(); | ||
if (path.substr(1, 2) === './') { | ||
return ImportType.Sibling; | ||
} else if (path.substr(1, 3) === '../') { | ||
return ImportType.Ancestor; | ||
} else if (path[1] === '/') { | ||
return ImportType.Absolute; | ||
} | ||
return ImportType.External; | ||
}; |
9 changes: 9 additions & 0 deletions
9
test/rules/structured-imports/invalid-order-side-effects.ts.lint
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,9 @@ | ||
import './Bar'; | ||
import '../foo'; | ||
~~~~~~~~ [0] | ||
import '/Baz'; | ||
~~~~~~ [0] | ||
import 'FooBarBaz'; | ||
~~~~~~~~~~~ [0] | ||
|
||
[0]: Imports should be listed in the following order: external imports, absolute imports, ancestor imports, sibling imports. |
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,10 @@ | ||
|
||
import BarTwo from './Bar'; | ||
import FooTwo from '../foo'; | ||
~~~~~~~~ [0] | ||
import BazTwo from '/Baz'; | ||
~~~~~~ [0] | ||
import {Foo, Bar, Baz} from 'FooBarBaz'; | ||
~~~~~~~~~~~ [0] | ||
|
||
[0]: Imports should be listed in the following order: external imports, absolute imports, ancestor imports, sibling imports. |
5 changes: 5 additions & 0 deletions
5
test/rules/structured-imports/no-absolute-before-external.ts.lint
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,5 @@ | ||
import Foo from '/foo'; | ||
import {Bar, Baz} from 'BarBaz'; | ||
~~~~~~~~ [0] | ||
|
||
[0]: Imports should be listed in the following order: external imports, absolute imports, ancestor imports, sibling imports. |
5 changes: 5 additions & 0 deletions
5
test/rules/structured-imports/no-parent-before-absolute.ts.lint
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,5 @@ | ||
import Foo from '../foo'; | ||
import BarModule from '/bar'; | ||
~~~~~~ [0] | ||
|
||
[0]: Imports should be listed in the following order: external imports, absolute imports, ancestor imports, sibling imports. |
5 changes: 5 additions & 0 deletions
5
test/rules/structured-imports/no-sibling-before-parent.ts.lint
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,5 @@ | ||
import Foo from './foo'; | ||
import Bar from '../bar'; | ||
~~~~~~~~ [0] | ||
|
||
[0]: Imports should be listed in the following order: external imports, absolute imports, ancestor imports, sibling imports. |
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,5 @@ | ||
{ | ||
"rules": { | ||
"import-type-order": true | ||
} | ||
} |
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,5 @@ | ||
import {Foo, Bar, Baz} from 'FooBarBaz'; | ||
import BazTwo from '/Baz'; | ||
import FooTwo from '../foo'; | ||
import './side-effect' | ||
import BarTwo from './Bar'; |