1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | "use strict";
|
18 | var __extends = (this && this.__extends) || function (d, b) {
|
19 | for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
|
20 | function __() { this.constructor = d; }
|
21 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
22 | };
|
23 | var Lint = require("tslint");
|
24 | var Rule = (function (_super) {
|
25 | __extends(Rule, _super);
|
26 | function Rule() {
|
27 | return _super.apply(this, arguments) || this;
|
28 | }
|
29 | Rule.prototype.apply = function (sourceFile) {
|
30 | var walker = new JsxAlignmentWalker(sourceFile, this.getOptions());
|
31 | return this.applyWithWalker(walker);
|
32 | };
|
33 | return Rule;
|
34 | }(Lint.Rules.AbstractRule));
|
35 | Rule.ATTR_LINE_FAILURE = "JSX attributes must be on a line below the opening tag";
|
36 | Rule.ATTR_INDENT_FAILURE = "JSX attributes must be indented further than the opening tag statement";
|
37 | Rule.ATTR_ALIGN_FAILURE = "JSX attributes must be on their own line and vertically aligned";
|
38 | Rule.TAG_CLOSE_FAILURE = "Tag closing must be on its own line and aligned with opening of tag";
|
39 | Rule.CLOSING_TAG_FAILURE = "Closing tag must be on its own line and aligned with opening tag";
|
40 | exports.Rule = Rule;
|
41 | var leadingWhitespaceRegex = /[ \t]/;
|
42 | var JsxAlignmentWalker = (function (_super) {
|
43 | __extends(JsxAlignmentWalker, _super);
|
44 | function JsxAlignmentWalker() {
|
45 | var _this = _super.apply(this, arguments) || this;
|
46 | _this.getCharacter = function (node) { return _this.getLineAndCharacter(node).character; };
|
47 | _this.getLine = function (node) { return _this.getLineAndCharacter(node).line; };
|
48 | return _this;
|
49 | }
|
50 | JsxAlignmentWalker.prototype.visitJsxElement = function (node) {
|
51 | if (this.isMultiline(node.openingElement)) {
|
52 | var startLocation = this.getLineAndCharacter(node);
|
53 | var closeLocation = this.getSourceFile().getLineAndCharacterOfPosition(node.openingElement.getEnd() - ">".length);
|
54 | this.checkElement(startLocation, node.openingElement.attributes, closeLocation, node.closingElement);
|
55 | }
|
56 | _super.prototype.visitJsxElement.call(this, node);
|
57 | };
|
58 | JsxAlignmentWalker.prototype.visitJsxSelfClosingElement = function (node) {
|
59 | if (this.isMultiline(node)) {
|
60 | var startLocation = this.getLineAndCharacter(node);
|
61 | var closeLocation = this.getSourceFile().getLineAndCharacterOfPosition(node.getEnd() - "/>".length);
|
62 | this.checkElement(startLocation, node.attributes, closeLocation);
|
63 | }
|
64 | _super.prototype.visitJsxSelfClosingElement.call(this, node);
|
65 | };
|
66 | JsxAlignmentWalker.prototype.checkElement = function (elementOpen, attributes, elementClose, closingTag) {
|
67 | if (attributes == null || attributes.length === 0) {
|
68 | return;
|
69 | }
|
70 |
|
71 |
|
72 | var initialIndent = this.getFirstNonWhitespaceCharacter(elementOpen.line);
|
73 | var firstAttr = attributes[0];
|
74 | var firstAttrCharacter = this.getCharacter(firstAttr);
|
75 |
|
76 | if (this.getLine(firstAttr) === elementOpen.line) {
|
77 | this.reportFailure(firstAttr, Rule.ATTR_LINE_FAILURE);
|
78 | }
|
79 | var lastSeenLine = -1;
|
80 | for (var _i = 0, attributes_1 = attributes; _i < attributes_1.length; _i++) {
|
81 | var attr = attributes_1[_i];
|
82 | var character = this.getCharacter(attr);
|
83 |
|
84 | if (character <= initialIndent) {
|
85 | this.reportFailure(attr, Rule.ATTR_INDENT_FAILURE);
|
86 | }
|
87 |
|
88 | if (attr !== firstAttr && character !== firstAttrCharacter) {
|
89 | this.reportFailure(attr, Rule.ATTR_ALIGN_FAILURE);
|
90 | }
|
91 | lastSeenLine = this.getLine(attr);
|
92 | }
|
93 |
|
94 |
|
95 | if (lastSeenLine === elementClose.line || elementClose.character !== initialIndent) {
|
96 | this.addFailure(this.createFailure(this.getSourceFile().getPositionOfLineAndCharacter(elementClose.line, elementClose.character), 1, Rule.TAG_CLOSE_FAILURE));
|
97 | }
|
98 |
|
99 | if (closingTag != null) {
|
100 | var closingTagLocation = this.getLineAndCharacter(closingTag);
|
101 | if (closingTagLocation.line <= elementClose.line || closingTagLocation.character !== initialIndent) {
|
102 | this.reportFailure(closingTag, Rule.CLOSING_TAG_FAILURE);
|
103 | }
|
104 | }
|
105 | };
|
106 | JsxAlignmentWalker.prototype.getFirstNonWhitespaceCharacter = function (line) {
|
107 | var lineStart = this.getSourceFile().getLineStarts()[line];
|
108 | var source = this.getSourceFile().getFullText();
|
109 | var width = 0;
|
110 | while (lineStart + width < source.length && leadingWhitespaceRegex.test(source.charAt(lineStart + width))) {
|
111 | width++;
|
112 | }
|
113 | return width;
|
114 | };
|
115 | JsxAlignmentWalker.prototype.isMultiline = function (node) {
|
116 | var startLine = this.getLine(node);
|
117 | var endLine = this.getSourceFile().getLineAndCharacterOfPosition(node.getEnd()).line;
|
118 | return startLine !== endLine;
|
119 | };
|
120 | JsxAlignmentWalker.prototype.getLineAndCharacter = function (node) {
|
121 | var sourceFile = this.getSourceFile();
|
122 | return sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
123 | };
|
124 | JsxAlignmentWalker.prototype.reportFailure = function (node, message) {
|
125 | this.addFailure(this.createFailure(node.getStart(), node.getWidth(), message));
|
126 | };
|
127 | return JsxAlignmentWalker;
|
128 | }(Lint.RuleWalker));
|