UNPKG

6.81 kBJavaScriptView Raw
1/**
2 * Requires all lines to be at most the number of characters specified
3 *
4 * Types: `Integer` or `Object`
5 *
6 * Values:
7 * - `Integer`: lines should be at most the number of characters specified
8 * - `Object`:
9 * - `value`: (required) lines should be at most the number of characters specified
10 * - `tabSize`: (default: `1`) considered the tab character as number of specified spaces
11 * - `allExcept`: (default: `[]`) an array of conditions that will exempt a line
12 * - `regex`: allows regular expression literals to break the rule
13 * - `comments`: allows comments to break the rule
14 * - `urlComments`: allows comments with long urls to break the rule
15 * - `functionSignature`: allows function definitions to break the rule
16 * - `require`: allows require expressions to break the rule
17 * - `allowRegex`: *deprecated* use `allExcept: ["regex"]` instead
18 * - `allowComments`: *deprecated* use `allExcept: ["comments"]` instead
19 * - `allowUrlComments`: *deprecated* use `allExcept: ["urlComments"]` instead
20 *
21 * JSHint: [`maxlen`](http://jshint.com/docs/options/#maxlen)
22 *
23 * #### Example
24 *
25 * ```js
26 * "maximumLineLength": 40
27 * ```
28 *
29 * ##### Valid
30 *
31 * ```js
32 * var aLineOf40Chars = 123456789012345678;
33 * ```
34 *
35 * ##### Invalid
36 *
37 * ```js
38 * var aLineOf41Chars = 1234567890123456789;
39 * ```
40 *
41 * #### Example for allExcept functionSignature
42 *
43 * ```js
44 * "maximumLineLength": { "value": 40, "allExcept": [ "functionSignature" ] }
45 * ```
46 *
47 * ##### Valid
48 *
49 * ```js
50 * var f = function(with, many, _many_, arguments) { .... };
51 * let f = x => x * x * x * x * x * x * x * x;
52 * (function(foo, bar, baz, quux, cuttlefish) {
53 * function namesNaamesNaaamesNaaaames() {
54 * ...
55 * }
56 * })();
57 * const longNameIgnoredAsWell = (a, b) => a * b;
58 * class X { myLongMethodName(withPossiblyManyArgs) { ... } };
59 * ```
60 *
61 * ##### Invalid
62 *
63 * ```js
64 * function x() { // valid
65 * return "function_bodies_are_not_protected";
66 * }
67 * ```
68 */
69
70var assert = require('assert');
71
72module.exports = function() {};
73
74module.exports.prototype = {
75
76 configure: function(maximumLineLength) {
77 this._tabSize = '';
78 this._allowRegex = false;
79 this._allowComments = false;
80 this._allowUrlComments = false;
81 this._allowRequire = false;
82
83 if (typeof maximumLineLength === 'object') {
84 assert(
85 typeof maximumLineLength.value === 'number',
86 this.getOptionName() + ' option requires the "value" property to be defined'
87 );
88
89 this._maximumLineLength = maximumLineLength.value;
90 var tabSize = maximumLineLength.tabSize || 0;
91
92 while (tabSize--) {
93 this._tabSize += ' ';
94 }
95
96 var exceptions = maximumLineLength.allExcept || [];
97 this._allowRegex = (exceptions.indexOf('regex') !== -1);
98 this._allowComments = (exceptions.indexOf('comments') !== -1);
99 this._allowUrlComments = (exceptions.indexOf('urlComments') !== -1);
100 this._allowFunctionSignature = (exceptions.indexOf('functionSignature') !== -1);
101 this._allowRequire = (exceptions.indexOf('require') !== -1);
102
103 if (maximumLineLength.hasOwnProperty('allowRegex')) {
104 this._allowRegex = (maximumLineLength.allowRegex === true);
105 }
106 if (maximumLineLength.hasOwnProperty('allowComments')) {
107 this._allowComments = (maximumLineLength.allowComments === true);
108 }
109 if (maximumLineLength.hasOwnProperty('allowUrlComments')) {
110 this._allowUrlComments = (maximumLineLength.allowUrlComments === true);
111 }
112
113 } else {
114 assert(
115 typeof maximumLineLength === 'number',
116 this.getOptionName() + ' option requires number value or options object'
117 );
118
119 this._maximumLineLength = maximumLineLength;
120 }
121 },
122
123 getOptionName: function() {
124 return 'maximumLineLength';
125 },
126
127 check: function(file, errors) {
128 var maximumLineLength = this._maximumLineLength;
129
130 var line;
131 var lines = this._allowComments ?
132 file.getLinesWithCommentsRemoved() : file.getLines();
133
134 // This check should not be destructive
135 lines = lines.slice();
136
137 var removeLoc = function(tokenOrNode) {
138
139 // Just in case (See #2107 for example)
140 if (!tokenOrNode) {
141 return;
142 }
143
144 for (var i = tokenOrNode.getLoc().start.line; i <= tokenOrNode.getLoc().end.line; i++) {
145 lines[i - 1] = '';
146 }
147 };
148
149 if (this._allowRegex) {
150 file.iterateTokensByType('RegularExpression', function(token) {
151 removeLoc(token);
152 });
153 }
154
155 if (this._allowUrlComments) {
156 file.iterateTokensByType(['CommentLine', 'CommentBlock'], function(comment) {
157 for (var i = comment.getLoc().start.line; i <= comment.getLoc().end.line; i++) {
158 lines[i - 1] = lines[i - 1].replace(/(http|https|ftp):\/\/[^\s$]+/, '');
159 }
160 });
161 }
162
163 if (this._allowFunctionSignature) {
164 file.iterateNodesByType('FunctionDeclaration', function(node) {
165
166 // Need to remove the first line, because we can't be sure there's any id or params
167 lines[node.getLoc().start.line - 1] = '';
168 removeLoc(node.id);
169 node.params.forEach(removeLoc);
170 });
171
172 file.iterateNodesByType('ClassMethod', function(node) {
173 removeLoc(node.key);
174 });
175
176 file.iterateNodesByType(['ArrowFunctionExpression', 'FunctionExpression'], function(node) {
177
178 // Need to remove the first line, because we can't be sure there's any id or params
179 lines[node.getLoc().start.line - 1] = '';
180 removeLoc(node.id);
181 node.params.forEach(removeLoc);
182 });
183 }
184
185 if (this._allowRequire) {
186 file.iterateNodesByType('CallExpression', function(node) {
187 if (node.callee.name === 'require') {
188 removeLoc(node);
189 }
190 });
191 }
192
193 for (var i = 0, l = lines.length; i < l; i++) {
194 line = this._tabSize ? lines[i].replace(/\t/g, this._tabSize) : lines[i];
195
196 if (line.length > maximumLineLength) {
197 errors.add(
198 'Line must be at most ' + maximumLineLength + ' characters',
199 file.getLastTokenOnLine(i + 1, { includeComments: true })
200 );
201 }
202 }
203 }
204
205};