278 lines
11 KiB
Plaintext
278 lines
11 KiB
Plaintext
|
"use strict";
|
||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||
|
};
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
const utils_1 = require("../utils");
|
||
|
const compat_1 = require("../utils/compat");
|
||
|
const eslint_utils_1 = require("@eslint-community/eslint-utils");
|
||
|
const ast_utils_1 = require("../utils/ast-utils");
|
||
|
const fix_tracker_1 = __importDefault(require("../utils/fix-tracker"));
|
||
|
exports.default = (0, utils_1.createRule)("semi", {
|
||
|
meta: {
|
||
|
docs: {
|
||
|
description: "Require or disallow semicolons instead of ASI",
|
||
|
category: "Extension Rules",
|
||
|
recommended: false,
|
||
|
extensionRule: "semi",
|
||
|
},
|
||
|
type: "layout",
|
||
|
fixable: "code",
|
||
|
schema: {
|
||
|
anyOf: [
|
||
|
{
|
||
|
type: "array",
|
||
|
items: [
|
||
|
{
|
||
|
type: "string",
|
||
|
enum: ["never"],
|
||
|
},
|
||
|
{
|
||
|
type: "object",
|
||
|
properties: {
|
||
|
beforeStatementContinuationChars: {
|
||
|
type: "string",
|
||
|
enum: ["always", "any", "never"],
|
||
|
},
|
||
|
},
|
||
|
additionalProperties: false,
|
||
|
},
|
||
|
],
|
||
|
minItems: 0,
|
||
|
maxItems: 2,
|
||
|
},
|
||
|
{
|
||
|
type: "array",
|
||
|
items: [
|
||
|
{
|
||
|
type: "string",
|
||
|
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 sourceCode = (0, compat_1.getSourceCode)(context);
|
||
|
if (!sourceCode.parserServices.isAstro) {
|
||
|
return {};
|
||
|
}
|
||
|
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 &&
|
||
|
"omitLastInOneLineBlock" in options &&
|
||
|
options.omitLastInOneLineBlock);
|
||
|
const exceptOneLineClassBody = Boolean(options &&
|
||
|
"omitLastInOneLineClassBody" in options &&
|
||
|
options.omitLastInOneLineClassBody);
|
||
|
const beforeStatementContinuationChars = (options &&
|
||
|
"beforeStatementContinuationChars" in options &&
|
||
|
options.beforeStatementContinuationChars) ||
|
||
|
"any";
|
||
|
function report(node, missing = false) {
|
||
|
const lastToken = sourceCode.getLastToken(node);
|
||
|
let messageId = "missingSemi";
|
||
|
let fix, loc;
|
||
|
if (!missing) {
|
||
|
loc = {
|
||
|
start: lastToken.loc.end,
|
||
|
end: (0, ast_utils_1.getNextLocation)(sourceCode, lastToken.loc.end),
|
||
|
};
|
||
|
fix = function (fixer) {
|
||
|
return fixer.insertTextAfter(lastToken, ";");
|
||
|
};
|
||
|
}
|
||
|
else {
|
||
|
messageId = "extraSemi";
|
||
|
loc = lastToken.loc;
|
||
|
fix = function (fixer) {
|
||
|
return new fix_tracker_1.default(fixer, sourceCode)
|
||
|
.retainSurroundingTokens(lastToken)
|
||
|
.remove(lastToken);
|
||
|
};
|
||
|
}
|
||
|
context.report({
|
||
|
node,
|
||
|
loc,
|
||
|
messageId,
|
||
|
fix,
|
||
|
});
|
||
|
}
|
||
|
function isRedundantSemi(semiToken) {
|
||
|
const nextToken = sourceCode.getTokenAfter(semiToken);
|
||
|
return (!nextToken ||
|
||
|
(0, eslint_utils_1.isClosingBraceToken)(nextToken) ||
|
||
|
(0, eslint_utils_1.isSemicolonToken)(nextToken));
|
||
|
}
|
||
|
function isEndOfArrowBlock(lastToken) {
|
||
|
if (!(0, eslint_utils_1.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 &&
|
||
|
"name" in node.key &&
|
||
|
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 Boolean(nextToken) && (0, ast_utils_1.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);
|
||
|
const lastToken = sourceCode.getLastToken(node, 1);
|
||
|
if (isEndOfArrowBlock(lastToken))
|
||
|
return false;
|
||
|
return true;
|
||
|
}
|
||
|
function maybeAsiHazardBefore(token) {
|
||
|
return (Boolean(token) &&
|
||
|
OPT_OUT_PATTERN.test(token.value) &&
|
||
|
token.value !== "++" &&
|
||
|
token.value !== "--" &&
|
||
|
token.value !== "---");
|
||
|
}
|
||
|
function canRemoveSemicolon(node) {
|
||
|
const lastToken = sourceCode.getLastToken(node);
|
||
|
if (isRedundantSemi(lastToken))
|
||
|
return true;
|
||
|
if (maybeClassFieldAsiHazard(node))
|
||
|
return false;
|
||
|
if (isOnSameLineWithNextToken(node))
|
||
|
return false;
|
||
|
if (node.type !== "PropertyDefinition" &&
|
||
|
beforeStatementContinuationChars === "never" &&
|
||
|
!maybeAsiHazardAfter(node))
|
||
|
return true;
|
||
|
const nextToken = sourceCode.getTokenAfter(node);
|
||
|
if (!maybeAsiHazardBefore(nextToken))
|
||
|
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 lastToken = sourceCode.getLastToken(node);
|
||
|
const isSemi = (0, eslint_utils_1.isSemicolonToken)(lastToken);
|
||
|
if (never) {
|
||
|
const nextToken = sourceCode.getTokenAfter(node);
|
||
|
if (isSemi && canRemoveSemicolon(node))
|
||
|
report(node, true);
|
||
|
else if (!isSemi &&
|
||
|
beforeStatementContinuationChars === "always" &&
|
||
|
node.type !== "PropertyDefinition" &&
|
||
|
maybeAsiHazardBefore(nextToken))
|
||
|
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 (node.declaration.type === "TSInterfaceDeclaration")
|
||
|
return;
|
||
|
if (!/(?:Class|Function)Declaration/u.test(node.declaration.type))
|
||
|
checkForSemicolon(node);
|
||
|
},
|
||
|
PropertyDefinition: checkForSemicolon,
|
||
|
TSAbstractPropertyDefinition: checkForSemicolon,
|
||
|
TSDeclareFunction: checkForSemicolon,
|
||
|
TSExportAssignment: checkForSemicolon,
|
||
|
TSImportEqualsDeclaration: checkForSemicolon,
|
||
|
TSTypeAliasDeclaration: checkForSemicolon,
|
||
|
TSEmptyBodyFunctionExpression: checkForSemicolon,
|
||
|
};
|
||
|
},
|
||
|
});
|