Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

Ordered-Imports Rule: Allow sorting by the trailing end of the module path #3178

Merged
merged 6 commits into from
Sep 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/configs/all.ts
Expand Up @@ -216,6 +216,7 @@ export const rules = {
"ordered-imports": [true, {
"import-sources-order": "case-insensitive",
"named-imports-order": "case-insensitive",
"module-source-path": "full",
}],
"prefer-function-over-method": true,
"prefer-method-signature": true,
Expand Down
1 change: 1 addition & 0 deletions src/configs/recommended.ts
Expand Up @@ -128,6 +128,7 @@ export const rules = {
"ordered-imports": {
options: {
"import-sources-order": "case-insensitive",
"module-source-path": "full",
"named-imports-order": "case-insensitive",
},
},
Expand Down
33 changes: 31 additions & 2 deletions src/rules/orderedImportsRule.ts
Expand Up @@ -64,6 +64,14 @@ export class Rule extends Lint.Rules.AbstractRule {
* \`"lowercase-last"\`: Correct order is \`{A, C, b}\`.
* \`"any"\`: Allow any order.

You may set the \`"module-source-path"\` option to control the ordering of imports based full path
or just the module name

Possible values for \`"module-source-path"\` are:

* \`"full'\`: Correct order is \`"./a/Foo"\`, \`"./b/baz"\`, \`"./c/Bar"\`. (This is the default.)
* \`"basename"\`: Correct order is \`"./c/Bar"\`, \`"./b/baz"\`, \`"./a/Foo"\`.

`,
options: {
type: "object",
Expand All @@ -76,6 +84,10 @@ export class Rule extends Lint.Rules.AbstractRule {
type: "string",
enum: ["case-insensitive", "lowercase-first", "lowercase-last", "any"],
},
"module-source-path": {
type: "string",
enum: ["full", "basename"],
},
},
additionalProperties: false,
},
Expand Down Expand Up @@ -104,26 +116,42 @@ const TRANSFORMS = new Map<string, Transform>([
["case-insensitive", (x) => x.toLowerCase()],
["lowercase-first", flipCase],
["lowercase-last", (x) => x],
["full", (x) => x],
["basename", (x) => {
if (!(ts as any as {isExternalModuleNameRelative(m: string): boolean}).isExternalModuleNameRelative(x)) {
return x;
}

const splitIndex = x.lastIndexOf("/");
if (splitIndex === -1) {
return x;
}
return x.substr(splitIndex + 1);
}],
]);

interface Options {
importSourcesOrderTransform: Transform;
moduleSourcePath: Transform;
namedImportsOrderTransform: Transform;
}

interface JsonOptions {
"import-sources-order"?: string;
"named-imports-order"?: string;
"module-source-path"?: string;
}

function parseOptions(ruleArguments: any[]): Options {
const optionSet = (ruleArguments as JsonOptions[])[0];
const {
"import-sources-order": sources = "case-insensitive",
"named-imports-order": named = "case-insensitive",
"module-source-path": path = "full",
} = optionSet === undefined ? {} : optionSet;
return {
importSourcesOrderTransform: TRANSFORMS.get(sources)!,
moduleSourcePath: TRANSFORMS.get(path)!,
namedImportsOrderTransform: TRANSFORMS.get(named)!,
};
}
Expand Down Expand Up @@ -196,10 +224,11 @@ class Walker extends Lint.AbstractWalker<Options> {
}

private checkSource(source: string, node: ImportDeclaration["node"]) {
const currentSource = this.options.moduleSourcePath(source);
const previousSource = this.currentImportsBlock.getLastImportSource();
this.currentImportsBlock.addImportDeclaration(this.sourceFile, node, source);
this.currentImportsBlock.addImportDeclaration(this.sourceFile, node, currentSource);

if (previousSource !== null && compare(source, previousSource) === -1) {
if (previousSource !== null && compare(currentSource, previousSource) === -1) {
this.lastFix = [];
this.addFailureAtNode(node, Rule.IMPORT_SOURCES_UNORDERED, this.lastFix);
}
Expand Down
19 changes: 19 additions & 0 deletions test/rules/ordered-imports/module-source-path/test.ts.fix
@@ -0,0 +1,19 @@
// Other styles of import statements.
import someDefault from "module";
import "something";
import someDefault, {nameA, nameBReallyLong as anotherName} from "./wherever";

// contains relative path & ImportEqualsDeclaration
import a from './foo/a';
import b from './bar/b';
import c = require('./baz/c');

// contains relative path & ImportEqualsDeclaration
import a from '../../foo/a';
import b from './bar/b';
import c = require('./baz/c');

// contains scoped import
import a from 'foo/a';
import b = require('foo/b');
import c from 'foo/c';
23 changes: 23 additions & 0 deletions test/rules/ordered-imports/module-source-path/test.ts.lint
@@ -0,0 +1,23 @@
// Other styles of import statements.
import someDefault, {nameA, nameBReallyLong as anotherName} from "./wherever";
import someDefault from "module";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [Import sources within a group must be alphabetized.]
import "something";

// contains relative path & ImportEqualsDeclaration
import c = require('./baz/c');
import a from './foo/a';
~~~~~~~~~~~~~~~~~~~~~~~~ [Import sources within a group must be alphabetized.]
import b from './bar/b';

// contains relative path & ImportEqualsDeclaration
import c = require('./baz/c');
import a from '../../foo/a';
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [Import sources within a group must be alphabetized.]
import b from './bar/b';

// contains scoped import
import b = require('foo/b');
import a from 'foo/a';
~~~~~~~~~~~~~~~~~~~~~~ [Import sources within a group must be alphabetized.]
import c from 'foo/c';
5 changes: 5 additions & 0 deletions test/rules/ordered-imports/module-source-path/tslint.json
@@ -0,0 +1,5 @@
{
"rules": {
"ordered-imports": [true, {"module-source-path": "basename"}]
}
}