]> git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/semi-style.js
2d17d028c2fb06b6cc2d824b0f338bba2e89e686
[pve-eslint.git] / eslint / lib / rules / semi-style.js
1 /**
2 * @fileoverview Rule to enforce location of semicolons.
3 * @author Toru Nagashima
4 */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Requirements
10 //------------------------------------------------------------------------------
11
12 const astUtils = require("./utils/ast-utils");
13
14 //------------------------------------------------------------------------------
15 // Rule Definition
16 //------------------------------------------------------------------------------
17
18 const SELECTOR = [
19 "BreakStatement", "ContinueStatement", "DebuggerStatement",
20 "DoWhileStatement", "ExportAllDeclaration",
21 "ExportDefaultDeclaration", "ExportNamedDeclaration",
22 "ExpressionStatement", "ImportDeclaration", "ReturnStatement",
23 "ThrowStatement", "VariableDeclaration", "PropertyDefinition"
24 ].join(",");
25
26 /**
27 * Get the child node list of a given node.
28 * This returns `BlockStatement#body`, `StaticBlock#body`, `Program#body`,
29 * `ClassBody#body`, or `SwitchCase#consequent`.
30 * This is used to check whether a node is the first/last child.
31 * @param {Node} node A node to get child node list.
32 * @returns {Node[]|null} The child node list.
33 */
34 function getChildren(node) {
35 const t = node.type;
36
37 if (
38 t === "BlockStatement" ||
39 t === "StaticBlock" ||
40 t === "Program" ||
41 t === "ClassBody"
42 ) {
43 return node.body;
44 }
45 if (t === "SwitchCase") {
46 return node.consequent;
47 }
48 return null;
49 }
50
51 /**
52 * Check whether a given node is the last statement in the parent block.
53 * @param {Node} node A node to check.
54 * @returns {boolean} `true` if the node is the last statement in the parent block.
55 */
56 function isLastChild(node) {
57 const t = node.parent.type;
58
59 if (t === "IfStatement" && node.parent.consequent === node && node.parent.alternate) { // before `else` keyword.
60 return true;
61 }
62 if (t === "DoWhileStatement") { // before `while` keyword.
63 return true;
64 }
65 const nodeList = getChildren(node.parent);
66
67 return nodeList !== null && nodeList[nodeList.length - 1] === node; // before `}` or etc.
68 }
69
70 module.exports = {
71 meta: {
72 type: "layout",
73
74 docs: {
75 description: "enforce location of semicolons",
76 recommended: false,
77 url: "https://eslint.org/docs/rules/semi-style"
78 },
79
80 schema: [{ enum: ["last", "first"] }],
81 fixable: "whitespace",
82
83 messages: {
84 expectedSemiColon: "Expected this semicolon to be at {{pos}}."
85 }
86 },
87
88 create(context) {
89 const sourceCode = context.getSourceCode();
90 const option = context.options[0] || "last";
91
92 /**
93 * Check the given semicolon token.
94 * @param {Token} semiToken The semicolon token to check.
95 * @param {"first"|"last"} expected The expected location to check.
96 * @returns {void}
97 */
98 function check(semiToken, expected) {
99 const prevToken = sourceCode.getTokenBefore(semiToken);
100 const nextToken = sourceCode.getTokenAfter(semiToken);
101 const prevIsSameLine = !prevToken || astUtils.isTokenOnSameLine(prevToken, semiToken);
102 const nextIsSameLine = !nextToken || astUtils.isTokenOnSameLine(semiToken, nextToken);
103
104 if ((expected === "last" && !prevIsSameLine) || (expected === "first" && !nextIsSameLine)) {
105 context.report({
106 loc: semiToken.loc,
107 messageId: "expectedSemiColon",
108 data: {
109 pos: (expected === "last")
110 ? "the end of the previous line"
111 : "the beginning of the next line"
112 },
113 fix(fixer) {
114 if (prevToken && nextToken && sourceCode.commentsExistBetween(prevToken, nextToken)) {
115 return null;
116 }
117
118 const start = prevToken ? prevToken.range[1] : semiToken.range[0];
119 const end = nextToken ? nextToken.range[0] : semiToken.range[1];
120 const text = (expected === "last") ? ";\n" : "\n;";
121
122 return fixer.replaceTextRange([start, end], text);
123 }
124 });
125 }
126 }
127
128 return {
129 [SELECTOR](node) {
130 if (option === "first" && isLastChild(node)) {
131 return;
132 }
133
134 const lastToken = sourceCode.getLastToken(node);
135
136 if (astUtils.isSemicolonToken(lastToken)) {
137 check(lastToken, option);
138 }
139 },
140
141 ForStatement(node) {
142 const firstSemi = node.init && sourceCode.getTokenAfter(node.init, astUtils.isSemicolonToken);
143 const secondSemi = node.test && sourceCode.getTokenAfter(node.test, astUtils.isSemicolonToken);
144
145 if (firstSemi) {
146 check(firstSemi, "last");
147 }
148 if (secondSemi) {
149 check(secondSemi, "last");
150 }
151 }
152 };
153 }
154 };