123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438 |
- "use strict";
- const FixTracker = require("./utils/fix-tracker");
- const astUtils = require("./utils/ast-utils");
- module.exports = {
- meta: {
- deprecated: true,
- replacedBy: [],
- type: "layout",
- docs: {
- description: "Require or disallow semicolons instead of ASI",
- recommended: false,
- url: "https://eslint.org/docs/latest/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" },
- omitLastInOneLineClassBody: { type: "boolean" }
- },
- additionalProperties: false
- }
- ],
- minItems: 0,
- maxItems: 2
- }
- ]
- },
- messages: {
- missingSemi: "Missing semicolon.",
- extraSemi: "Extra semicolon."
- }
- },
- create(context) {
- const OPT_OUT_PATTERN = /^[-[(/+`]/u;
- const unsafeClassFieldNames = new Set(["get", "set", "static"]);
- const unsafeClassFieldFollowers = new Set(["*", "in", "instanceof"]);
- const options = context.options[1];
- const never = context.options[0] === "never";
- const exceptOneLine = Boolean(options && options.omitLastInOneLineBlock);
- const exceptOneLineClassBody = Boolean(options && options.omitLastInOneLineClassBody);
- const beforeStatementContinuationChars = options && options.beforeStatementContinuationChars || "any";
- const sourceCode = context.sourceCode;
-
-
-
-
- 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) {
-
- return new FixTracker(fixer, sourceCode)
- .retainSurroundingTokens(lastToken)
- .remove(lastToken);
- };
- }
- context.report({
- node,
- loc,
- messageId,
- fix
- });
- }
-
- function isRedundantSemi(semiToken) {
- const nextToken = sourceCode.getTokenAfter(semiToken);
- return (
- !nextToken ||
- astUtils.isClosingBraceToken(nextToken) ||
- astUtils.isSemicolonToken(nextToken)
- );
- }
-
- function isEndOfArrowBlock(lastToken) {
- if (!astUtils.isClosingBraceToken(lastToken)) {
- return false;
- }
- const node = sourceCode.getNodeByRangeIndex(lastToken.range[0]);
- return (
- node.type === "BlockStatement" &&
- node.parent.type === "ArrowFunctionExpression"
- );
- }
-
- function maybeClassFieldAsiHazard(node) {
- if (node.type !== "PropertyDefinition") {
- return false;
- }
-
- const needsNameCheck = !node.computed && node.key.type === "Identifier";
-
- if (needsNameCheck && unsafeClassFieldNames.has(node.key.name)) {
-
- const isStaticStatic = node.static && node.key.name === "static";
-
- if (!isStaticStatic && !node.value) {
- return true;
- }
- }
- const followingToken = sourceCode.getTokenAfter(node);
- return unsafeClassFieldFollowers.has(followingToken.value);
- }
-
- function isOnSameLineWithNextToken(node) {
- const prevToken = sourceCode.getLastToken(node, 1);
- const nextToken = sourceCode.getTokenAfter(node);
- return !!nextToken && astUtils.isTokenOnSameLine(prevToken, nextToken);
- }
-
- 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;
- }
-
- function maybeAsiHazardBefore(token) {
- return (
- Boolean(token) &&
- OPT_OUT_PATTERN.test(token.value) &&
- token.value !== "++" &&
- token.value !== "--"
- );
- }
-
- function canRemoveSemicolon(node) {
- if (isRedundantSemi(sourceCode.getLastToken(node))) {
- return true;
- }
- if (maybeClassFieldAsiHazard(node)) {
- return false;
- }
- if (isOnSameLineWithNextToken(node)) {
- return false;
- }
-
- if (
- node.type !== "PropertyDefinition" &&
- beforeStatementContinuationChars === "never" &&
- !maybeAsiHazardAfter(node)
- ) {
- return true;
- }
- if (!maybeAsiHazardBefore(sourceCode.getTokenAfter(node))) {
- return true;
- }
- return false;
- }
-
- function isLastInOneLinerBlock(node) {
- const parent = node.parent;
- const nextToken = sourceCode.getTokenAfter(node);
- if (!nextToken || nextToken.value !== "}") {
- return false;
- }
- if (parent.type === "BlockStatement") {
- return parent.loc.start.line === parent.loc.end.line;
- }
- if (parent.type === "StaticBlock") {
- const openingBrace = sourceCode.getFirstToken(parent, { skip: 1 });
- return openingBrace.loc.start.line === parent.loc.end.line;
- }
- return false;
- }
-
- function isLastInOneLinerClassBody(node) {
- const parent = node.parent;
- const nextToken = sourceCode.getTokenAfter(node);
- if (!nextToken || nextToken.value !== "}") {
- return false;
- }
- if (parent.type === "ClassBody") {
- return parent.loc.start.line === parent.loc.end.line;
- }
- return false;
- }
-
- function checkForSemicolon(node) {
- const isSemi = astUtils.isSemicolonToken(sourceCode.getLastToken(node));
- if (never) {
- if (isSemi && canRemoveSemicolon(node)) {
- report(node, true);
- } else if (
- !isSemi && beforeStatementContinuationChars === "always" &&
- node.type !== "PropertyDefinition" &&
- maybeAsiHazardBefore(sourceCode.getTokenAfter(node))
- ) {
- report(node);
- }
- } else {
- const oneLinerBlock = (exceptOneLine && isLastInOneLinerBlock(node));
- const oneLinerClassBody = (exceptOneLineClassBody && isLastInOneLinerClassBody(node));
- const oneLinerBlockOrClassBody = oneLinerBlock || oneLinerClassBody;
- if (isSemi && oneLinerBlockOrClassBody) {
- report(node, true);
- } else if (!isSemi && !oneLinerBlockOrClassBody) {
- report(node);
- }
- }
- }
-
- 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);
- }
- }
-
-
-
- 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);
- }
- },
- PropertyDefinition: checkForSemicolon
- };
- }
- };
|