| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 | 
							- /**
 
-  * @fileoverview Rule to flag missing semicolons.
 
-  * @author Nicholas C. Zakas
 
-  */
 
- "use strict";
 
- //------------------------------------------------------------------------------
 
- // Requirements
 
- //------------------------------------------------------------------------------
 
- const FixTracker = require("./utils/fix-tracker");
 
- const astUtils = require("./utils/ast-utils");
 
- //------------------------------------------------------------------------------
 
- // Rule Definition
 
- //------------------------------------------------------------------------------
 
- module.exports = {
 
-     meta: {
 
-         type: "layout",
 
-         docs: {
 
-             description: "require or disallow semicolons instead of ASI",
 
-             category: "Stylistic Issues",
 
-             recommended: false,
 
-             url: "https://eslint.org/docs/rules/semi"
 
-         },
 
-         fixable: "code",
 
-         schema: {
 
-             anyOf: [
 
-                 {
 
-                     type: "array",
 
-                     items: [
 
-                         {
 
-                             enum: ["never"]
 
-                         },
 
-                         {
 
-                             type: "object",
 
-                             properties: {
 
-                                 beforeStatementContinuationChars: {
 
-                                     enum: ["always", "any", "never"]
 
-                                 }
 
-                             },
 
-                             additionalProperties: false
 
-                         }
 
-                     ],
 
-                     minItems: 0,
 
-                     maxItems: 2
 
-                 },
 
-                 {
 
-                     type: "array",
 
-                     items: [
 
-                         {
 
-                             enum: ["always"]
 
-                         },
 
-                         {
 
-                             type: "object",
 
-                             properties: {
 
-                                 omitLastInOneLineBlock: { type: "boolean" }
 
-                             },
 
-                             additionalProperties: false
 
-                         }
 
-                     ],
 
-                     minItems: 0,
 
-                     maxItems: 2
 
-                 }
 
-             ]
 
-         },
 
-         messages: {
 
-             missingSemi: "Missing semicolon.",
 
-             extraSemi: "Extra semicolon."
 
-         }
 
-     },
 
-     create(context) {
 
-         const OPT_OUT_PATTERN = /^[-[(/+`]/u; // One of [(/+-`
 
-         const options = context.options[1];
 
-         const never = context.options[0] === "never";
 
-         const exceptOneLine = Boolean(options && options.omitLastInOneLineBlock);
 
-         const beforeStatementContinuationChars = options && options.beforeStatementContinuationChars || "any";
 
-         const sourceCode = context.getSourceCode();
 
-         //--------------------------------------------------------------------------
 
-         // Helpers
 
-         //--------------------------------------------------------------------------
 
-         /**
 
-          * Reports a semicolon error with appropriate location and message.
 
-          * @param {ASTNode} node The node with an extra or missing semicolon.
 
-          * @param {boolean} missing True if the semicolon is missing.
 
-          * @returns {void}
 
-          */
 
-         function report(node, missing) {
 
-             const lastToken = sourceCode.getLastToken(node);
 
-             let messageId,
 
-                 fix,
 
-                 loc;
 
-             if (!missing) {
 
-                 messageId = "missingSemi";
 
-                 loc = {
 
-                     start: lastToken.loc.end,
 
-                     end: astUtils.getNextLocation(sourceCode, lastToken.loc.end)
 
-                 };
 
-                 fix = function(fixer) {
 
-                     return fixer.insertTextAfter(lastToken, ";");
 
-                 };
 
-             } else {
 
-                 messageId = "extraSemi";
 
-                 loc = lastToken.loc;
 
-                 fix = function(fixer) {
 
-                     /*
 
-                      * Expand the replacement range to include the surrounding
 
-                      * tokens to avoid conflicting with no-extra-semi.
 
-                      * https://github.com/eslint/eslint/issues/7928
 
-                      */
 
-                     return new FixTracker(fixer, sourceCode)
 
-                         .retainSurroundingTokens(lastToken)
 
-                         .remove(lastToken);
 
-                 };
 
-             }
 
-             context.report({
 
-                 node,
 
-                 loc,
 
-                 messageId,
 
-                 fix
 
-             });
 
-         }
 
-         /**
 
-          * Check whether a given semicolon token is redundant.
 
-          * @param {Token} semiToken A semicolon token to check.
 
-          * @returns {boolean} `true` if the next token is `;` or `}`.
 
-          */
 
-         function isRedundantSemi(semiToken) {
 
-             const nextToken = sourceCode.getTokenAfter(semiToken);
 
-             return (
 
-                 !nextToken ||
 
-                 astUtils.isClosingBraceToken(nextToken) ||
 
-                 astUtils.isSemicolonToken(nextToken)
 
-             );
 
-         }
 
-         /**
 
-          * Check whether a given token is the closing brace of an arrow function.
 
-          * @param {Token} lastToken A token to check.
 
-          * @returns {boolean} `true` if the token is the closing brace of an arrow function.
 
-          */
 
-         function isEndOfArrowBlock(lastToken) {
 
-             if (!astUtils.isClosingBraceToken(lastToken)) {
 
-                 return false;
 
-             }
 
-             const node = sourceCode.getNodeByRangeIndex(lastToken.range[0]);
 
-             return (
 
-                 node.type === "BlockStatement" &&
 
-                 node.parent.type === "ArrowFunctionExpression"
 
-             );
 
-         }
 
-         /**
 
-          * Check whether a given node is on the same line with the next token.
 
-          * @param {Node} node A statement node to check.
 
-          * @returns {boolean} `true` if the node is on the same line with the next token.
 
-          */
 
-         function isOnSameLineWithNextToken(node) {
 
-             const prevToken = sourceCode.getLastToken(node, 1);
 
-             const nextToken = sourceCode.getTokenAfter(node);
 
-             return !!nextToken && astUtils.isTokenOnSameLine(prevToken, nextToken);
 
-         }
 
-         /**
 
-          * Check whether a given node can connect the next line if the next line is unreliable.
 
-          * @param {Node} node A statement node to check.
 
-          * @returns {boolean} `true` if the node can connect the next line.
 
-          */
 
-         function maybeAsiHazardAfter(node) {
 
-             const t = node.type;
 
-             if (t === "DoWhileStatement" ||
 
-                 t === "BreakStatement" ||
 
-                 t === "ContinueStatement" ||
 
-                 t === "DebuggerStatement" ||
 
-                 t === "ImportDeclaration" ||
 
-                 t === "ExportAllDeclaration"
 
-             ) {
 
-                 return false;
 
-             }
 
-             if (t === "ReturnStatement") {
 
-                 return Boolean(node.argument);
 
-             }
 
-             if (t === "ExportNamedDeclaration") {
 
-                 return Boolean(node.declaration);
 
-             }
 
-             if (isEndOfArrowBlock(sourceCode.getLastToken(node, 1))) {
 
-                 return false;
 
-             }
 
-             return true;
 
-         }
 
-         /**
 
-          * Check whether a given token can connect the previous statement.
 
-          * @param {Token} token A token to check.
 
-          * @returns {boolean} `true` if the token is one of `[`, `(`, `/`, `+`, `-`, ```, `++`, and `--`.
 
-          */
 
-         function maybeAsiHazardBefore(token) {
 
-             return (
 
-                 Boolean(token) &&
 
-                 OPT_OUT_PATTERN.test(token.value) &&
 
-                 token.value !== "++" &&
 
-                 token.value !== "--"
 
-             );
 
-         }
 
-         /**
 
-          * Check if the semicolon of a given node is unnecessary, only true if:
 
-          *   - next token is a valid statement divider (`;` or `}`).
 
-          *   - next token is on a new line and the node is not connectable to the new line.
 
-          * @param {Node} node A statement node to check.
 
-          * @returns {boolean} whether the semicolon is unnecessary.
 
-          */
 
-         function canRemoveSemicolon(node) {
 
-             if (isRedundantSemi(sourceCode.getLastToken(node))) {
 
-                 return true; // `;;` or `;}`
 
-             }
 
-             if (isOnSameLineWithNextToken(node)) {
 
-                 return false; // One liner.
 
-             }
 
-             if (beforeStatementContinuationChars === "never" && !maybeAsiHazardAfter(node)) {
 
-                 return true; // ASI works. This statement doesn't connect to the next.
 
-             }
 
-             if (!maybeAsiHazardBefore(sourceCode.getTokenAfter(node))) {
 
-                 return true; // ASI works. The next token doesn't connect to this statement.
 
-             }
 
-             return false;
 
-         }
 
-         /**
 
-          * Checks a node to see if it's in a one-liner block statement.
 
-          * @param {ASTNode} node The node to check.
 
-          * @returns {boolean} whether the node is in a one-liner block statement.
 
-          */
 
-         function isOneLinerBlock(node) {
 
-             const parent = node.parent;
 
-             const nextToken = sourceCode.getTokenAfter(node);
 
-             if (!nextToken || nextToken.value !== "}") {
 
-                 return false;
 
-             }
 
-             return (
 
-                 !!parent &&
 
-                 parent.type === "BlockStatement" &&
 
-                 parent.loc.start.line === parent.loc.end.line
 
-             );
 
-         }
 
-         /**
 
-          * Checks a node to see if it's followed by a semicolon.
 
-          * @param {ASTNode} node The node to check.
 
-          * @returns {void}
 
-          */
 
-         function checkForSemicolon(node) {
 
-             const isSemi = astUtils.isSemicolonToken(sourceCode.getLastToken(node));
 
-             if (never) {
 
-                 if (isSemi && canRemoveSemicolon(node)) {
 
-                     report(node, true);
 
-                 } else if (!isSemi && beforeStatementContinuationChars === "always" && maybeAsiHazardBefore(sourceCode.getTokenAfter(node))) {
 
-                     report(node);
 
-                 }
 
-             } else {
 
-                 const oneLinerBlock = (exceptOneLine && isOneLinerBlock(node));
 
-                 if (isSemi && oneLinerBlock) {
 
-                     report(node, true);
 
-                 } else if (!isSemi && !oneLinerBlock) {
 
-                     report(node);
 
-                 }
 
-             }
 
-         }
 
-         /**
 
-          * Checks to see if there's a semicolon after a variable declaration.
 
-          * @param {ASTNode} node The node to check.
 
-          * @returns {void}
 
-          */
 
-         function checkForSemicolonForVariableDeclaration(node) {
 
-             const parent = node.parent;
 
-             if ((parent.type !== "ForStatement" || parent.init !== node) &&
 
-                 (!/^For(?:In|Of)Statement/u.test(parent.type) || parent.left !== node)
 
-             ) {
 
-                 checkForSemicolon(node);
 
-             }
 
-         }
 
-         //--------------------------------------------------------------------------
 
-         // Public API
 
-         //--------------------------------------------------------------------------
 
-         return {
 
-             VariableDeclaration: checkForSemicolonForVariableDeclaration,
 
-             ExpressionStatement: checkForSemicolon,
 
-             ReturnStatement: checkForSemicolon,
 
-             ThrowStatement: checkForSemicolon,
 
-             DoWhileStatement: checkForSemicolon,
 
-             DebuggerStatement: checkForSemicolon,
 
-             BreakStatement: checkForSemicolon,
 
-             ContinueStatement: checkForSemicolon,
 
-             ImportDeclaration: checkForSemicolon,
 
-             ExportAllDeclaration: checkForSemicolon,
 
-             ExportNamedDeclaration(node) {
 
-                 if (!node.declaration) {
 
-                     checkForSemicolon(node);
 
-                 }
 
-             },
 
-             ExportDefaultDeclaration(node) {
 
-                 if (!/(?:Class|Function)Declaration/u.test(node.declaration.type)) {
 
-                     checkForSemicolon(node);
 
-                 }
 
-             }
 
-         };
 
-     }
 
- };
 
 
  |