aboutsummaryrefslogtreecommitdiff
path: root/node_modules/tslint/lib/rules/alignRule.js
blob: 7d70b92299f102db287e8a8c9b842640de51b48f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
"use strict";
/**
 * @license
 * Copyright 2013 Palantir Technologies, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var tsutils_1 = require("tsutils");
var ts = require("typescript");
var Lint = require("../index");
var OPTION_STATEMENTS = "statements";
var OPTION_MEMBERS = "members";
var OPTION_ELEMENTS = "elements";
var OPTION_PARAMETERS = "parameters";
var OPTION_ARGUMENTS = "arguments";
var Rule = (function (_super) {
    tslib_1.__extends(Rule, _super);
    function Rule() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    Rule.prototype.apply = function (sourceFile) {
        return this.applyWithWalker(new AlignWalker(sourceFile, this.ruleName, {
            arguments: this.ruleArguments.indexOf(OPTION_ARGUMENTS) !== -1,
            elements: this.ruleArguments.indexOf(OPTION_ELEMENTS) !== -1,
            members: this.ruleArguments.indexOf(OPTION_MEMBERS) !== -1,
            parameters: this.ruleArguments.indexOf(OPTION_PARAMETERS) !== -1,
            statements: this.ruleArguments.indexOf(OPTION_STATEMENTS) !== -1,
        }));
    };
    /* tslint:disable:object-literal-sort-keys */
    Rule.metadata = {
        ruleName: "align",
        description: "Enforces vertical alignment.",
        hasFix: true,
        rationale: "Helps maintain a readable, consistent style in your codebase.",
        optionsDescription: (_a = ["\n            Five arguments may be optionally provided:\n\n            * `\"", "\"` checks alignment of function parameters.\n            * `\"", "\"` checks alignment of function call arguments.\n            * `\"", "\"` checks alignment of statements.\n            * `\"", "\"` checks alignment of members of classes, interfaces, type literal, object literals and\n            object destructuring.\n            * `\"", "\"` checks alignment of elements of array iterals, array destructuring and tuple types."], _a.raw = ["\n            Five arguments may be optionally provided:\n\n            * \\`\"", "\"\\` checks alignment of function parameters.\n            * \\`\"", "\"\\` checks alignment of function call arguments.\n            * \\`\"", "\"\\` checks alignment of statements.\n            * \\`\"", "\"\\` checks alignment of members of classes, interfaces, type literal, object literals and\n            object destructuring.\n            * \\`\"", "\"\\` checks alignment of elements of array iterals, array destructuring and tuple types."], Lint.Utils.dedent(_a, OPTION_PARAMETERS, OPTION_ARGUMENTS, OPTION_STATEMENTS, OPTION_MEMBERS, OPTION_ELEMENTS)),
        options: {
            type: "array",
            items: {
                type: "string",
                enum: [OPTION_ARGUMENTS, OPTION_ELEMENTS, OPTION_MEMBERS, OPTION_PARAMETERS, OPTION_STATEMENTS],
            },
            minLength: 1,
            maxLength: 5,
        },
        optionExamples: [[true, "parameters", "statements"]],
        type: "style",
        typescriptOnly: false,
    };
    /* tslint:enable:object-literal-sort-keys */
    Rule.FAILURE_STRING_SUFFIX = " are not aligned";
    return Rule;
}(Lint.Rules.AbstractRule));
exports.Rule = Rule;
var AlignWalker = (function (_super) {
    tslib_1.__extends(AlignWalker, _super);
    function AlignWalker() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    AlignWalker.prototype.walk = function (sourceFile) {
        var _this = this;
        var cb = function (node) {
            if (_this.options.statements && tsutils_1.isBlockLike(node)) {
                _this.checkAlignment(node.statements.filter(function (s) { return s.kind !== ts.SyntaxKind.EmptyStatement; }), OPTION_STATEMENTS);
            }
            else {
                switch (node.kind) {
                    case ts.SyntaxKind.NewExpression:
                        if (node.arguments === undefined) {
                            break;
                        }
                    // falls through
                    case ts.SyntaxKind.CallExpression:
                        if (_this.options.arguments) {
                            _this.checkAlignment(node.arguments, OPTION_ARGUMENTS);
                        }
                        break;
                    case ts.SyntaxKind.FunctionDeclaration:
                    case ts.SyntaxKind.FunctionExpression:
                    case ts.SyntaxKind.Constructor:
                    case ts.SyntaxKind.MethodDeclaration:
                    case ts.SyntaxKind.ArrowFunction:
                    case ts.SyntaxKind.CallSignature:
                    case ts.SyntaxKind.ConstructSignature:
                    case ts.SyntaxKind.MethodSignature:
                    case ts.SyntaxKind.FunctionType:
                    case ts.SyntaxKind.ConstructorType:
                        if (_this.options.parameters) {
                            _this.checkAlignment(node.parameters, OPTION_PARAMETERS);
                        }
                        break;
                    case ts.SyntaxKind.ArrayLiteralExpression:
                    case ts.SyntaxKind.ArrayBindingPattern:
                        if (_this.options.elements) {
                            _this.checkAlignment(node.elements, OPTION_ELEMENTS);
                        }
                        break;
                    case ts.SyntaxKind.TupleType:
                        if (_this.options.elements) {
                            _this.checkAlignment(node.elementTypes, OPTION_ELEMENTS);
                        }
                        break;
                    case ts.SyntaxKind.ObjectLiteralExpression:
                        if (_this.options.members) {
                            _this.checkAlignment(node.properties, OPTION_MEMBERS);
                        }
                        break;
                    case ts.SyntaxKind.ObjectBindingPattern:
                        if (_this.options.members) {
                            _this.checkAlignment(node.elements, OPTION_MEMBERS);
                        }
                        break;
                    case ts.SyntaxKind.ClassDeclaration:
                    case ts.SyntaxKind.ClassExpression:
                        if (_this.options.members) {
                            _this.checkAlignment(node.members.filter(function (m) { return m.kind !== ts.SyntaxKind.SemicolonClassElement; }), OPTION_MEMBERS);
                        }
                        break;
                    case ts.SyntaxKind.InterfaceDeclaration:
                    case ts.SyntaxKind.TypeLiteral:
                        if (_this.options.members) {
                            _this.checkAlignment(node.members, OPTION_MEMBERS);
                        }
                }
            }
            return ts.forEachChild(node, cb);
        };
        return cb(sourceFile);
    };
    AlignWalker.prototype.checkAlignment = function (nodes, kind) {
        if (nodes.length <= 1) {
            return;
        }
        var sourceFile = this.sourceFile;
        var pos = getLineAndCharacterWithoutBom(sourceFile, this.getStart(nodes[0]));
        var alignToColumn = pos.character;
        var line = pos.line;
        // skip first node in list
        for (var i = 1; i < nodes.length; ++i) {
            var node = nodes[i];
            var start = this.getStart(node);
            pos = ts.getLineAndCharacterOfPosition(sourceFile, start);
            if (line !== pos.line && pos.character !== alignToColumn) {
                var diff = alignToColumn - pos.character;
                var fix = void 0;
                if (diff >= 0) {
                    fix = Lint.Replacement.appendText(start, " ".repeat(diff));
                }
                else if (node.pos <= start + diff && /^\s+$/.test(sourceFile.text.substring(start + diff, start))) {
                    // only delete text if there is only whitespace
                    fix = Lint.Replacement.deleteText(start + diff, -diff);
                }
                this.addFailure(start, Math.max(node.end, start), kind + Rule.FAILURE_STRING_SUFFIX, fix);
            }
            line = pos.line;
        }
    };
    AlignWalker.prototype.getStart = function (node) {
        return node.kind !== ts.SyntaxKind.OmittedExpression
            ? node.getStart(this.sourceFile)
            : tsutils_1.getNextToken(node, this.sourceFile).getStart(this.sourceFile);
    };
    return AlignWalker;
}(Lint.AbstractWalker));
function getLineAndCharacterWithoutBom(sourceFile, pos) {
    var result = ts.getLineAndCharacterOfPosition(sourceFile, pos);
    if (result.line === 0 && sourceFile.text[0] === "\uFEFF") {
        result.character -= 1;
    }
    return result;
}
var _a;