UNPKG

4.5 kBJavaScriptView Raw
1/**
2 * @fileoverview A rule to ensure blank lines within blocks.
3 * @author Mathias Schreck <https://github.com/lo1tuma>
4 * @copyright 2014 Mathias Schreck. All rights reserved.
5 */
6
7"use strict";
8
9//------------------------------------------------------------------------------
10// Rule Definition
11//------------------------------------------------------------------------------
12
13module.exports = function(context) {
14 var requirePadding = context.options[0] !== "never";
15
16 var ALWAYS_MESSAGE = "Block must be padded by blank lines.",
17 NEVER_MESSAGE = "Block must not be padded by blank lines.";
18
19 /**
20 * Retrieves an array of all comments defined inside the given node.
21 * @param {ASTNode} node The AST node.
22 * @returns {ASTNode[]} An array of comment nodes.
23 */
24 function getCommentsInNode(node) {
25 var allComments = context.getAllComments();
26
27 return allComments.filter(function(comment) {
28 return node.range[0] < comment.range[0] &&
29 node.range[1] > comment.range[1];
30 });
31 }
32
33 /**
34 * Checks if the location of a node or token is before the location of another node or token
35 * @param {ASTNode|Token} a The node or token to check if its location is before b.
36 * @param {ASTNode|Token} b The node or token which will be compared with a.
37 * @returns {boolean} True if a is located before b.
38 */
39 function isLocatedBefore(a, b) {
40 return a.range[1] < b.range[0];
41 }
42
43 /**
44 * Checks if the given non empty block node has a blank line before its first child node.
45 * @param {ASTNode} node The AST node of a BlockStatement.
46 * @returns {boolean} Whether or not the block starts with a blank line.
47 */
48 function isBlockTopPadded(node) {
49 var blockStart = node.loc.start.line,
50 first = node.body[0],
51 firstLine = first.loc.start.line,
52 expectedFirstLine = blockStart + 2,
53 comments = getCommentsInNode(node),
54 firstComment = comments[0];
55
56 if (firstComment && isLocatedBefore(firstComment, first)) {
57 firstLine = firstComment.loc.start.line;
58 }
59
60 return expectedFirstLine <= firstLine;
61 }
62
63 /**
64 * Checks if the given non empty block node has a blank line after its last child node.
65 * @param {ASTNode} node The AST node of a BlockStatement.
66 * @returns {boolean} Whether or not the block ends with a blank line.
67 */
68 function isBlockBottomPadded(node) {
69 var blockEnd = node.loc.end.line,
70 last = node.body[node.body.length - 1],
71 lastToken = context.getLastToken(last),
72 lastLine = lastToken.loc.end.line,
73 expectedLastLine = blockEnd - 2,
74 comments = getCommentsInNode(node),
75 lastComment = comments[comments.length - 1];
76
77 if (lastComment && isLocatedBefore(lastToken, lastComment)) {
78 lastLine = lastComment.loc.end.line;
79 }
80
81 return lastLine <= expectedLastLine;
82 }
83
84 /**
85 * Checks the given BlockStatement node to be padded if the block is not empty.
86 * @param {ASTNode} node The AST node of a BlockStatement.
87 * @returns {void} undefined.
88 */
89 function checkPadding(node) {
90 if (node.body.length > 0) {
91
92 var blockHasTopPadding = isBlockTopPadded(node),
93 blockHasBottomPadding = isBlockBottomPadded(node);
94
95 if (requirePadding) {
96 if (!blockHasTopPadding) {
97 context.report(node, ALWAYS_MESSAGE);
98 }
99 if (!blockHasBottomPadding) {
100 context.report({
101 node: node,
102 loc: {line: node.loc.end.line, column: node.loc.end.column - 1 },
103 message: ALWAYS_MESSAGE
104 });
105 }
106 } else {
107 if (blockHasTopPadding) {
108 context.report(node, NEVER_MESSAGE);
109 }
110
111 if (blockHasBottomPadding) {
112 context.report({
113 node: node,
114 loc: {line: node.loc.end.line, column: node.loc.end.column - 1 },
115 message: NEVER_MESSAGE
116 });
117 }
118 }
119 }
120 }
121
122 return {
123 "BlockStatement": checkPadding
124 };
125
126};
127
128module.exports.schema = [
129 {
130 "enum": ["always", "never"]
131 }
132];