orderedImportsRule.js 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. "use strict";
  2. /**
  3. * @license
  4. * Copyright 2016 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 these to group imports\n however you like, e.g. by first- vs. third-party or thematically or can you can\n enforce a grouping of third-party, parent directories and the current directory."], ["\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 these to group imports\n however you like, e.g. by first- vs. third-party or thematically or can you can\n enforce a grouping of third-party, parent directories and the current directory."]))),
  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\"`).\n\n Possible values for `\"grouped-imports\"` are:\n\n * `false`: Do not enforce grouping. (This is the default.)\n * `true`: Group source imports by `\"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\"\\`).\n\n Possible values for \\`\"grouped-imports\"\\` are:\n\n * \\`false\\`: Do not enforce grouping. (This is the default.)\n * \\`true\\`: Group source imports by \\`\"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. "import-sources-order": {
  45. type: "string",
  46. enum: ["case-insensitive", "lowercase-first", "lowercase-last", "any"],
  47. },
  48. "named-imports-order": {
  49. type: "string",
  50. enum: ["case-insensitive", "lowercase-first", "lowercase-last", "any"],
  51. },
  52. "module-source-path": {
  53. type: "string",
  54. enum: ["full", "basename"],
  55. },
  56. },
  57. additionalProperties: false,
  58. },
  59. optionExamples: [
  60. true,
  61. [true, { "import-sources-order": "lowercase-last", "named-imports-order": "lowercase-first" }],
  62. ],
  63. type: "style",
  64. typescriptOnly: false,
  65. };
  66. /* tslint:enable:object-literal-sort-keys */
  67. Rule.IMPORT_SOURCES_NOT_GROUPED = "Import sources of different groups must be sorted by: libraries, parent directories, current directory.";
  68. Rule.IMPORT_SOURCES_UNORDERED = "Import sources within a group must be alphabetized.";
  69. Rule.NAMED_IMPORTS_UNORDERED = "Named imports must be alphabetized.";
  70. return Rule;
  71. }(Lint.Rules.AbstractRule));
  72. exports.Rule = Rule;
  73. var TRANSFORMS = new Map([
  74. ["any", function () { return ""; }],
  75. ["case-insensitive", function (x) { return x.toLowerCase(); }],
  76. ["lowercase-first", flipCase],
  77. ["lowercase-last", function (x) { return x; }],
  78. ["full", function (x) { return x; }],
  79. ["basename", function (x) {
  80. if (!ts.isExternalModuleNameRelative(x)) {
  81. return x;
  82. }
  83. var splitIndex = x.lastIndexOf("/");
  84. if (splitIndex === -1) {
  85. return x;
  86. }
  87. return x.substr(splitIndex + 1);
  88. }],
  89. ]);
  90. var ImportType;
  91. (function (ImportType) {
  92. ImportType[ImportType["LIBRARY_IMPORT"] = 1] = "LIBRARY_IMPORT";
  93. ImportType[ImportType["PARENT_DIRECTORY_IMPORT"] = 2] = "PARENT_DIRECTORY_IMPORT";
  94. ImportType[ImportType["CURRENT_DIRECTORY_IMPORT"] = 3] = "CURRENT_DIRECTORY_IMPORT";
  95. })(ImportType || (ImportType = {}));
  96. function parseOptions(ruleArguments) {
  97. var optionSet = ruleArguments[0];
  98. 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;
  99. return {
  100. groupedImports: isGrouped,
  101. importSourcesOrderTransform: TRANSFORMS.get(sources),
  102. moduleSourcePath: TRANSFORMS.get(path),
  103. namedImportsOrderTransform: TRANSFORMS.get(named),
  104. };
  105. }
  106. var Walker = /** @class */ (function (_super) {
  107. tslib_1.__extends(Walker, _super);
  108. function Walker() {
  109. var _this = _super !== null && _super.apply(this, arguments) || this;
  110. _this.importsBlocks = [new ImportsBlock()];
  111. _this.nextType = ImportType.LIBRARY_IMPORT;
  112. return _this;
  113. }
  114. Object.defineProperty(Walker.prototype, "currentImportsBlock", {
  115. get: function () {
  116. return this.importsBlocks[this.importsBlocks.length - 1];
  117. },
  118. enumerable: true,
  119. configurable: true
  120. });
  121. Walker.prototype.walk = function (sourceFile) {
  122. for (var _i = 0, _a = sourceFile.statements; _i < _a.length; _i++) {
  123. var statement = _a[_i];
  124. this.checkStatement(statement);
  125. }
  126. this.endBlock();
  127. if (this.options.groupedImports) {
  128. this.checkBlocksGrouping();
  129. }
  130. };
  131. Walker.prototype.checkStatement = function (statement) {
  132. if (!(tsutils_1.isImportDeclaration(statement) || tsutils_1.isImportEqualsDeclaration(statement)) ||
  133. /\r?\n\r?\n/.test(this.sourceFile.text.slice(statement.getFullStart(), statement.getStart(this.sourceFile)))) {
  134. this.endBlock();
  135. }
  136. if (tsutils_1.isImportDeclaration(statement)) {
  137. this.checkImportDeclaration(statement);
  138. }
  139. else if (tsutils_1.isImportEqualsDeclaration(statement)) {
  140. this.checkImportEqualsDeclaration(statement);
  141. }
  142. else if (tsutils_1.isModuleDeclaration(statement)) {
  143. var body = moduleDeclarationBody(statement);
  144. if (body !== undefined) {
  145. for (var _i = 0, _a = body.statements; _i < _a.length; _i++) {
  146. var subStatement = _a[_i];
  147. this.checkStatement(subStatement);
  148. }
  149. this.endBlock();
  150. }
  151. }
  152. };
  153. Walker.prototype.checkImportDeclaration = function (node) {
  154. if (!tsutils_1.isStringLiteral(node.moduleSpecifier)) {
  155. // Ignore grammar error
  156. return;
  157. }
  158. var source = this.options.importSourcesOrderTransform(removeQuotes(node.moduleSpecifier.text));
  159. this.checkSource(source, node);
  160. var importClause = node.importClause;
  161. if (importClause !== undefined && importClause.namedBindings !== undefined && tsutils_1.isNamedImports(importClause.namedBindings)) {
  162. this.checkNamedImports(importClause.namedBindings);
  163. }
  164. };
  165. Walker.prototype.checkImportEqualsDeclaration = function (node) {
  166. // only allowed `import x = require('y');`
  167. var moduleReference = node.moduleReference;
  168. if (!tsutils_1.isExternalModuleReference(moduleReference)) {
  169. return;
  170. }
  171. var expression = moduleReference.expression;
  172. if (expression === undefined || !tsutils_1.isStringLiteral(expression)) {
  173. return;
  174. }
  175. var source = this.options.importSourcesOrderTransform(removeQuotes(expression.text));
  176. this.checkSource(source, node);
  177. };
  178. Walker.prototype.checkSource = function (source, node) {
  179. var currentSource = this.options.moduleSourcePath(source);
  180. var previousSource = this.currentImportsBlock.getLastImportSource();
  181. this.currentImportsBlock.addImportDeclaration(this.sourceFile, node, currentSource);
  182. if (previousSource !== null && compare(currentSource, previousSource) === -1) {
  183. this.lastFix = [];
  184. this.addFailureAtNode(node, Rule.IMPORT_SOURCES_UNORDERED, this.lastFix);
  185. }
  186. };
  187. Walker.prototype.endBlock = function () {
  188. if (this.lastFix !== undefined) {
  189. var replacement = this.currentImportsBlock.getReplacement();
  190. if (replacement !== undefined) {
  191. this.lastFix.push(replacement);
  192. }
  193. this.lastFix = undefined;
  194. }
  195. this.importsBlocks.push(new ImportsBlock());
  196. };
  197. Walker.prototype.checkNamedImports = function (node) {
  198. var _this = this;
  199. var imports = node.elements;
  200. var pair = findUnsortedPair(imports, this.options.namedImportsOrderTransform);
  201. if (pair !== undefined) {
  202. var a = pair[0], b = pair[1];
  203. var sortedDeclarations = sortByKey(imports, function (x) {
  204. return _this.options.namedImportsOrderTransform(x.getText());
  205. }).map(function (x) { return x.getText(); });
  206. // replace in reverse order to preserve earlier offsets
  207. for (var i = imports.length - 1; i >= 0; i--) {
  208. var start = imports[i].getStart();
  209. var length = imports[i].getText().length;
  210. // replace the named imports one at a time to preserve whitespace
  211. this.currentImportsBlock.replaceNamedImports(start, length, sortedDeclarations[i]);
  212. }
  213. this.lastFix = [];
  214. this.addFailure(a.getStart(), b.getEnd(), Rule.NAMED_IMPORTS_UNORDERED, this.lastFix);
  215. }
  216. };
  217. Walker.prototype.checkBlocksGrouping = function () {
  218. this.importsBlocks.some(this.checkBlockGroups, this);
  219. };
  220. Walker.prototype.checkBlockGroups = function (importsBlock) {
  221. var oddImportDeclaration = this.getOddImportDeclaration(importsBlock);
  222. if (oddImportDeclaration !== undefined) {
  223. this.addFailureAtNode(oddImportDeclaration.node, Rule.IMPORT_SOURCES_NOT_GROUPED, this.getReplacements());
  224. return true;
  225. }
  226. return false;
  227. };
  228. Walker.prototype.getOddImportDeclaration = function (importsBlock) {
  229. var importDeclarations = importsBlock.getImportDeclarations();
  230. if (importDeclarations.length === 0) {
  231. return undefined;
  232. }
  233. var type = importDeclarations[0].type;
  234. if (type < this.nextType) {
  235. return importDeclarations[0];
  236. }
  237. else {
  238. this.nextType = type;
  239. return importDeclarations.find(function (importDeclaration) { return importDeclaration.type !== type; });
  240. }
  241. };
  242. Walker.prototype.getReplacements = function () {
  243. var importDeclarationsList = this.importsBlocks
  244. .map(function (block) { return block.getImportDeclarations(); })
  245. .filter(function (imports) { return imports.length > 0; });
  246. var allImportDeclarations = (_a = []).concat.apply(_a, importDeclarationsList);
  247. var replacements = this.getReplacementsForExistingImports(importDeclarationsList);
  248. var startOffset = allImportDeclarations.length === 0 ? 0 : allImportDeclarations[0].nodeStartOffset;
  249. replacements.push(Lint.Replacement.appendText(startOffset, this.getGroupedImports(allImportDeclarations)));
  250. return replacements;
  251. var _a;
  252. };
  253. Walker.prototype.getReplacementsForExistingImports = function (importDeclarationsList) {
  254. var _this = this;
  255. return importDeclarationsList.map(function (items, index) {
  256. var start = items[0].nodeStartOffset;
  257. if (index > 0) {
  258. var prevItems = importDeclarationsList[index - 1];
  259. var last = prevItems[prevItems.length - 1];
  260. if (/[\r\n]+/.test(_this.sourceFile.text.slice(last.nodeEndOffset, start))) {
  261. // remove whitespace between blocks
  262. start = last.nodeEndOffset;
  263. }
  264. }
  265. return Lint.Replacement.deleteFromTo(start, items[items.length - 1].nodeEndOffset);
  266. });
  267. };
  268. Walker.prototype.getGroupedImports = function (importDeclarations) {
  269. return [ImportType.LIBRARY_IMPORT, ImportType.PARENT_DIRECTORY_IMPORT, ImportType.CURRENT_DIRECTORY_IMPORT]
  270. .map(function (type) {
  271. var imports = importDeclarations.filter(function (importDeclaration) { return importDeclaration.type === type; });
  272. return getSortedImportDeclarationsAsText(imports);
  273. })
  274. .filter(function (text) { return text.length > 0; })
  275. .join(this.getEolChar());
  276. };
  277. Walker.prototype.getEolChar = function () {
  278. var lineEnd = this.sourceFile.getLineEndOfPosition(0);
  279. var newLine;
  280. if (lineEnd > 0) {
  281. if (lineEnd > 1 && this.sourceFile.text[lineEnd - 1] === "\r") {
  282. newLine = "\r\n";
  283. }
  284. else if (this.sourceFile.text[lineEnd] === "\n") {
  285. newLine = "\n";
  286. }
  287. }
  288. return newLine === undefined ? ts.sys.newLine : newLine;
  289. };
  290. return Walker;
  291. }(Lint.AbstractWalker));
  292. var ImportsBlock = /** @class */ (function () {
  293. function ImportsBlock() {
  294. this.importDeclarations = [];
  295. }
  296. ImportsBlock.prototype.addImportDeclaration = function (sourceFile, node, sourcePath) {
  297. var start = this.getStartOffset(node);
  298. var end = this.getEndOffset(sourceFile, node);
  299. var text = sourceFile.text.substring(start, end);
  300. var type = this.getImportType(sourcePath);
  301. if (start > node.getStart() || end === 0) {
  302. // skip block if any statements don't end with a newline to simplify implementation
  303. this.importDeclarations = [];
  304. return;
  305. }
  306. this.importDeclarations.push({
  307. node: node,
  308. nodeEndOffset: end,
  309. nodeStartOffset: start,
  310. sourcePath: sourcePath,
  311. text: text,
  312. type: type,
  313. });
  314. };
  315. ImportsBlock.prototype.getImportDeclarations = function () {
  316. return this.importDeclarations;
  317. };
  318. // replaces the named imports on the most recent import declaration
  319. ImportsBlock.prototype.replaceNamedImports = function (fileOffset, length, replacement) {
  320. var importDeclaration = this.getLastImportDeclaration();
  321. if (importDeclaration === undefined) {
  322. // nothing to replace. This can happen if the block is skipped
  323. return;
  324. }
  325. var start = fileOffset - importDeclaration.nodeStartOffset;
  326. if (start < 0 || start + length > importDeclaration.node.getEnd()) {
  327. throw new Error("Unexpected named import position");
  328. }
  329. var initialText = importDeclaration.text;
  330. importDeclaration.text = initialText.substring(0, start) + replacement + initialText.substring(start + length);
  331. };
  332. ImportsBlock.prototype.getLastImportSource = function () {
  333. if (this.importDeclarations.length === 0) {
  334. return null;
  335. }
  336. return this.getLastImportDeclaration().sourcePath;
  337. };
  338. // creates a Lint.Replacement object with ordering fixes for the entire block
  339. ImportsBlock.prototype.getReplacement = function () {
  340. if (this.importDeclarations.length === 0) {
  341. return undefined;
  342. }
  343. var fixedText = getSortedImportDeclarationsAsText(this.importDeclarations);
  344. var start = this.importDeclarations[0].nodeStartOffset;
  345. var end = this.getLastImportDeclaration().nodeEndOffset;
  346. return new Lint.Replacement(start, end - start, fixedText);
  347. };
  348. // gets the offset immediately after the end of the previous declaration to include comment above
  349. ImportsBlock.prototype.getStartOffset = function (node) {
  350. if (this.importDeclarations.length === 0) {
  351. return node.getStart();
  352. }
  353. return this.getLastImportDeclaration().nodeEndOffset;
  354. };
  355. // gets the offset of the end of the import's line, including newline, to include comment to the right
  356. ImportsBlock.prototype.getEndOffset = function (sourceFile, node) {
  357. return sourceFile.text.indexOf("\n", node.end) + 1;
  358. };
  359. ImportsBlock.prototype.getLastImportDeclaration = function () {
  360. return this.importDeclarations[this.importDeclarations.length - 1];
  361. };
  362. ImportsBlock.prototype.getImportType = function (sourcePath) {
  363. if (sourcePath.charAt(0) === ".") {
  364. if (sourcePath.charAt(1) === ".") {
  365. return ImportType.PARENT_DIRECTORY_IMPORT;
  366. }
  367. else {
  368. return ImportType.CURRENT_DIRECTORY_IMPORT;
  369. }
  370. }
  371. else {
  372. return ImportType.LIBRARY_IMPORT;
  373. }
  374. };
  375. return ImportsBlock;
  376. }());
  377. // Convert aBcD --> AbCd
  378. function flipCase(str) {
  379. return Array.from(str).map(function (char) {
  380. if (char >= "a" && char <= "z") {
  381. return char.toUpperCase();
  382. }
  383. else if (char >= "A" && char <= "Z") {
  384. return char.toLowerCase();
  385. }
  386. return char;
  387. }).join("");
  388. }
  389. // After applying a transformation, are the nodes sorted according to the text they contain?
  390. // If not, return the pair of nodes which are out of order.
  391. function findUnsortedPair(xs, transform) {
  392. for (var i = 1; i < xs.length; i++) {
  393. if (transform(xs[i].getText()) < transform(xs[i - 1].getText())) {
  394. return [xs[i - 1], xs[i]];
  395. }
  396. }
  397. return undefined;
  398. }
  399. function compare(a, b) {
  400. function isLow(value) {
  401. return value[0] === "." || value[0] === "/";
  402. }
  403. if (isLow(a) && !isLow(b)) {
  404. return 1;
  405. }
  406. else if (!isLow(a) && isLow(b)) {
  407. return -1;
  408. }
  409. else if (a > b) {
  410. return 1;
  411. }
  412. else if (a < b) {
  413. return -1;
  414. }
  415. return 0;
  416. }
  417. function removeQuotes(value) {
  418. // strip out quotes
  419. if (value.length > 1 && (value[0] === "'" || value[0] === "\"")) {
  420. value = value.substr(1, value.length - 2);
  421. }
  422. return value;
  423. }
  424. function getSortedImportDeclarationsAsText(importDeclarations) {
  425. var sortedDeclarations = sortByKey(importDeclarations.slice(), function (x) { return x.sourcePath; });
  426. return sortedDeclarations.map(function (x) { return x.text; }).join("");
  427. }
  428. function sortByKey(xs, getSortKey) {
  429. return xs.slice().sort(function (a, b) { return compare(getSortKey(a), getSortKey(b)); });
  430. }
  431. function moduleDeclarationBody(node) {
  432. var body = node.body;
  433. while (body !== undefined && body.kind === ts.SyntaxKind.ModuleDeclaration) {
  434. body = body.body;
  435. }
  436. return body !== undefined && body.kind === ts.SyntaxKind.ModuleBlock ? body : undefined;
  437. }
  438. var templateObject_1, templateObject_2;