whitespaceRule.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. "use strict";
  2. var __extends = (this && this.__extends) || function (d, b) {
  3. for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
  4. function __() { this.constructor = d; }
  5. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  6. };
  7. var ts = require("typescript");
  8. var Lint = require("../lint");
  9. var OPTION_BRANCH = "check-branch";
  10. var OPTION_DECL = "check-decl";
  11. var OPTION_OPERATOR = "check-operator";
  12. var OPTION_MODULE = "check-module";
  13. var OPTION_SEPARATOR = "check-separator";
  14. var OPTION_TYPE = "check-type";
  15. var OPTION_TYPECAST = "check-typecast";
  16. var Rule = (function (_super) {
  17. __extends(Rule, _super);
  18. function Rule() {
  19. _super.apply(this, arguments);
  20. }
  21. Rule.prototype.apply = function (sourceFile) {
  22. return this.applyWithWalker(new WhitespaceWalker(sourceFile, this.getOptions()));
  23. };
  24. Rule.FAILURE_STRING = "missing whitespace";
  25. return Rule;
  26. }(Lint.Rules.AbstractRule));
  27. exports.Rule = Rule;
  28. var WhitespaceWalker = (function (_super) {
  29. __extends(WhitespaceWalker, _super);
  30. function WhitespaceWalker(sourceFile, options) {
  31. _super.call(this, sourceFile, options);
  32. this.scanner = ts.createScanner(ts.ScriptTarget.ES5, false, ts.LanguageVariant.Standard, sourceFile.text);
  33. }
  34. WhitespaceWalker.prototype.visitSourceFile = function (node) {
  35. var _this = this;
  36. _super.prototype.visitSourceFile.call(this, node);
  37. var prevTokenShouldBeFollowedByWhitespace = false;
  38. this.scanner.setTextPos(0);
  39. Lint.scanAllTokens(this.scanner, function (scanner) {
  40. var startPos = scanner.getStartPos();
  41. var tokenKind = scanner.getToken();
  42. if (tokenKind === ts.SyntaxKind.WhitespaceTrivia || tokenKind === ts.SyntaxKind.NewLineTrivia) {
  43. prevTokenShouldBeFollowedByWhitespace = false;
  44. }
  45. else if (prevTokenShouldBeFollowedByWhitespace) {
  46. var failure = _this.createFailure(startPos, 1, Rule.FAILURE_STRING);
  47. _this.addFailure(failure);
  48. prevTokenShouldBeFollowedByWhitespace = false;
  49. }
  50. if (_this.tokensToSkipStartEndMap[startPos] != null) {
  51. scanner.setTextPos(_this.tokensToSkipStartEndMap[startPos]);
  52. return;
  53. }
  54. switch (tokenKind) {
  55. case ts.SyntaxKind.CatchKeyword:
  56. case ts.SyntaxKind.ForKeyword:
  57. case ts.SyntaxKind.IfKeyword:
  58. case ts.SyntaxKind.SwitchKeyword:
  59. case ts.SyntaxKind.WhileKeyword:
  60. case ts.SyntaxKind.WithKeyword:
  61. if (_this.hasOption(OPTION_BRANCH)) {
  62. prevTokenShouldBeFollowedByWhitespace = true;
  63. }
  64. break;
  65. case ts.SyntaxKind.CommaToken:
  66. case ts.SyntaxKind.SemicolonToken:
  67. if (_this.hasOption(OPTION_SEPARATOR)) {
  68. prevTokenShouldBeFollowedByWhitespace = true;
  69. }
  70. break;
  71. case ts.SyntaxKind.EqualsToken:
  72. if (_this.hasOption(OPTION_DECL)) {
  73. prevTokenShouldBeFollowedByWhitespace = true;
  74. }
  75. break;
  76. case ts.SyntaxKind.ColonToken:
  77. if (_this.hasOption(OPTION_TYPE)) {
  78. prevTokenShouldBeFollowedByWhitespace = true;
  79. }
  80. break;
  81. case ts.SyntaxKind.ImportKeyword:
  82. case ts.SyntaxKind.ExportKeyword:
  83. case ts.SyntaxKind.FromKeyword:
  84. if (_this.hasOption(OPTION_MODULE)) {
  85. prevTokenShouldBeFollowedByWhitespace = true;
  86. }
  87. break;
  88. }
  89. });
  90. };
  91. WhitespaceWalker.prototype.visitArrowFunction = function (node) {
  92. this.checkEqualsGreaterThanTokenInNode(node);
  93. _super.prototype.visitArrowFunction.call(this, node);
  94. };
  95. WhitespaceWalker.prototype.visitBinaryExpression = function (node) {
  96. if (this.hasOption(OPTION_OPERATOR) && node.operatorToken.kind !== ts.SyntaxKind.CommaToken) {
  97. this.checkForTrailingWhitespace(node.left.getEnd());
  98. this.checkForTrailingWhitespace(node.right.getFullStart());
  99. }
  100. _super.prototype.visitBinaryExpression.call(this, node);
  101. };
  102. WhitespaceWalker.prototype.visitConditionalExpression = function (node) {
  103. if (this.hasOption(OPTION_OPERATOR)) {
  104. this.checkForTrailingWhitespace(node.condition.getEnd());
  105. this.checkForTrailingWhitespace(node.whenTrue.getFullStart());
  106. this.checkForTrailingWhitespace(node.whenTrue.getEnd());
  107. }
  108. _super.prototype.visitConditionalExpression.call(this, node);
  109. };
  110. WhitespaceWalker.prototype.visitConstructorType = function (node) {
  111. this.checkEqualsGreaterThanTokenInNode(node);
  112. _super.prototype.visitConstructorType.call(this, node);
  113. };
  114. WhitespaceWalker.prototype.visitExportAssignment = function (node) {
  115. if (this.hasOption(OPTION_MODULE)) {
  116. var exportKeyword = node.getChildAt(0);
  117. var position = exportKeyword.getEnd();
  118. this.checkForTrailingWhitespace(position);
  119. }
  120. _super.prototype.visitExportAssignment.call(this, node);
  121. };
  122. WhitespaceWalker.prototype.visitFunctionType = function (node) {
  123. this.checkEqualsGreaterThanTokenInNode(node);
  124. _super.prototype.visitFunctionType.call(this, node);
  125. };
  126. WhitespaceWalker.prototype.visitImportDeclaration = function (node) {
  127. var importClause = node.importClause;
  128. if (this.hasOption(OPTION_MODULE) && importClause != null) {
  129. var position = (importClause.namedBindings == null) ? importClause.name.getEnd()
  130. : importClause.namedBindings.getEnd();
  131. this.checkForTrailingWhitespace(position);
  132. }
  133. _super.prototype.visitImportDeclaration.call(this, node);
  134. };
  135. WhitespaceWalker.prototype.visitImportEqualsDeclaration = function (node) {
  136. if (this.hasOption(OPTION_MODULE)) {
  137. var position = node.name.getEnd();
  138. this.checkForTrailingWhitespace(position);
  139. }
  140. _super.prototype.visitImportEqualsDeclaration.call(this, node);
  141. };
  142. WhitespaceWalker.prototype.visitJsxElement = function (node) {
  143. this.addTokenToSkipFromNode(node);
  144. _super.prototype.visitJsxElement.call(this, node);
  145. };
  146. WhitespaceWalker.prototype.visitJsxSelfClosingElement = function (node) {
  147. this.addTokenToSkipFromNode(node);
  148. _super.prototype.visitJsxSelfClosingElement.call(this, node);
  149. };
  150. WhitespaceWalker.prototype.visitTypeAssertionExpression = function (node) {
  151. if (this.hasOption(OPTION_TYPECAST)) {
  152. var position = node.expression.getFullStart();
  153. this.checkForTrailingWhitespace(position);
  154. }
  155. _super.prototype.visitTypeAssertionExpression.call(this, node);
  156. };
  157. WhitespaceWalker.prototype.visitVariableDeclaration = function (node) {
  158. if (this.hasOption(OPTION_DECL) && node.initializer != null) {
  159. if (node.type != null) {
  160. this.checkForTrailingWhitespace(node.type.getEnd());
  161. }
  162. else {
  163. this.checkForTrailingWhitespace(node.name.getEnd());
  164. }
  165. }
  166. _super.prototype.visitVariableDeclaration.call(this, node);
  167. };
  168. WhitespaceWalker.prototype.checkEqualsGreaterThanTokenInNode = function (node) {
  169. var arrowChildNumber = -1;
  170. node.getChildren().forEach(function (child, i) {
  171. if (child.kind === ts.SyntaxKind.EqualsGreaterThanToken) {
  172. arrowChildNumber = i;
  173. }
  174. });
  175. if (arrowChildNumber !== -1) {
  176. var equalsGreaterThanToken = node.getChildAt(arrowChildNumber);
  177. if (this.hasOption(OPTION_OPERATOR)) {
  178. var position = equalsGreaterThanToken.getFullStart();
  179. this.checkForTrailingWhitespace(position);
  180. position = equalsGreaterThanToken.getEnd();
  181. this.checkForTrailingWhitespace(position);
  182. }
  183. }
  184. };
  185. WhitespaceWalker.prototype.checkForTrailingWhitespace = function (position) {
  186. this.scanner.setTextPos(position);
  187. var nextTokenType = this.scanner.scan();
  188. if (nextTokenType !== ts.SyntaxKind.WhitespaceTrivia
  189. && nextTokenType !== ts.SyntaxKind.NewLineTrivia
  190. && nextTokenType !== ts.SyntaxKind.EndOfFileToken) {
  191. this.addFailure(this.createFailure(position, 1, Rule.FAILURE_STRING));
  192. }
  193. };
  194. return WhitespaceWalker;
  195. }(Lint.SkippableTokenAwareRuleWalker));