"use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var Lint = require("../lint"); var Rule = (function (_super) { __extends(Rule, _super); function Rule() { _super.apply(this, arguments); } Rule.prototype.apply = function (sourceFile) { var orderedImportsWalker = new OrderedImportsWalker(sourceFile, this.getOptions()); return this.applyWithWalker(orderedImportsWalker); }; Rule.metadata = { ruleName: "ordered-imports", description: "Requires that import statements be alphabetized.", descriptionDetails: (_a = ["\n Enforce a consistent ordering for ES6 imports:\n - Named imports must be alphabetized (i.e. \"import {A, B, C} from \"foo\";\")\n - The exact ordering can be controled by the named-imports-order option.\n - \"longName as name\" imports are ordered by \"longName\".\n - Import sources must be alphabetized within groups, i.e.:\n import * as foo from \"a\";\n import * as bar from \"b\";\n - Groups of imports are delineated by blank lines. You can use these to group imports\n however you like, e.g. by first- vs. third-party or thematically."], _a.raw = ["\n Enforce a consistent ordering for ES6 imports:\n - Named imports must be alphabetized (i.e. \"import {A, B, C} from \"foo\";\")\n - The exact ordering can be controled by the named-imports-order option.\n - \"longName as name\" imports are ordered by \"longName\".\n - Import sources must be alphabetized within groups, i.e.:\n import * as foo from \"a\";\n import * as bar from \"b\";\n - Groups of imports are delineated by blank lines. You can use these to group imports\n however you like, e.g. by first- vs. third-party or thematically."], Lint.Utils.dedent(_a)), optionsDescription: (_b = ["\n You may set the `\"import-sources-order\"` option to control the ordering of source\n imports (the `\"foo\"` in `import {A, B, C} from \"foo\"`).\n\n Possible values for `\"import-sources-order\"` are:\n * `\"case-insensitive'`: Correct order is `\"Bar\"`, `\"baz\"`, `\"Foo\"`. (This is the default.)\n * `\"lowercase-first\"`: Correct order is `\"baz\"`, `\"Bar\"`, `\"Foo\"`.\n * `\"lowercase-last\"`: Correct order is `\"Bar\"`, `\"Foo\"`, `\"baz\"`.\n\n You may set the `\"named-imports-order\"` option to control the ordering of named\n imports (the `{A, B, C}` in `import {A, B, C} from \"foo\"`).\n\n Possible values for `\"named-imports-order\"` are:\n\n * `\"case-insensitive'`: Correct order is `{A, b, C}`. (This is the default.)\n * `\"lowercase-first\"`: Correct order is `{b, A, C}`.\n * `\"lowercase-last\"`: Correct order is `{A, C, b}`.\n\n "], _b.raw = ["\n You may set the \\`\"import-sources-order\"\\` option to control the ordering of source\n imports (the \\`\"foo\"\\` in \\`import {A, B, C} from \"foo\"\\`).\n\n Possible values for \\`\"import-sources-order\"\\` are:\n * \\`\"case-insensitive'\\`: Correct order is \\`\"Bar\"\\`, \\`\"baz\"\\`, \\`\"Foo\"\\`. (This is the default.)\n * \\`\"lowercase-first\"\\`: Correct order is \\`\"baz\"\\`, \\`\"Bar\"\\`, \\`\"Foo\"\\`.\n * \\`\"lowercase-last\"\\`: Correct order is \\`\"Bar\"\\`, \\`\"Foo\"\\`, \\`\"baz\"\\`.\n\n You may set the \\`\"named-imports-order\"\\` option to control the ordering of named\n imports (the \\`{A, B, C}\\` in \\`import {A, B, C} from \"foo\"\\`).\n\n Possible values for \\`\"named-imports-order\"\\` are:\n\n * \\`\"case-insensitive'\\`: Correct order is \\`{A, b, C}\\`. (This is the default.)\n * \\`\"lowercase-first\"\\`: Correct order is \\`{b, A, C}\\`.\n * \\`\"lowercase-last\"\\`: Correct order is \\`{A, C, b}\\`.\n\n "], Lint.Utils.dedent(_b)), options: { type: "object", properties: { "import-sources-order": { type: "string", enum: ["case-insensitive", "lowercase-first", "lowercase-last"], }, "named-imports-order": { type: "string", enum: ["case-insensitive", "lowercase-first", "lowercase-last"], }, }, additionalProperties: false, }, optionExamples: [ "true", '[true, {"import-sources-order": "lowercase-last", "named-imports-order": "lowercase-first"}]', ], type: "style", }; Rule.IMPORT_SOURCES_UNORDERED = "Import sources within a group must be alphabetized."; Rule.NAMED_IMPORTS_UNORDERED = "Named imports must be alphabetized."; return Rule; var _a, _b; }(Lint.Rules.AbstractRule)); exports.Rule = Rule; function flipCase(x) { return x.split("").map(function (char) { if (char >= "a" && char <= "z") { return char.toUpperCase(); } else if (char >= "A" && char <= "Z") { return char.toLowerCase(); } return char; }).join(""); } function findUnsortedPair(xs, transform) { for (var i = 1; i < xs.length; i++) { if (transform(xs[i].getText()) < transform(xs[i - 1].getText())) { return [xs[i - 1], xs[i]]; } } return null; } var TRANSFORMS = { "case-insensitive": function (x) { return x.toLowerCase(); }, "lowercase-first": flipCase, "lowercase-last": function (x) { return x; }, }; var OrderedImportsWalker = (function (_super) { __extends(OrderedImportsWalker, _super); function OrderedImportsWalker(sourceFile, options) { _super.call(this, sourceFile, options); this.lastImportSource = null; this.importSourcesOrderTransform = null; this.namedImportsOrderTransform = null; var optionSet = this.getOptions()[0] || {}; this.importSourcesOrderTransform = TRANSFORMS[optionSet["import-sources-order"] || "case-insensitive"]; this.namedImportsOrderTransform = TRANSFORMS[optionSet["named-imports-order"] || "case-insensitive"]; } OrderedImportsWalker.prototype.visitImportDeclaration = function (node) { var source = this.importSourcesOrderTransform(node.moduleSpecifier.getText()); if (this.lastImportSource && source < this.lastImportSource) { this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.IMPORT_SOURCES_UNORDERED)); } this.lastImportSource = source; _super.prototype.visitImportDeclaration.call(this, node); }; OrderedImportsWalker.prototype.visitNamedImports = function (node) { var imports = node.elements; var pair = findUnsortedPair(imports, this.namedImportsOrderTransform); if (pair !== null) { var a = pair[0], b = pair[1]; this.addFailure(this.createFailure(a.getStart(), b.getEnd() - a.getStart(), Rule.NAMED_IMPORTS_UNORDERED)); } _super.prototype.visitNamedImports.call(this, node); }; OrderedImportsWalker.prototype.visitNode = function (node) { var prefixLength = node.getStart() - node.getFullStart(); var prefix = node.getFullText().slice(0, prefixLength); if (prefix.indexOf("\n\n") >= 0 || prefix.indexOf("\r\n\r\n") >= 0) { this.lastImportSource = null; } _super.prototype.visitNode.call(this, node); }; return OrderedImportsWalker; }(Lint.RuleWalker));