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