Skip to content

Commit

Permalink
New: max-lines rule (fixes #6078)
Browse files Browse the repository at this point in the history
  • Loading branch information
alberto committed Jun 8, 2016
1 parent f804397 commit e426bc2
Show file tree
Hide file tree
Showing 5 changed files with 447 additions and 0 deletions.
1 change: 1 addition & 0 deletions conf/eslint.json
Expand Up @@ -166,6 +166,7 @@
"lines-around-comment": "off",
"max-depth": "off",
"max-len": "off",
"max-lines": "off",
"max-nested-callbacks": "off",
"max-params": "off",
"max-statements": "off",
Expand Down
1 change: 1 addition & 0 deletions docs/rules/README.md
Expand Up @@ -174,6 +174,7 @@ These rules relate to style guidelines, and are therefore quite subjective:
* [lines-around-comment](lines-around-comment.md): require empty lines around comments
* [max-depth](max-depth.md): enforce a maximum depth that blocks can be nested
* [max-len](max-len.md): enforce a maximum line length
* [max-lines](max-lines.md): enforce a maximum file length
* [max-nested-callbacks](max-nested-callbacks.md): enforce a maximum depth that callbacks can be nested
* [max-params](max-params.md): enforce a maximum number of parameters in `function` definitions
* [max-statements](max-statements.md): enforce a maximum number of statements allowed in `function` blocks
Expand Down
121 changes: 121 additions & 0 deletions docs/rules/max-lines.md
@@ -0,0 +1,121 @@
# enforce a maximum file length (max-lines)

Some people consider large files a code smell. Large files tend to do a lot of things and can make following what's going on hard.

## Rule Details

This rule enforces a maximum number of lines per file, in order to aid in maintainability and reduce complexity.


## Options

This rule has a number or object option:

* `"max"` (default `300`) enforces a maximum number of lines in a file

* `"skipBlankLines": true` ignore lines made up purely of whitespace.

* `"skipComment": true` ignore lines containing just comments

### code

Examples of **incorrect** code for this rule with a max value of `2`:

```js
/*eslint max-lines: ["error", 2]*/
var a,
b,
c;
```

```js
/*eslint max-lines: ["error", 2]*/

var a,
b,c;
```

```js
/*eslint max-lines: ["error", 2]*/
// a comment
var a,
b,c;
```

Examples of **correct** code for this rule with a max value of `2`:

```js
/*eslint max-lines: ["error", 2]*/
var a,
b, c;
```

```js
/*eslint max-lines: ["error", 2]*/

var a, b, c;
```

```js
/*eslint max-lines: ["error", 2]*/
// a comment
var a, b, c;
```

### skipBlankLines

Examples of **incorrect** code for this rule with the `{ "skipBlankLines": true }` option:

```js
/*eslint max-lines: ["error", 2, {"skipBlankLines": true}]*/

var a,
b,
c;
```

Examples of **correct** code for this rule with the `{ "skipBlankLines": true }` option:

```js
/*eslint max-lines: ["error", 2, {"skipBlankLines": true}]*/

var a,
b, c;
```

### skipComments

Examples of **incorrect** code for this rule with the `{ "skipComments": true }` option:

```js
/*eslint max-lines: ["error", 2, {"skipComments": true}]*/
// a comment
var a,
b,
c;
```

Examples of **correct** code for this rule with the `{ "skipComments": true }` option:

```js
/*eslint max-lines: ["error", 2, {"skipComments": true}]*/
// a comment
var a,
b, c;
```

## When Not To Use It

You can turn this rule off if you are not concerned with the number of lines in your files.

## Related Rules

* [complexity](complexity.md)
* [max-depth](max-depth.md)
* [max-nested-callbacks](max-nested-callbacks.md)
* [max-params](max-params.md)
* [max-statements](max-statements.md)

## Compatibility

* **JSCS**: [maximumNumberOfLines](http://jscs.info/rule/maximumNumberOfLines)
148 changes: 148 additions & 0 deletions lib/rules/max-lines.js
@@ -0,0 +1,148 @@
/**
* @fileoverview enforce a maximum file length
* @author Alberto Rodríguez
*/
"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

var lodash = require("lodash");
var astUtils = require("../ast-utils");

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = {
meta: {
docs: {
description: "enforce a maximum number of lines per file",
category: "Stylistic Issues",
recommended: false
},

schema: [
{
oneOf: [
{
type: "integer",
minimum: 0
},
{
type: "object",
properties: {
max: {
type: "integer",
minimum: 0
},
skipComments: {
type: "boolean"
},
skipBlankLines: {
type: "boolean"
}
},
additionalProperties: false
}
]
}
]
},

create: function(context) {
var option = context.options[0],
max = 300;

if (typeof option === "object" && option.hasOwnProperty("max") && typeof option.max === "number") {
max = option.max;
}

if (typeof option === "number") {
max = option;
}

var skipComments = option && option.skipComments;
var skipBlankLines = option && option.skipBlankLines;

var sourceCode = context.getSourceCode();

/**
* Returns whether or not a token is a comment node type
* @param {Token} token The token to check
* @returns {boolean} True if the token is a comment node
*/
function isCommentNodeType(token) {
return token && (token.type === "Block" || token.type === "Line");
}

/**
* Returns the line numbers of a comment that don't have any code on the same line
* @param {Node} comment The comment node to check
* @returns {int[]} The line numbers
*/
function getLinesWithoutCode(comment) {
var start = comment.loc.start.line;
var end = comment.loc.end.line;

var token;

token = comment;
do {
token = sourceCode.getTokenOrCommentBefore(token);
} while (isCommentNodeType(token));

if (token && astUtils.isTokenOnSameLine(token, comment)) {
start += 1;
}

token = comment;
do {
token = sourceCode.getTokenOrCommentAfter(token);
} while (isCommentNodeType(token));

if (token && astUtils.isTokenOnSameLine(comment, token)) {
end -= 1;
}

if (start <= end) {
return lodash.range(start, end + 1);
}
return [];
}

return {
"Program:exit": function() {
var lines = sourceCode.lines.map(function(text, i) {
return { lineNumber: i + 1, text: text };
});

if (skipBlankLines) {
lines = lines.filter(function(l) {
return l.text.trim() !== "";
});
}

if (skipComments) {
var comments = sourceCode.getAllComments();

var commentLines = lodash.flatten(comments.map(function(comment) {
return getLinesWithoutCode(comment);
}));

lines = lines.filter(function(l) {
return !lodash.includes(commentLines, l.lineNumber);
});
}

if (lines.length > max) {
context.report({
loc: { line: 1, column: 0 },
message: "File must be at most " + max + " lines long"
});
}
}
};
}
};

0 comments on commit e426bc2

Please sign in to comment.