UNPKG

6.56 kBJavaScriptView Raw
1/**
2 * @license
3 * Copyright 2016 Palantir Technologies, Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17"use strict";
18var __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};
23var Lint = require("tslint");
24var 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));
35Rule.ATTR_LINE_FAILURE = "JSX attributes must be on a line below the opening tag";
36Rule.ATTR_INDENT_FAILURE = "JSX attributes must be indented further than the opening tag statement";
37Rule.ATTR_ALIGN_FAILURE = "JSX attributes must be on their own line and vertically aligned";
38Rule.TAG_CLOSE_FAILURE = "Tag closing must be on its own line and aligned with opening of tag";
39Rule.CLOSING_TAG_FAILURE = "Closing tag must be on its own line and aligned with opening tag";
40exports.Rule = Rule;
41var leadingWhitespaceRegex = /[ \t]/;
42var 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 // in a line like "const element = <Foo",
71 // we want the initial indent to be the start of "const" instead of the start of "<Foo"
72 var initialIndent = this.getFirstNonWhitespaceCharacter(elementOpen.line);
73 var firstAttr = attributes[0];
74 var firstAttrCharacter = this.getCharacter(firstAttr);
75 // ensure that first attribute is not on the same line as the start of the tag
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 // ensure each attribute is indented further than the start of the tag
84 if (character <= initialIndent) {
85 this.reportFailure(attr, Rule.ATTR_INDENT_FAILURE);
86 }
87 // ensure each attribute is indented equally
88 if (attr !== firstAttr && character !== firstAttrCharacter) {
89 this.reportFailure(attr, Rule.ATTR_ALIGN_FAILURE);
90 }
91 lastSeenLine = this.getLine(attr);
92 }
93 // ensure that the closing token of the tag with attributes is on its own line
94 // and that it is indented the same as the opening
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 // ensure closing tag is on its own line and aligned with the opening tag
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));