source: imaps-frontend/node_modules/eslint/lib/rules/semi.js@ d565449

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

Update repo after prototype presentation

  • Property mode set to 100644
File size: 15.9 KB
RevLine 
[d565449]1/**
2 * @fileoverview Rule to flag missing semicolons.
3 * @author Nicholas C. Zakas
4 * @deprecated in ESLint v8.53.0
5 */
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const FixTracker = require("./utils/fix-tracker");
13const astUtils = require("./utils/ast-utils");
14
15//------------------------------------------------------------------------------
16// Rule Definition
17//------------------------------------------------------------------------------
18
19/** @type {import('../shared/types').Rule} */
20module.exports = {
21 meta: {
22 deprecated: true,
23 replacedBy: [],
24 type: "layout",
25
26 docs: {
27 description: "Require or disallow semicolons instead of ASI",
28 recommended: false,
29 url: "https://eslint.org/docs/latest/rules/semi"
30 },
31
32 fixable: "code",
33
34 schema: {
35 anyOf: [
36 {
37 type: "array",
38 items: [
39 {
40 enum: ["never"]
41 },
42 {
43 type: "object",
44 properties: {
45 beforeStatementContinuationChars: {
46 enum: ["always", "any", "never"]
47 }
48 },
49 additionalProperties: false
50 }
51 ],
52 minItems: 0,
53 maxItems: 2
54 },
55 {
56 type: "array",
57 items: [
58 {
59 enum: ["always"]
60 },
61 {
62 type: "object",
63 properties: {
64 omitLastInOneLineBlock: { type: "boolean" },
65 omitLastInOneLineClassBody: { type: "boolean" }
66 },
67 additionalProperties: false
68 }
69 ],
70 minItems: 0,
71 maxItems: 2
72 }
73 ]
74 },
75
76 messages: {
77 missingSemi: "Missing semicolon.",
78 extraSemi: "Extra semicolon."
79 }
80 },
81
82 create(context) {
83
84 const OPT_OUT_PATTERN = /^[-[(/+`]/u; // One of [(/+-`
85 const unsafeClassFieldNames = new Set(["get", "set", "static"]);
86 const unsafeClassFieldFollowers = new Set(["*", "in", "instanceof"]);
87 const options = context.options[1];
88 const never = context.options[0] === "never";
89 const exceptOneLine = Boolean(options && options.omitLastInOneLineBlock);
90 const exceptOneLineClassBody = Boolean(options && options.omitLastInOneLineClassBody);
91 const beforeStatementContinuationChars = options && options.beforeStatementContinuationChars || "any";
92 const sourceCode = context.sourceCode;
93
94 //--------------------------------------------------------------------------
95 // Helpers
96 //--------------------------------------------------------------------------
97
98 /**
99 * Reports a semicolon error with appropriate location and message.
100 * @param {ASTNode} node The node with an extra or missing semicolon.
101 * @param {boolean} missing True if the semicolon is missing.
102 * @returns {void}
103 */
104 function report(node, missing) {
105 const lastToken = sourceCode.getLastToken(node);
106 let messageId,
107 fix,
108 loc;
109
110 if (!missing) {
111 messageId = "missingSemi";
112 loc = {
113 start: lastToken.loc.end,
114 end: astUtils.getNextLocation(sourceCode, lastToken.loc.end)
115 };
116 fix = function(fixer) {
117 return fixer.insertTextAfter(lastToken, ";");
118 };
119 } else {
120 messageId = "extraSemi";
121 loc = lastToken.loc;
122 fix = function(fixer) {
123
124 /*
125 * Expand the replacement range to include the surrounding
126 * tokens to avoid conflicting with no-extra-semi.
127 * https://github.com/eslint/eslint/issues/7928
128 */
129 return new FixTracker(fixer, sourceCode)
130 .retainSurroundingTokens(lastToken)
131 .remove(lastToken);
132 };
133 }
134
135 context.report({
136 node,
137 loc,
138 messageId,
139 fix
140 });
141
142 }
143
144 /**
145 * Check whether a given semicolon token is redundant.
146 * @param {Token} semiToken A semicolon token to check.
147 * @returns {boolean} `true` if the next token is `;` or `}`.
148 */
149 function isRedundantSemi(semiToken) {
150 const nextToken = sourceCode.getTokenAfter(semiToken);
151
152 return (
153 !nextToken ||
154 astUtils.isClosingBraceToken(nextToken) ||
155 astUtils.isSemicolonToken(nextToken)
156 );
157 }
158
159 /**
160 * Check whether a given token is the closing brace of an arrow function.
161 * @param {Token} lastToken A token to check.
162 * @returns {boolean} `true` if the token is the closing brace of an arrow function.
163 */
164 function isEndOfArrowBlock(lastToken) {
165 if (!astUtils.isClosingBraceToken(lastToken)) {
166 return false;
167 }
168 const node = sourceCode.getNodeByRangeIndex(lastToken.range[0]);
169
170 return (
171 node.type === "BlockStatement" &&
172 node.parent.type === "ArrowFunctionExpression"
173 );
174 }
175
176 /**
177 * Checks if a given PropertyDefinition node followed by a semicolon
178 * can safely remove that semicolon. It is not to safe to remove if
179 * the class field name is "get", "set", or "static", or if
180 * followed by a generator method.
181 * @param {ASTNode} node The node to check.
182 * @returns {boolean} `true` if the node cannot have the semicolon
183 * removed.
184 */
185 function maybeClassFieldAsiHazard(node) {
186
187 if (node.type !== "PropertyDefinition") {
188 return false;
189 }
190
191 /*
192 * Computed property names and non-identifiers are always safe
193 * as they can be distinguished from keywords easily.
194 */
195 const needsNameCheck = !node.computed && node.key.type === "Identifier";
196
197 /*
198 * Certain names are problematic unless they also have a
199 * a way to distinguish between keywords and property
200 * names.
201 */
202 if (needsNameCheck && unsafeClassFieldNames.has(node.key.name)) {
203
204 /*
205 * Special case: If the field name is `static`,
206 * it is only valid if the field is marked as static,
207 * so "static static" is okay but "static" is not.
208 */
209 const isStaticStatic = node.static && node.key.name === "static";
210
211 /*
212 * For other unsafe names, we only care if there is no
213 * initializer. No initializer = hazard.
214 */
215 if (!isStaticStatic && !node.value) {
216 return true;
217 }
218 }
219
220 const followingToken = sourceCode.getTokenAfter(node);
221
222 return unsafeClassFieldFollowers.has(followingToken.value);
223 }
224
225 /**
226 * Check whether a given node is on the same line with the next token.
227 * @param {Node} node A statement node to check.
228 * @returns {boolean} `true` if the node is on the same line with the next token.
229 */
230 function isOnSameLineWithNextToken(node) {
231 const prevToken = sourceCode.getLastToken(node, 1);
232 const nextToken = sourceCode.getTokenAfter(node);
233
234 return !!nextToken && astUtils.isTokenOnSameLine(prevToken, nextToken);
235 }
236
237 /**
238 * Check whether a given node can connect the next line if the next line is unreliable.
239 * @param {Node} node A statement node to check.
240 * @returns {boolean} `true` if the node can connect the next line.
241 */
242 function maybeAsiHazardAfter(node) {
243 const t = node.type;
244
245 if (t === "DoWhileStatement" ||
246 t === "BreakStatement" ||
247 t === "ContinueStatement" ||
248 t === "DebuggerStatement" ||
249 t === "ImportDeclaration" ||
250 t === "ExportAllDeclaration"
251 ) {
252 return false;
253 }
254 if (t === "ReturnStatement") {
255 return Boolean(node.argument);
256 }
257 if (t === "ExportNamedDeclaration") {
258 return Boolean(node.declaration);
259 }
260 if (isEndOfArrowBlock(sourceCode.getLastToken(node, 1))) {
261 return false;
262 }
263
264 return true;
265 }
266
267 /**
268 * Check whether a given token can connect the previous statement.
269 * @param {Token} token A token to check.
270 * @returns {boolean} `true` if the token is one of `[`, `(`, `/`, `+`, `-`, ```, `++`, and `--`.
271 */
272 function maybeAsiHazardBefore(token) {
273 return (
274 Boolean(token) &&
275 OPT_OUT_PATTERN.test(token.value) &&
276 token.value !== "++" &&
277 token.value !== "--"
278 );
279 }
280
281 /**
282 * Check if the semicolon of a given node is unnecessary, only true if:
283 * - next token is a valid statement divider (`;` or `}`).
284 * - next token is on a new line and the node is not connectable to the new line.
285 * @param {Node} node A statement node to check.
286 * @returns {boolean} whether the semicolon is unnecessary.
287 */
288 function canRemoveSemicolon(node) {
289 if (isRedundantSemi(sourceCode.getLastToken(node))) {
290 return true; // `;;` or `;}`
291 }
292 if (maybeClassFieldAsiHazard(node)) {
293 return false;
294 }
295 if (isOnSameLineWithNextToken(node)) {
296 return false; // One liner.
297 }
298
299 // continuation characters should not apply to class fields
300 if (
301 node.type !== "PropertyDefinition" &&
302 beforeStatementContinuationChars === "never" &&
303 !maybeAsiHazardAfter(node)
304 ) {
305 return true; // ASI works. This statement doesn't connect to the next.
306 }
307 if (!maybeAsiHazardBefore(sourceCode.getTokenAfter(node))) {
308 return true; // ASI works. The next token doesn't connect to this statement.
309 }
310
311 return false;
312 }
313
314 /**
315 * Checks a node to see if it's the last item in a one-liner block.
316 * Block is any `BlockStatement` or `StaticBlock` node. Block is a one-liner if its
317 * braces (and consequently everything between them) are on the same line.
318 * @param {ASTNode} node The node to check.
319 * @returns {boolean} whether the node is the last item in a one-liner block.
320 */
321 function isLastInOneLinerBlock(node) {
322 const parent = node.parent;
323 const nextToken = sourceCode.getTokenAfter(node);
324
325 if (!nextToken || nextToken.value !== "}") {
326 return false;
327 }
328
329 if (parent.type === "BlockStatement") {
330 return parent.loc.start.line === parent.loc.end.line;
331 }
332
333 if (parent.type === "StaticBlock") {
334 const openingBrace = sourceCode.getFirstToken(parent, { skip: 1 }); // skip the `static` token
335
336 return openingBrace.loc.start.line === parent.loc.end.line;
337 }
338
339 return false;
340 }
341
342 /**
343 * Checks a node to see if it's the last item in a one-liner `ClassBody` node.
344 * ClassBody is a one-liner if its braces (and consequently everything between them) are on the same line.
345 * @param {ASTNode} node The node to check.
346 * @returns {boolean} whether the node is the last item in a one-liner ClassBody.
347 */
348 function isLastInOneLinerClassBody(node) {
349 const parent = node.parent;
350 const nextToken = sourceCode.getTokenAfter(node);
351
352 if (!nextToken || nextToken.value !== "}") {
353 return false;
354 }
355
356 if (parent.type === "ClassBody") {
357 return parent.loc.start.line === parent.loc.end.line;
358 }
359
360 return false;
361 }
362
363 /**
364 * Checks a node to see if it's followed by a semicolon.
365 * @param {ASTNode} node The node to check.
366 * @returns {void}
367 */
368 function checkForSemicolon(node) {
369 const isSemi = astUtils.isSemicolonToken(sourceCode.getLastToken(node));
370
371 if (never) {
372 if (isSemi && canRemoveSemicolon(node)) {
373 report(node, true);
374 } else if (
375 !isSemi && beforeStatementContinuationChars === "always" &&
376 node.type !== "PropertyDefinition" &&
377 maybeAsiHazardBefore(sourceCode.getTokenAfter(node))
378 ) {
379 report(node);
380 }
381 } else {
382 const oneLinerBlock = (exceptOneLine && isLastInOneLinerBlock(node));
383 const oneLinerClassBody = (exceptOneLineClassBody && isLastInOneLinerClassBody(node));
384 const oneLinerBlockOrClassBody = oneLinerBlock || oneLinerClassBody;
385
386 if (isSemi && oneLinerBlockOrClassBody) {
387 report(node, true);
388 } else if (!isSemi && !oneLinerBlockOrClassBody) {
389 report(node);
390 }
391 }
392 }
393
394 /**
395 * Checks to see if there's a semicolon after a variable declaration.
396 * @param {ASTNode} node The node to check.
397 * @returns {void}
398 */
399 function checkForSemicolonForVariableDeclaration(node) {
400 const parent = node.parent;
401
402 if ((parent.type !== "ForStatement" || parent.init !== node) &&
403 (!/^For(?:In|Of)Statement/u.test(parent.type) || parent.left !== node)
404 ) {
405 checkForSemicolon(node);
406 }
407 }
408
409 //--------------------------------------------------------------------------
410 // Public API
411 //--------------------------------------------------------------------------
412
413 return {
414 VariableDeclaration: checkForSemicolonForVariableDeclaration,
415 ExpressionStatement: checkForSemicolon,
416 ReturnStatement: checkForSemicolon,
417 ThrowStatement: checkForSemicolon,
418 DoWhileStatement: checkForSemicolon,
419 DebuggerStatement: checkForSemicolon,
420 BreakStatement: checkForSemicolon,
421 ContinueStatement: checkForSemicolon,
422 ImportDeclaration: checkForSemicolon,
423 ExportAllDeclaration: checkForSemicolon,
424 ExportNamedDeclaration(node) {
425 if (!node.declaration) {
426 checkForSemicolon(node);
427 }
428 },
429 ExportDefaultDeclaration(node) {
430 if (!/(?:Class|Function)Declaration/u.test(node.declaration.type)) {
431 checkForSemicolon(node);
432 }
433 },
434 PropertyDefinition: checkForSemicolon
435 };
436
437 }
438};
Note: See TracBrowser for help on using the repository browser.