source: imaps-frontend/node_modules/eslint/lib/rules/lines-between-class-members.js

main
Last change on this file was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 11.2 KB
Line 
1/**
2 * @fileoverview Rule to check empty newline between class members
3 * @author 薛定谔的猫<hh_2013@foxmail.com>
4 * @deprecated in ESLint v8.53.0
5 */
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const astUtils = require("./utils/ast-utils");
13
14//------------------------------------------------------------------------------
15// Helpers
16//------------------------------------------------------------------------------
17
18/**
19 * Types of class members.
20 * Those have `test` method to check it matches to the given class member.
21 * @private
22 */
23const ClassMemberTypes = {
24 "*": { test: () => true },
25 field: { test: node => node.type === "PropertyDefinition" },
26 method: { test: node => node.type === "MethodDefinition" }
27};
28
29//------------------------------------------------------------------------------
30// Rule Definition
31//------------------------------------------------------------------------------
32
33/** @type {import('../shared/types').Rule} */
34module.exports = {
35 meta: {
36 deprecated: true,
37 replacedBy: [],
38 type: "layout",
39
40 docs: {
41 description: "Require or disallow an empty line between class members",
42 recommended: false,
43 url: "https://eslint.org/docs/latest/rules/lines-between-class-members"
44 },
45
46 fixable: "whitespace",
47
48 schema: [
49 {
50 anyOf: [
51 {
52 type: "object",
53 properties: {
54 enforce: {
55 type: "array",
56 items: {
57 type: "object",
58 properties: {
59 blankLine: { enum: ["always", "never"] },
60 prev: { enum: ["method", "field", "*"] },
61 next: { enum: ["method", "field", "*"] }
62 },
63 additionalProperties: false,
64 required: ["blankLine", "prev", "next"]
65 },
66 minItems: 1
67 }
68 },
69 additionalProperties: false,
70 required: ["enforce"]
71 },
72 {
73 enum: ["always", "never"]
74 }
75 ]
76 },
77 {
78 type: "object",
79 properties: {
80 exceptAfterSingleLine: {
81 type: "boolean",
82 default: false
83 }
84 },
85 additionalProperties: false
86 }
87 ],
88 messages: {
89 never: "Unexpected blank line between class members.",
90 always: "Expected blank line between class members."
91 }
92 },
93
94 create(context) {
95
96 const options = [];
97
98 options[0] = context.options[0] || "always";
99 options[1] = context.options[1] || { exceptAfterSingleLine: false };
100
101 const configureList = typeof options[0] === "object" ? options[0].enforce : [{ blankLine: options[0], prev: "*", next: "*" }];
102 const sourceCode = context.sourceCode;
103
104 /**
105 * Gets a pair of tokens that should be used to check lines between two class member nodes.
106 *
107 * In most cases, this returns the very last token of the current node and
108 * the very first token of the next node.
109 * For example:
110 *
111 * class C {
112 * x = 1; // curLast: `;` nextFirst: `in`
113 * in = 2
114 * }
115 *
116 * There is only one exception. If the given node ends with a semicolon, and it looks like
117 * a semicolon-less style's semicolon - one that is not on the same line as the preceding
118 * token, but is on the line where the next class member starts - this returns the preceding
119 * token and the semicolon as boundary tokens.
120 * For example:
121 *
122 * class C {
123 * x = 1 // curLast: `1` nextFirst: `;`
124 * ;in = 2
125 * }
126 * When determining the desired layout of the code, we should treat this semicolon as
127 * a part of the next class member node instead of the one it technically belongs to.
128 * @param {ASTNode} curNode Current class member node.
129 * @param {ASTNode} nextNode Next class member node.
130 * @returns {Token} The actual last token of `node`.
131 * @private
132 */
133 function getBoundaryTokens(curNode, nextNode) {
134 const lastToken = sourceCode.getLastToken(curNode);
135 const prevToken = sourceCode.getTokenBefore(lastToken);
136 const nextToken = sourceCode.getFirstToken(nextNode); // skip possible lone `;` between nodes
137
138 const isSemicolonLessStyle = (
139 astUtils.isSemicolonToken(lastToken) &&
140 !astUtils.isTokenOnSameLine(prevToken, lastToken) &&
141 astUtils.isTokenOnSameLine(lastToken, nextToken)
142 );
143
144 return isSemicolonLessStyle
145 ? { curLast: prevToken, nextFirst: lastToken }
146 : { curLast: lastToken, nextFirst: nextToken };
147 }
148
149 /**
150 * Return the last token among the consecutive tokens that have no exceed max line difference in between, before the first token in the next member.
151 * @param {Token} prevLastToken The last token in the previous member node.
152 * @param {Token} nextFirstToken The first token in the next member node.
153 * @param {number} maxLine The maximum number of allowed line difference between consecutive tokens.
154 * @returns {Token} The last token among the consecutive tokens.
155 */
156 function findLastConsecutiveTokenAfter(prevLastToken, nextFirstToken, maxLine) {
157 const after = sourceCode.getTokenAfter(prevLastToken, { includeComments: true });
158
159 if (after !== nextFirstToken && after.loc.start.line - prevLastToken.loc.end.line <= maxLine) {
160 return findLastConsecutiveTokenAfter(after, nextFirstToken, maxLine);
161 }
162 return prevLastToken;
163 }
164
165 /**
166 * Return the first token among the consecutive tokens that have no exceed max line difference in between, after the last token in the previous member.
167 * @param {Token} nextFirstToken The first token in the next member node.
168 * @param {Token} prevLastToken The last token in the previous member node.
169 * @param {number} maxLine The maximum number of allowed line difference between consecutive tokens.
170 * @returns {Token} The first token among the consecutive tokens.
171 */
172 function findFirstConsecutiveTokenBefore(nextFirstToken, prevLastToken, maxLine) {
173 const before = sourceCode.getTokenBefore(nextFirstToken, { includeComments: true });
174
175 if (before !== prevLastToken && nextFirstToken.loc.start.line - before.loc.end.line <= maxLine) {
176 return findFirstConsecutiveTokenBefore(before, prevLastToken, maxLine);
177 }
178 return nextFirstToken;
179 }
180
181 /**
182 * Checks if there is a token or comment between two tokens.
183 * @param {Token} before The token before.
184 * @param {Token} after The token after.
185 * @returns {boolean} True if there is a token or comment between two tokens.
186 */
187 function hasTokenOrCommentBetween(before, after) {
188 return sourceCode.getTokensBetween(before, after, { includeComments: true }).length !== 0;
189 }
190
191 /**
192 * Checks whether the given node matches the given type.
193 * @param {ASTNode} node The class member node to check.
194 * @param {string} type The class member type to check.
195 * @returns {boolean} `true` if the class member node matched the type.
196 * @private
197 */
198 function match(node, type) {
199 return ClassMemberTypes[type].test(node);
200 }
201
202 /**
203 * Finds the last matched configuration from the configureList.
204 * @param {ASTNode} prevNode The previous node to match.
205 * @param {ASTNode} nextNode The current node to match.
206 * @returns {string|null} Padding type or `null` if no matches were found.
207 * @private
208 */
209 function getPaddingType(prevNode, nextNode) {
210 for (let i = configureList.length - 1; i >= 0; --i) {
211 const configure = configureList[i];
212 const matched =
213 match(prevNode, configure.prev) &&
214 match(nextNode, configure.next);
215
216 if (matched) {
217 return configure.blankLine;
218 }
219 }
220 return null;
221 }
222
223 return {
224 ClassBody(node) {
225 const body = node.body;
226
227 for (let i = 0; i < body.length - 1; i++) {
228 const curFirst = sourceCode.getFirstToken(body[i]);
229 const { curLast, nextFirst } = getBoundaryTokens(body[i], body[i + 1]);
230 const isMulti = !astUtils.isTokenOnSameLine(curFirst, curLast);
231 const skip = !isMulti && options[1].exceptAfterSingleLine;
232 const beforePadding = findLastConsecutiveTokenAfter(curLast, nextFirst, 1);
233 const afterPadding = findFirstConsecutiveTokenBefore(nextFirst, curLast, 1);
234 const isPadded = afterPadding.loc.start.line - beforePadding.loc.end.line > 1;
235 const hasTokenInPadding = hasTokenOrCommentBetween(beforePadding, afterPadding);
236 const curLineLastToken = findLastConsecutiveTokenAfter(curLast, nextFirst, 0);
237 const paddingType = getPaddingType(body[i], body[i + 1]);
238
239 if (paddingType === "never" && isPadded) {
240 context.report({
241 node: body[i + 1],
242 messageId: "never",
243
244 fix(fixer) {
245 if (hasTokenInPadding) {
246 return null;
247 }
248 return fixer.replaceTextRange([beforePadding.range[1], afterPadding.range[0]], "\n");
249 }
250 });
251 } else if (paddingType === "always" && !skip && !isPadded) {
252 context.report({
253 node: body[i + 1],
254 messageId: "always",
255
256 fix(fixer) {
257 if (hasTokenInPadding) {
258 return null;
259 }
260 return fixer.insertTextAfter(curLineLastToken, "\n");
261 }
262 });
263 }
264
265 }
266 }
267 };
268 }
269};
Note: See TracBrowser for help on using the repository browser.