.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / tslint / lib / rules / orderedImportsRule.js
1 "use strict";
2 /**
3  * @license
4  * Copyright 2018 Palantir Technologies, Inc.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 Object.defineProperty(exports, "__esModule", { value: true });
19 var tslib_1 = require("tslib");
20 var tsutils_1 = require("tsutils");
21 var ts = require("typescript");
22 var Lint = require("../index");
23 var Rule = /** @class */ (function (_super) {
24     tslib_1.__extends(Rule, _super);
25     function Rule() {
26         return _super !== null && _super.apply(this, arguments) || this;
27     }
28     Rule.prototype.apply = function (sourceFile) {
29         return this.applyWithWalker(new Walker(sourceFile, this.ruleName, parseOptions(this.ruleArguments)));
30     };
31     /* tslint:disable:object-literal-sort-keys */
32     Rule.metadata = {
33         ruleName: "ordered-imports",
34         description: "Requires that import statements be alphabetized and grouped.",
35         descriptionDetails: Lint.Utils.dedent(templateObject_1 || (templateObject_1 = tslib_1.__makeTemplateObject(["\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 controlled 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 this rule to group\n                imports however you like, e.g. by first- vs. third-party or thematically or\n                you can define groups based upon patterns in import path names."], ["\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 controlled 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 this rule to group\n                imports however you like, e.g. by first- vs. third-party or thematically or\n                you can define groups based upon patterns in import path names."]))),
36         hasFix: true,
37         optionsDescription: Lint.Utils.dedent(templateObject_2 || (templateObject_2 = tslib_1.__makeTemplateObject(["\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\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            * `\"any\"`: Allow any order.\n\n            You may set the `\"grouped-imports\"` option to control the grouping of source\n            imports (the `\"foo\"` in `import {A, B, C} from \"foo\"`).  The grouping used\n            is controlled by the `\"groups\"` option.\n\n            Possible values for `\"grouped-imports\"` are:\n\n            * `false`: Do not enforce grouping. (This is the default.)\n            * `true`: Group source imports using default grouping or groups setting.\n\n            The value of `\"groups\"` is a list of group rules of the form:\n\n                [{\n                    \"name\": \"optional rule name\",\n                    \"match\": \"regex string\",\n                    \"order\": 10\n                }, {\n                    \"name\": \"pkga imports\",\n                    \"match\": \"^@pkga\",\n                    \"order\": 20\n                }]\n\n            there is also a simplified form where you only pass a list of patterns and\n            the order is given by the position in the list\n\n                [\"^@pkga\", \"^\\.\\.\"]\n\n            The first rule in the list to match a given import is the group that is used.\n            If no rule in matched then the import will be put in an `unmatched` group\n            at the end of all groups. The groups must be ordered based upon the sequential\n            value of the `order` value. (ie. order 0 is first)\n\n            If no `\"groups\"` options is set, a default grouping is used of third-party,\n            parent directories and the current directory. (`\"bar\"`, `\"../baz\"`, `\"./foo\"`.)\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            * `\"any\"`: Allow any order.\n\n            You may set the `\"module-source-path\"` option to control the ordering of imports based full path\n            or just the module name\n\n            Possible values for `\"module-source-path\"` are:\n\n            * `\"full'`: Correct order is  `\"./a/Foo\"`, `\"./b/baz\"`, `\"./c/Bar\"`. (This is the default.)\n            * `\"basename\"`: Correct order is `\"./c/Bar\"`, `\"./b/baz\"`, `\"./a/Foo\"`.\n\n        "], ["\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\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            * \\`\"any\"\\`: Allow any order.\n\n            You may set the \\`\"grouped-imports\"\\` option to control the grouping of source\n            imports (the \\`\"foo\"\\` in \\`import {A, B, C} from \"foo\"\\`).  The grouping used\n            is controlled by the \\`\"groups\"\\` option.\n\n            Possible values for \\`\"grouped-imports\"\\` are:\n\n            * \\`false\\`: Do not enforce grouping. (This is the default.)\n            * \\`true\\`: Group source imports using default grouping or groups setting.\n\n            The value of \\`\"groups\"\\` is a list of group rules of the form:\n\n                [{\n                    \"name\": \"optional rule name\",\n                    \"match\": \"regex string\",\n                    \"order\": 10\n                }, {\n                    \"name\": \"pkga imports\",\n                    \"match\": \"^@pkga\",\n                    \"order\": 20\n                }]\n\n            there is also a simplified form where you only pass a list of patterns and\n            the order is given by the position in the list\n\n                [\"^@pkga\", \"^\\\\.\\\\.\"]\n\n            The first rule in the list to match a given import is the group that is used.\n            If no rule in matched then the import will be put in an \\`unmatched\\` group\n            at the end of all groups. The groups must be ordered based upon the sequential\n            value of the \\`order\\` value. (ie. order 0 is first)\n\n            If no \\`\"groups\"\\` options is set, a default grouping is used of third-party,\n            parent directories and the current directory. (\\`\"bar\"\\`, \\`\"../baz\"\\`, \\`\"./foo\"\\`.)\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            * \\`\"any\"\\`: Allow any order.\n\n            You may set the \\`\"module-source-path\"\\` option to control the ordering of imports based full path\n            or just the module name\n\n            Possible values for \\`\"module-source-path\"\\` are:\n\n            * \\`\"full'\\`: Correct order is  \\`\"./a/Foo\"\\`, \\`\"./b/baz\"\\`, \\`\"./c/Bar\"\\`. (This is the default.)\n            * \\`\"basename\"\\`: Correct order is \\`\"./c/Bar\"\\`, \\`\"./b/baz\"\\`, \\`\"./a/Foo\"\\`.\n\n        "]))),
38         options: {
39             type: "object",
40             properties: {
41                 "grouped-imports": {
42                     type: "boolean",
43                 },
44                 groups: {
45                     type: "list",
46                     listType: {
47                         oneOf: [
48                             {
49                                 type: "string",
50                             },
51                             {
52                                 type: "object",
53                                 properties: {
54                                     name: { type: "string" },
55                                     match: { type: "string" },
56                                     order: { type: "number" },
57                                 },
58                                 required: ["match", "order"],
59                             },
60                         ],
61                     },
62                 },
63                 "import-sources-order": {
64                     type: "string",
65                     enum: ["case-insensitive", "lowercase-first", "lowercase-last", "any"],
66                 },
67                 "named-imports-order": {
68                     type: "string",
69                     enum: ["case-insensitive", "lowercase-first", "lowercase-last", "any"],
70                 },
71                 "module-source-path": {
72                     type: "string",
73                     enum: ["full", "basename"],
74                 },
75             },
76             additionalProperties: false,
77         },
78         optionExamples: [
79             true,
80             [
81                 true,
82                 {
83                     "import-sources-order": "lowercase-last",
84                     "named-imports-order": "lowercase-first",
85                 },
86             ],
87         ],
88         type: "style",
89         typescriptOnly: false,
90     };
91     /* tslint:enable:object-literal-sort-keys */
92     Rule.IMPORT_SOURCES_NOT_GROUPED_PREFIX = "Imports from this module are not allowed in this group.  The expected groups (in order) are:";
93     Rule.IMPORT_SOURCES_UNORDERED = "Import sources within a group must be alphabetized.";
94     Rule.NAMED_IMPORTS_UNORDERED = "Named imports must be alphabetized.";
95     return Rule;
96 }(Lint.Rules.AbstractRule));
97 exports.Rule = Rule;
98 var TRANSFORMS = new Map([
99     ["any", function () { return ""; }],
100     ["case-insensitive", function (x) { return x.toLowerCase(); }],
101     ["lowercase-first", flipCase],
102     ["lowercase-last", function (x) { return x; }],
103     ["full", function (x) { return x; }],
104     [
105         "basename",
106         function (x) {
107             if (!ts.isExternalModuleNameRelative(x)) {
108                 return x;
109             }
110             var splitIndex = x.lastIndexOf("/");
111             if (splitIndex === -1) {
112                 return x;
113             }
114             return x.substr(splitIndex + 1);
115         },
116     ],
117 ]);
118 function parseOptions(ruleArguments) {
119     var optionSet = ruleArguments[0];
120     // Use default groups to order by third-party, parent, local
121     var defaultGroups = [
122         { name: "parent directories", match: "^\\.\\.", order: 20 },
123         { name: "current directory", match: "^\\.", order: 30 },
124         { name: "libraries", match: ".*", order: 10 },
125     ];
126     var _a = optionSet === undefined ? {} : optionSet, _b = _a["grouped-imports"], isGrouped = _b === void 0 ? false : _b, _c = _a["import-sources-order"], sources = _c === void 0 ? "case-insensitive" : _c, _d = _a["named-imports-order"], named = _d === void 0 ? "case-insensitive" : _d, _e = _a["module-source-path"], path = _e === void 0 ? "full" : _e, _f = _a.groups, groups = _f === void 0 ? defaultGroups : _f;
127     // build up list of compiled groups
128     // - handle case where "group" is just a string pattern
129     //   vs a full group object
130     var compiledGroups = groups.map(function (g, idx) {
131         if (typeof g === "string") {
132             return { name: "/" + g + "/", match: new RegExp(g), order: idx };
133         }
134         else {
135             return {
136                 match: new RegExp(g.match),
137                 name: g.name !== undefined ? g.name : "/" + g.match + "/",
138                 order: g.order,
139             };
140         }
141     });
142     return {
143         groupedImports: isGrouped,
144         groups: compiledGroups,
145         importSourcesOrderTransform: TRANSFORMS.get(sources),
146         moduleSourceTransform: TRANSFORMS.get(path),
147         namedImportsOrderTransform: TRANSFORMS.get(named),
148     };
149 }
150 var Walker = /** @class */ (function (_super) {
151     tslib_1.__extends(Walker, _super);
152     function Walker() {
153         var _this = _super !== null && _super.apply(this, arguments) || this;
154         _this.importsBlocks = [new ImportsBlock()];
155         // group to use when no other group matches
156         _this.defaultGroup = {
157             match: /.*/,
158             name: "unmatched",
159             order: Number.MAX_SAFE_INTEGER,
160         };
161         return _this;
162     }
163     Object.defineProperty(Walker.prototype, "currentImportsBlock", {
164         get: function () {
165             return this.importsBlocks[this.importsBlocks.length - 1];
166         },
167         enumerable: true,
168         configurable: true
169     });
170     Walker.prototype.walk = function (sourceFile) {
171         // Walk through all statements checking import statements
172         // and building up ImportsBlocks along the way (with replacements)
173         for (var _i = 0, _a = sourceFile.statements; _i < _a.length; _i++) {
174             var statement = _a[_i];
175             this.checkStatement(statement);
176         }
177         this.endBlock();
178         // Optionally check the ImportsBlocks for grouping
179         if (this.options.groupedImports) {
180             this.checkBlocksGrouping();
181         }
182     };
183     Walker.prototype.checkStatement = function (statement) {
184         if (!(tsutils_1.isImportDeclaration(statement) || tsutils_1.isImportEqualsDeclaration(statement)) ||
185             /\r?\n\r?\n/.test(this.sourceFile.text.slice(statement.getFullStart(), statement.getStart(this.sourceFile)))) {
186             this.endBlock();
187         }
188         if (tsutils_1.isImportDeclaration(statement)) {
189             this.checkImportDeclaration(statement);
190         }
191         else if (tsutils_1.isImportEqualsDeclaration(statement)) {
192             this.checkImportEqualsDeclaration(statement);
193         }
194         else if (tsutils_1.isModuleDeclaration(statement)) {
195             var body = moduleDeclarationBody(statement);
196             if (body !== undefined) {
197                 for (var _i = 0, _a = body.statements; _i < _a.length; _i++) {
198                     var subStatement = _a[_i];
199                     this.checkStatement(subStatement);
200                 }
201                 this.endBlock();
202             }
203         }
204     };
205     Walker.prototype.checkImportDeclaration = function (node) {
206         // ex:  import {name1, name2 } from 'import/path';
207         if (!tsutils_1.isStringLiteral(node.moduleSpecifier)) {
208             // Ignore grammar error
209             return;
210         }
211         var importPath = removeQuotes(node.moduleSpecifier.text);
212         this.checkImport(importPath, node);
213         // check the names in the import are ordered correctly
214         var importClause = node.importClause;
215         if (importClause !== undefined &&
216             importClause.namedBindings !== undefined &&
217             tsutils_1.isNamedImports(importClause.namedBindings)) {
218             this.checkNamedImports(importClause.namedBindings);
219         }
220     };
221     Walker.prototype.checkImportEqualsDeclaration = function (node) {
222         // only allowed `import x = require('y');`
223         var moduleReference = node.moduleReference;
224         if (!tsutils_1.isExternalModuleReference(moduleReference)) {
225             return;
226         }
227         var expression = moduleReference.expression;
228         if (expression === undefined || !tsutils_1.isStringLiteral(expression)) {
229             return;
230         }
231         var importPath = removeQuotes(expression.text);
232         this.checkImport(importPath, node);
233     };
234     /**
235      * Check the given import to see if we have valid import ordering.
236      */
237     Walker.prototype.checkImport = function (fullImportPath, node) {
238         // from this point forward we use the transformed import paths
239         // - group lookup is based upon the full import path with no transform
240         var matchingGroup = this.getMatchingGroup(fullImportPath);
241         // determine the module name to use for sorting after the required transforms
242         var importPath = this.options.importSourcesOrderTransform(this.options.moduleSourceTransform(fullImportPath));
243         var prevImportPath = this.currentImportsBlock.getLastImportSource();
244         this.currentImportsBlock.addImportDeclaration(this.sourceFile, node, importPath, matchingGroup);
245         if (prevImportPath !== null && compare(importPath, prevImportPath) === -1) {
246             this.lastFix = [];
247             this.addFailureAtNode(node, Rule.IMPORT_SOURCES_UNORDERED, this.lastFix);
248         }
249     };
250     Walker.prototype.endBlock = function () {
251         if (this.lastFix !== undefined) {
252             var replacement = this.currentImportsBlock.getReplacement();
253             if (replacement !== undefined) {
254                 this.lastFix.push(replacement);
255             }
256             this.lastFix = undefined;
257         }
258         this.importsBlocks.push(new ImportsBlock());
259     };
260     /**
261      * Check that names within the given import are ordered correctly as required.
262      * If not, adds a failure and updates import blocks with correct order
263      * for replacement.
264      */
265     Walker.prototype.checkNamedImports = function (node) {
266         var _this = this;
267         var imports = node.elements;
268         var pair = findUnsortedPair(imports, this.options.namedImportsOrderTransform);
269         if (pair !== undefined) {
270             var a = pair[0], b = pair[1];
271             var sortedDeclarations = sortByKey(imports, function (x) {
272                 return _this.options.namedImportsOrderTransform(x.getText());
273             }).map(function (x) { return x.getText(); });
274             // replace in reverse order to preserve earlier offsets
275             for (var i = imports.length - 1; i >= 0; i--) {
276                 var start = imports[i].getStart();
277                 var length = imports[i].getText().length;
278                 // replace the named imports one at a time to preserve whitespace
279                 this.currentImportsBlock.replaceNamedImports(start, length, sortedDeclarations[i]);
280             }
281             this.lastFix = [];
282             this.addFailure(a.getStart(), b.getEnd(), Rule.NAMED_IMPORTS_UNORDERED, this.lastFix);
283         }
284     };
285     /**
286      * Check all import blocks stopping at the first failure.
287      */
288     Walker.prototype.checkBlocksGrouping = function () {
289         var _this = this;
290         var prevBlockOrder = Number.MIN_SAFE_INTEGER;
291         var addFailingImportDecl = function (decl) {
292             var groupsMsg = _this.options.groups.slice().sort(function (a, b) { return a.order - b.order; })
293                 .map(function (g) { return g.name; })
294                 .join(", ");
295             var msg = Rule.IMPORT_SOURCES_NOT_GROUPED_PREFIX + " " + groupsMsg + ".";
296             _this.addFailureAtNode(decl.node, msg, _this.getGroupOrderReplacements());
297         };
298         var blocksWithContent = this.importsBlocks.filter(function (b) { return b.getImportDeclarations().length > 0; });
299         // Check if each block is out of order
300         for (var _i = 0, blocksWithContent_1 = blocksWithContent; _i < blocksWithContent_1.length; _i++) {
301             var block = blocksWithContent_1[_i];
302             var importDeclarations = block.getImportDeclarations();
303             var blockOrder = importDeclarations[0].group.order;
304             // check if group is out of order
305             if (blockOrder <= prevBlockOrder) {
306                 addFailingImportDecl(importDeclarations[0]);
307                 return;
308             }
309             // check if all declarations have the same order value
310             // and mark the first one that is out of order
311             for (var _a = 0, importDeclarations_1 = importDeclarations; _a < importDeclarations_1.length; _a++) {
312                 var decl = importDeclarations_1[_a];
313                 if (decl.group.order !== blockOrder) {
314                     addFailingImportDecl(decl);
315                     return;
316                 }
317             }
318             prevBlockOrder = blockOrder;
319         }
320     };
321     /**
322      * Return the first import group pattern matching the given import path.
323      */
324     Walker.prototype.getMatchingGroup = function (importPath) {
325         // find the first matching group.
326         for (var _i = 0, _a = this.options.groups; _i < _a.length; _i++) {
327             var group = _a[_i];
328             if (group.match.test(importPath)) {
329                 return group;
330             }
331         }
332         return this.defaultGroup;
333     };
334     /**
335      * Build up replaces to remove all imports and replace with grouped and sorted imports.
336      */
337     Walker.prototype.getGroupOrderReplacements = function () {
338         var _a;
339         // Get all import declarations for all ImportBlocks groups that are not empty
340         var groupedDeclarations = this.importsBlocks
341             .map(function (block) { return block.getImportDeclarations(); })
342             .filter(function (imports) { return imports.length > 0; });
343         var replacements = this.getGroupRemovalReplacements(groupedDeclarations);
344         var allImportDeclarations = (_a = []).concat.apply(_a, groupedDeclarations);
345         var startOffset = allImportDeclarations.length === 0 ? 0 : allImportDeclarations[0].nodeStartOffset;
346         replacements.push(Lint.Replacement.appendText(startOffset, this.getGroupedImports(allImportDeclarations)));
347         return replacements;
348     };
349     /**
350      * Get set of replacements that delete all existing imports.
351      */
352     Walker.prototype.getGroupRemovalReplacements = function (groupedDeclarations) {
353         var _this = this;
354         return groupedDeclarations.map(function (items, index) {
355             var start = items[0].nodeStartOffset;
356             if (index > 0) {
357                 var prevItems = groupedDeclarations[index - 1];
358                 var last = prevItems[prevItems.length - 1];
359                 var textFragment = _this.sourceFile.text.slice(last.nodeEndOffset, start);
360                 if (!/\S/.test(textFragment)) {
361                     // remove whitespace between blocks
362                     start = last.nodeEndOffset;
363                 }
364             }
365             return Lint.Replacement.deleteFromTo(start, items[items.length - 1].nodeEndOffset);
366         });
367     };
368     /**
369      * Get text of new set of grouped and sorted imports as text.
370      */
371     Walker.prototype.getGroupedImports = function (importDeclarations) {
372         // list of all unique order values in sorted order
373         var orderValues = importDeclarations
374             .map(function (decl) { return decl.group.order; })
375             .filter(function (v, i, a) { return a.indexOf(v) === i; })
376             .sort(function (a, b) { return a - b; });
377         return orderValues
378             .map(function (curOrder) {
379             var imports = importDeclarations.filter(function (i) { return i.group.order === curOrder; });
380             return getSortedImportDeclarationsAsText(imports);
381         })
382             .filter(function (text) { return text.length > 0; })
383             .join(this.getEolChar());
384     };
385     /**
386      * Return the type of newline that should be used in the codebase.
387      */
388     Walker.prototype.getEolChar = function () {
389         var lineEnd = this.sourceFile.getLineEndOfPosition(0);
390         var newLine;
391         if (lineEnd > 0) {
392             if (lineEnd > 1 && this.sourceFile.text[lineEnd - 1] === "\r") {
393                 newLine = "\r\n";
394             }
395             else if (this.sourceFile.text[lineEnd] === "\n") {
396                 newLine = "\n";
397             }
398         }
399         return newLine === undefined ? ts.sys.newLine : newLine;
400     };
401     return Walker;
402 }(Lint.AbstractWalker));
403 /**
404  * Wrapper around a set of imports grouped together in a sequence (block)
405  * in the source code.
406  */
407 var ImportsBlock = /** @class */ (function () {
408     function ImportsBlock() {
409         this.importDeclarations = [];
410     }
411     /**
412      * Add a new import declaration to the block
413      */
414     ImportsBlock.prototype.addImportDeclaration = function (sourceFile, node, importPath, group) {
415         var start = this.getStartOffset(node);
416         var end = this.getEndOffset(sourceFile, node);
417         var text = sourceFile.text.substring(start, end);
418         if (start > node.getStart() || end === 0) {
419             // skip block if any statements don't end with a newline to simplify implementation
420             this.importDeclarations = [];
421             return;
422         }
423         this.importDeclarations.push({
424             group: group,
425             importPath: importPath,
426             node: node,
427             nodeEndOffset: end,
428             nodeStartOffset: start,
429             text: text,
430         });
431     };
432     ImportsBlock.prototype.getImportDeclarations = function () {
433         return this.importDeclarations;
434     };
435     /**
436      * Replaces the named imports on the most recent import declaration.
437      * Updates the imports in place so the getReplacement method below can
438      * return full fixes for the entire import block.
439      */
440     ImportsBlock.prototype.replaceNamedImports = function (fileOffset, length, replacement) {
441         var importDeclaration = this.getLastImportDeclaration();
442         if (importDeclaration === undefined) {
443             // nothing to replace. This can happen if the block is skipped
444             return;
445         }
446         var start = fileOffset - importDeclaration.nodeStartOffset;
447         if (start < 0 || start + length > importDeclaration.node.getEnd()) {
448             throw new Error("Unexpected named import position");
449         }
450         var initialText = importDeclaration.text;
451         importDeclaration.text =
452             initialText.substring(0, start) + replacement + initialText.substring(start + length);
453     };
454     /**
455      * Return the source path of the most recently added import.
456      */
457     ImportsBlock.prototype.getLastImportSource = function () {
458         if (this.importDeclarations.length === 0) {
459             return null;
460         }
461         return this.getLastImportDeclaration().importPath;
462     };
463     /**
464      * Return a Lint.Replacement object with ordering fixes for the entire block.
465      */
466     ImportsBlock.prototype.getReplacement = function () {
467         if (this.importDeclarations.length === 0) {
468             return undefined;
469         }
470         var fixedText = getSortedImportDeclarationsAsText(this.importDeclarations);
471         var start = this.importDeclarations[0].nodeStartOffset;
472         var end = this.getLastImportDeclaration().nodeEndOffset;
473         return new Lint.Replacement(start, end - start, fixedText);
474     };
475     // gets the offset immediately after the end of the previous declaration to include comment above
476     ImportsBlock.prototype.getStartOffset = function (node) {
477         if (this.importDeclarations.length === 0) {
478             return node.getStart();
479         }
480         return this.getLastImportDeclaration().nodeEndOffset;
481     };
482     // gets the offset of the end of the import's line, including newline, to include comment to the right
483     ImportsBlock.prototype.getEndOffset = function (sourceFile, node) {
484         return sourceFile.text.indexOf("\n", node.end) + 1;
485     };
486     ImportsBlock.prototype.getLastImportDeclaration = function () {
487         return this.importDeclarations[this.importDeclarations.length - 1];
488     };
489     return ImportsBlock;
490 }());
491 // Convert aBcD --> AbCd
492 function flipCase(str) {
493     return Array.from(str)
494         .map(function (char) {
495         if (char >= "a" && char <= "z") {
496             return char.toUpperCase();
497         }
498         else if (char >= "A" && char <= "Z") {
499             return char.toLowerCase();
500         }
501         return char;
502     })
503         .join("");
504 }
505 // After applying a transformation, are the nodes sorted according to the text they contain?
506 // If not, return the pair of nodes which are out of order.
507 function findUnsortedPair(xs, transform) {
508     for (var i = 1; i < xs.length; i++) {
509         if (transform(xs[i].getText()) < transform(xs[i - 1].getText())) {
510             return [xs[i - 1], xs[i]];
511         }
512     }
513     return undefined;
514 }
515 function compare(a, b) {
516     function isLow(value) {
517         return value[0] === "." || value[0] === "/";
518     }
519     if (isLow(a) && !isLow(b)) {
520         return 1;
521     }
522     else if (!isLow(a) && isLow(b)) {
523         return -1;
524     }
525     else if (a > b) {
526         return 1;
527     }
528     else if (a < b) {
529         return -1;
530     }
531     return 0;
532 }
533 function removeQuotes(value) {
534     // strip out quotes
535     if (value.length > 1 && (value[0] === "'" || value[0] === '"')) {
536         value = value.substr(1, value.length - 2);
537     }
538     return value;
539 }
540 function getSortedImportDeclarationsAsText(importDeclarations) {
541     var sortedDeclarations = sortByKey(importDeclarations.slice(), function (x) { return x.importPath; });
542     return sortedDeclarations.map(function (x) { return x.text; }).join("");
543 }
544 function sortByKey(xs, getSortKey) {
545     return xs.slice().sort(function (a, b) { return compare(getSortKey(a), getSortKey(b)); });
546 }
547 function moduleDeclarationBody(node) {
548     var body = node.body;
549     while (body !== undefined && body.kind === ts.SyntaxKind.ModuleDeclaration) {
550         body = body.body;
551     }
552     return body !== undefined && body.kind === ts.SyntaxKind.ModuleBlock ? body : undefined;
553 }
554 var templateObject_1, templateObject_2;