eslint-plugin-svelte
Version:
ESLint plugin for Svelte using AST
786 lines (785 loc) • 38.4 kB
JavaScript
import { getFirstAndLastTokens } from './commons.js';
import { isArrowToken, isClosingBraceToken, isClosingBracketToken, isClosingParenToken, isNotClosingParenToken, isNotOpeningParenToken, isNotSemicolonToken, isOpeningBraceToken, isOpeningBracketToken, isOpeningParenToken, isSemicolonToken } from '@eslint-community/eslint-utils';
import { getParent } from '../../utils/ast-utils.js';
/**
* Creates AST event handlers for ES nodes.
*
* @param context The rule context.
* @returns AST event handlers.
*/
export function defineVisitor(context) {
const { sourceCode, offsets, options } = context;
/**
* Find the head of chaining nodes.
*/
function getChainHeadNode(node) {
let target = node;
let parent = getParent(target);
while (parent &&
(parent.type === 'AssignmentExpression' ||
parent.type === 'AssignmentPattern' ||
parent.type === 'BinaryExpression' ||
parent.type === 'LogicalExpression')) {
const prevToken = sourceCode.getTokenBefore(target);
const nextToken = sourceCode.getTokenAfter(target);
if (prevToken &&
isOpeningParenToken(prevToken) &&
nextToken &&
isClosingParenToken(nextToken)) {
// The chain is broken because it is enclosed in parentheses.
break;
}
target = parent;
parent = getParent(target);
}
return target;
}
const visitor = {
Program(node) {
for (const body of node.body) {
if (body.type === 'SvelteText' && !body.value.trim()) {
continue;
}
offsets.setStartOffsetToken(sourceCode.getFirstToken(body), 0);
}
},
ArrayExpression(node) {
const firstToken = sourceCode.getFirstToken(node);
const rightToken = sourceCode.getTokenAfter(node.elements[node.elements.length - 1] || firstToken, { filter: isClosingBracketToken, includeComments: false });
offsets.setOffsetElementList(node.elements, firstToken, rightToken, 1);
},
ArrayPattern(node) {
visitor.ArrayExpression(node);
},
ArrowFunctionExpression(node) {
const [firstToken, secondToken] = sourceCode.getFirstTokens(node, {
count: 2,
includeComments: false
});
const leftToken = node.async ? secondToken : firstToken;
const arrowToken = sourceCode.getTokenBefore(node.body, {
filter: isArrowToken,
includeComments: false
});
if (node.async) {
offsets.setOffsetToken(secondToken, 1, firstToken);
}
if (isOpeningParenToken(leftToken)) {
const rightToken = sourceCode.getTokenAfter(node.params[node.params.length - 1] || leftToken, { filter: isClosingParenToken, includeComments: false });
offsets.setOffsetElementList(node.params, leftToken, rightToken, 1);
}
offsets.setOffsetToken(arrowToken, 1, firstToken);
const bodyFirstToken = sourceCode.getFirstToken(node.body);
offsets.setOffsetToken(bodyFirstToken, isOpeningBraceToken(bodyFirstToken) ? 0 : 1, firstToken);
},
AssignmentExpression(node) {
const baseNode = getChainHeadNode(node);
const opToken = sourceCode.getTokenAfter(node.left, {
filter: isNotClosingParenToken,
includeComments: false
});
const baseToken = baseNode.type === 'AssignmentExpression' || baseNode.type === 'AssignmentPattern'
? sourceCode.getFirstToken(baseNode)
: getFirstAndLastTokens(sourceCode, baseNode).firstToken;
const leftToken = getFirstAndLastTokens(sourceCode, node.left).firstToken;
const rightToken = getFirstAndLastTokens(sourceCode, node.right).firstToken;
offsets.setOffsetToken([leftToken === baseToken ? null : leftToken, opToken, rightToken], 1, baseToken);
},
AssignmentPattern(node) {
visitor.AssignmentExpression(node);
},
BinaryExpression(node) {
visitor.AssignmentExpression(node);
},
LogicalExpression(node) {
visitor.AssignmentExpression(node);
},
AwaitExpression(node) {
// `await`, `...`, or UnaryOperator
const firstToken = sourceCode.getFirstToken(node);
const nextToken = sourceCode.getTokenAfter(firstToken);
offsets.setOffsetToken(nextToken, 1, firstToken);
},
RestElement(node) {
visitor.AwaitExpression(node);
},
SpreadElement(node) {
visitor.AwaitExpression(node);
},
UnaryExpression(node) {
visitor.AwaitExpression(node);
},
BlockStatement(node) {
offsets.setOffsetElementList(node.body, sourceCode.getFirstToken(node), sourceCode.getLastToken(node), 1);
},
ClassBody(node) {
visitor.BlockStatement(node);
},
BreakStatement(node) {
if (node.label) {
const firstToken = sourceCode.getFirstToken(node);
const nextToken = sourceCode.getTokenAfter(firstToken);
offsets.setOffsetToken(nextToken, 1, firstToken);
}
},
ContinueStatement(node) {
visitor.BreakStatement(node);
},
CallExpression(node) {
const typeArguments = node.typeArguments ??
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Support old typescript-eslint
node.typeParameters;
const firstToken = sourceCode.getFirstToken(node);
const leftParenToken = sourceCode.getTokenAfter(typeArguments || node.callee, {
filter: isOpeningParenToken,
includeComments: false
});
const rightParenToken = sourceCode.getLastToken(node);
if (typeArguments) {
offsets.setOffsetToken(sourceCode.getFirstToken(typeArguments), 1, firstToken);
}
for (const optionalToken of sourceCode.getTokensBetween(sourceCode.getLastToken(typeArguments || node.callee), leftParenToken, { filter: isOptionalToken, includeComments: false })) {
offsets.setOffsetToken(optionalToken, 1, firstToken);
}
offsets.setOffsetToken(leftParenToken, 1, firstToken);
offsets.setOffsetElementList(node.arguments, leftParenToken, rightParenToken, 1);
},
CatchClause(node) {
const catchToken = sourceCode.getFirstToken(node);
if (node.param != null) {
const leftParenToken = sourceCode.getTokenBefore(node.param);
const rightParenToken = sourceCode.getTokenAfter(node.param);
offsets.setOffsetToken(leftParenToken, 1, catchToken);
offsets.setOffsetElementList([node.param], leftParenToken, rightParenToken, 1);
}
const bodyToken = sourceCode.getFirstToken(node.body);
offsets.setOffsetToken(bodyToken, 0, catchToken);
},
ClassDeclaration(node) {
const classToken = sourceCode.getFirstToken(node);
if (node.id != null) {
offsets.setOffsetToken(sourceCode.getFirstToken(node.id), 1, classToken);
}
if (node.superClass != null) {
const extendsToken = sourceCode.getTokenBefore(node.superClass);
const superClassToken = sourceCode.getTokenAfter(extendsToken);
offsets.setOffsetToken(extendsToken, 1, classToken);
offsets.setOffsetToken(superClassToken, 1, extendsToken);
}
const bodyToken = sourceCode.getFirstToken(node.body);
offsets.setOffsetToken(bodyToken, 0, classToken);
},
ClassExpression(node) {
visitor.ClassDeclaration(node);
},
ConditionalExpression(node) {
const questionToken = sourceCode.getTokenAfter(node.test, {
filter: isNotClosingParenToken,
includeComments: false
});
const consequentToken = sourceCode.getTokenAfter(questionToken);
const colonToken = sourceCode.getTokenAfter(node.consequent, {
filter: isNotClosingParenToken,
includeComments: false
});
const alternateToken = sourceCode.getTokenAfter(colonToken);
let baseNode = node;
let parent = getParent(baseNode);
while (parent && parent.type === 'ConditionalExpression' && parent.alternate === baseNode) {
baseNode = parent;
parent = getParent(baseNode);
}
const baseToken = sourceCode.getFirstToken(baseNode);
offsets.setOffsetToken([questionToken, colonToken], 1, baseToken);
offsets.setOffsetToken(consequentToken, 1, questionToken);
offsets.setOffsetToken(alternateToken, 1, colonToken);
},
DoWhileStatement(node) {
const doToken = sourceCode.getFirstToken(node);
const whileToken = sourceCode.getTokenAfter(node.body, {
filter: isNotClosingParenToken,
includeComments: false
});
const leftParenToken = sourceCode.getTokenAfter(whileToken);
const rightParenToken = sourceCode.getTokenAfter(node.test);
const bodyFirstToken = sourceCode.getFirstToken(node.body);
offsets.setOffsetToken(bodyFirstToken, isOpeningBraceToken(bodyFirstToken) ? 0 : 1, doToken);
offsets.setOffsetToken(whileToken, 0, doToken);
offsets.setOffsetToken(leftParenToken, 1, whileToken);
offsets.setOffsetElementList([node.test], leftParenToken, rightParenToken, 1);
},
ExportAllDeclaration(node) {
const exportToken = sourceCode.getFirstToken(node);
const tokens = sourceCode.getTokensBetween(exportToken, node.source);
const fromIndex = tokens.findIndex((t) => t.value === 'from');
const fromToken = tokens[fromIndex];
const beforeTokens = tokens.slice(0, fromIndex);
const afterTokens = [...tokens.slice(fromIndex + 1), sourceCode.getFirstToken(node.source)];
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- type bug?
if (!node.exported) {
// export * from "mod"
offsets.setOffsetToken(beforeTokens, 1, exportToken);
}
else {
// export * as foo from "mod"
const asIndex = beforeTokens.findIndex((t) => t.value === 'as');
offsets.setOffsetToken(beforeTokens.slice(0, asIndex), 1, exportToken);
offsets.setOffsetToken(beforeTokens.slice(asIndex), 1, beforeTokens[asIndex - 1]);
}
offsets.setOffsetToken(fromToken, 0, exportToken);
offsets.setOffsetToken(afterTokens, 1, fromToken);
// assertions
const lastToken = sourceCode.getLastToken(node, {
filter: isNotSemicolonToken,
includeComments: false
});
const assertionTokens = sourceCode.getTokensBetween(node.source, lastToken);
if (assertionTokens.length) {
const assertToken = assertionTokens.shift();
offsets.setOffsetToken(assertToken, 0, exportToken);
const assertionOpen = assertionTokens.shift();
if (assertionOpen) {
offsets.setOffsetToken(assertionOpen, 1, assertToken);
offsets.setOffsetElementList(assertionTokens, assertionOpen, lastToken, 1);
}
}
},
ExportDefaultDeclaration(node) {
const exportToken = sourceCode.getFirstToken(node);
const declarationToken = getFirstAndLastTokens(sourceCode, node.declaration).firstToken;
const defaultTokens = sourceCode.getTokensBetween(exportToken, declarationToken);
offsets.setOffsetToken([...defaultTokens, declarationToken], 1, exportToken);
},
ExportNamedDeclaration(node) {
const exportToken = sourceCode.getFirstToken(node);
if (node.declaration) {
// export var foo = 1;
const declarationToken = sourceCode.getFirstToken(node.declaration);
offsets.setOffsetToken(declarationToken, 1, exportToken);
}
else {
const firstSpecifier = node.specifiers[0];
if (!firstSpecifier || firstSpecifier.type === 'ExportSpecifier') {
// export {foo, bar}; or export {foo, bar} from "mod";
const leftBraceTokens = firstSpecifier
? sourceCode.getTokensBetween(exportToken, firstSpecifier)
: [sourceCode.getTokenAfter(exportToken)];
const rightBraceToken = node.source
? sourceCode.getTokenBefore(node.source, {
filter: isClosingBraceToken,
includeComments: false
})
: sourceCode.getLastToken(node, {
filter: isClosingBraceToken,
includeComments: false
});
offsets.setOffsetToken(leftBraceTokens, 0, exportToken);
offsets.setOffsetElementList(node.specifiers, leftBraceTokens[leftBraceTokens.length - 1], rightBraceToken, 1);
if (node.source) {
const [fromToken, ...tokens] = sourceCode.getTokensBetween(rightBraceToken, node.source);
offsets.setOffsetToken(fromToken, 0, exportToken);
offsets.setOffsetToken([...tokens, sourceCode.getFirstToken(node.source)], 1, fromToken);
// assertions
const lastToken = sourceCode.getLastToken(node, {
filter: isNotSemicolonToken,
includeComments: false
});
const assertionTokens = sourceCode.getTokensBetween(node.source, lastToken);
if (assertionTokens.length) {
const assertToken = assertionTokens.shift();
offsets.setOffsetToken(assertToken, 0, exportToken);
const assertionOpen = assertionTokens.shift();
if (assertionOpen) {
offsets.setOffsetToken(assertionOpen, 1, assertToken);
offsets.setOffsetElementList(assertionTokens, assertionOpen, lastToken, 1);
}
}
}
}
else {
// maybe babel-eslint
}
}
},
ExportSpecifier(node) {
const tokens = sourceCode.getTokens(node);
let firstToken = tokens.shift();
if (firstToken.value === 'type') {
const typeToken = firstToken;
firstToken = tokens.shift();
offsets.setOffsetToken(firstToken, 0, typeToken);
}
offsets.setOffsetToken(tokens, 1, firstToken);
},
ForInStatement(node) {
const forToken = sourceCode.getFirstToken(node);
const awaitToken = (node.type === 'ForOfStatement' && node.await && sourceCode.getTokenAfter(forToken)) ||
null;
const leftParenToken = sourceCode.getTokenAfter(awaitToken || forToken);
const leftToken = sourceCode.getFirstToken(node.left);
const inOrOfToken = sourceCode.getTokenAfter(node.left, {
filter: isNotClosingParenToken,
includeComments: false
});
const rightToken = sourceCode.getTokenAfter(inOrOfToken);
const rightParenToken = sourceCode.getTokenBefore(node.body, {
filter: isNotOpeningParenToken,
includeComments: false
});
if (awaitToken != null) {
offsets.setOffsetToken(awaitToken, 0, forToken);
}
offsets.setOffsetToken(leftParenToken, 1, forToken);
offsets.setOffsetToken(leftToken, 1, leftParenToken);
offsets.setOffsetToken([inOrOfToken, rightToken], 1, leftToken);
offsets.setOffsetToken(rightParenToken, 0, leftParenToken);
const bodyFirstToken = sourceCode.getFirstToken(node.body);
offsets.setOffsetToken(bodyFirstToken, isOpeningBraceToken(bodyFirstToken) ? 0 : 1, forToken);
},
ForOfStatement(node) {
visitor.ForInStatement(node);
},
ForStatement(node) {
const forToken = sourceCode.getFirstToken(node);
const leftParenToken = sourceCode.getTokenAfter(forToken);
const rightParenToken = sourceCode.getTokenBefore(node.body, {
filter: isNotOpeningParenToken,
includeComments: false
});
offsets.setOffsetToken(leftParenToken, 1, forToken);
offsets.setOffsetElementList([node.init, node.test, node.update], leftParenToken, rightParenToken, 1);
const bodyFirstToken = sourceCode.getFirstToken(node.body);
offsets.setOffsetToken(bodyFirstToken, isOpeningBraceToken(bodyFirstToken) ? 0 : 1, forToken);
},
FunctionDeclaration(node) {
const firstToken = sourceCode.getFirstToken(node);
const leftParenToken = sourceCode.getTokenBefore(node.params[0] ||
node.returnType ||
sourceCode.getTokenBefore(node.body), {
filter: isOpeningParenToken,
includeComments: false
});
let bodyBaseToken = null;
if (firstToken.type === 'Punctuator') {
// method
bodyBaseToken = sourceCode.getFirstToken(getParent(node));
}
else {
let tokenOffset = 0;
for (const token of sourceCode.getTokensBetween(firstToken, leftParenToken)) {
if (token.value === '<') {
break;
}
if (token.value === '*' || (node.id && token.range[0] === node.id.range[0])) {
tokenOffset = 1;
}
offsets.setOffsetToken(token, tokenOffset, firstToken);
}
bodyBaseToken = firstToken;
}
const rightParenToken = sourceCode.getTokenAfter(node.params[node.params.length - 1] || leftParenToken, { filter: isClosingParenToken, includeComments: false });
offsets.setOffsetToken(leftParenToken, 1, bodyBaseToken);
offsets.setOffsetElementList(node.params, leftParenToken, rightParenToken, 1);
const bodyToken = sourceCode.getFirstToken(node.body);
offsets.setOffsetToken(bodyToken, 0, bodyBaseToken);
},
FunctionExpression(node) {
visitor.FunctionDeclaration(node);
},
IfStatement(node) {
const [ifToken, ifLeftParenToken] = sourceCode.getFirstTokens(node, {
count: 2,
includeComments: false
});
const ifRightParenToken = sourceCode.getTokenBefore(node.consequent, {
filter: isClosingParenToken,
includeComments: false
});
offsets.setOffsetToken(ifLeftParenToken, 1, ifToken);
offsets.setOffsetToken(ifRightParenToken, 0, ifLeftParenToken);
const consequentFirstToken = sourceCode.getFirstToken(node.consequent);
offsets.setOffsetToken(consequentFirstToken, isOpeningBraceToken(consequentFirstToken) ? 0 : 1, ifToken);
if (node.alternate != null) {
const elseToken = sourceCode.getTokenAfter(node.consequent, {
filter: isNotClosingParenToken,
includeComments: false
});
offsets.setOffsetToken(elseToken, 0, ifToken);
const alternateFirstToken = sourceCode.getFirstToken(node.alternate);
offsets.setOffsetToken(alternateFirstToken, isOpeningBraceToken(alternateFirstToken) ? 0 : 1, elseToken);
}
},
ImportDeclaration(node) {
const importToken = sourceCode.getFirstToken(node);
const tokens = sourceCode.getTokensBetween(importToken, node.source);
const fromIndex = tokens.map((t) => t.value).lastIndexOf('from');
const { fromToken, beforeTokens, afterTokens } = fromIndex >= 0
? {
fromToken: tokens[fromIndex],
beforeTokens: tokens.slice(0, fromIndex),
afterTokens: [...tokens.slice(fromIndex + 1), sourceCode.getFirstToken(node.source)]
}
: {
fromToken: null,
beforeTokens: [...tokens, sourceCode.getFirstToken(node.source)],
afterTokens: []
};
const namedSpecifiers = [];
for (const specifier of node.specifiers) {
if (specifier.type === 'ImportSpecifier') {
namedSpecifiers.push(specifier);
}
else {
const removeTokens = sourceCode.getTokens(specifier);
removeTokens.shift();
for (const token of removeTokens) {
const i = beforeTokens.indexOf(token);
if (i >= 0) {
beforeTokens.splice(i, 1);
}
}
}
}
if (namedSpecifiers.length) {
const leftBrace = sourceCode.getTokenBefore(namedSpecifiers[0]);
const rightBrace = sourceCode.getTokenAfter(namedSpecifiers[namedSpecifiers.length - 1], {
filter: isClosingBraceToken,
includeComments: false
});
offsets.setOffsetElementList(namedSpecifiers, leftBrace, rightBrace, 1);
for (const token of [...sourceCode.getTokensBetween(leftBrace, rightBrace), rightBrace]) {
const i = beforeTokens.indexOf(token);
if (i >= 0) {
beforeTokens.splice(i, 1);
}
}
}
if (beforeTokens.every((t) => isOpeningBraceToken(t) || isClosingBraceToken(t))) {
offsets.setOffsetToken(beforeTokens, 0, importToken);
}
else {
offsets.setOffsetToken(beforeTokens, 1, importToken);
}
if (fromToken) {
offsets.setOffsetToken(fromToken, 0, importToken);
offsets.setOffsetToken(afterTokens, 1, fromToken);
}
// assertions
const lastToken = sourceCode.getLastToken(node, {
filter: isNotSemicolonToken,
includeComments: false
});
const assertionTokens = sourceCode.getTokensBetween(node.source, lastToken);
if (assertionTokens.length) {
const assertToken = assertionTokens.shift();
offsets.setOffsetToken(assertToken, 0, importToken);
const assertionOpen = assertionTokens.shift();
if (assertionOpen) {
offsets.setOffsetToken(assertionOpen, 1, assertToken);
offsets.setOffsetElementList(assertionTokens, assertionOpen, lastToken, 1);
}
}
},
ImportExpression(node) {
const firstToken = sourceCode.getFirstToken(node);
const rightToken = sourceCode.getLastToken(node);
const leftToken = sourceCode.getTokenAfter(firstToken, {
filter: isOpeningParenToken,
includeComments: false
});
offsets.setOffsetToken(leftToken, 1, firstToken);
offsets.setOffsetElementList([node.source], leftToken, rightToken, 1);
},
ImportNamespaceSpecifier(node) {
const tokens = sourceCode.getTokens(node);
const firstToken = tokens.shift();
offsets.setOffsetToken(tokens, 1, firstToken);
},
ImportSpecifier(node) {
visitor.ExportSpecifier(node);
},
LabeledStatement(node) {
const labelToken = sourceCode.getFirstToken(node);
const colonToken = sourceCode.getTokenAfter(labelToken);
const bodyToken = sourceCode.getTokenAfter(colonToken);
offsets.setOffsetToken([colonToken, bodyToken], 1, labelToken);
},
SvelteReactiveStatement(node) {
visitor.LabeledStatement(node);
},
MemberExpression(node) {
const objectToken = sourceCode.getFirstToken(node);
if (node.type === 'MemberExpression' && node.computed) {
const leftBracketToken = sourceCode.getTokenBefore(node.property, {
filter: isOpeningBracketToken,
includeComments: false
});
const rightBracketToken = sourceCode.getTokenAfter(node.property, {
filter: isClosingBracketToken,
includeComments: false
});
for (const optionalToken of sourceCode.getTokensBetween(sourceCode.getLastToken(node.object), leftBracketToken, { filter: isOptionalToken, includeComments: false })) {
offsets.setOffsetToken(optionalToken, 1, objectToken);
}
offsets.setOffsetToken(leftBracketToken, 1, objectToken);
offsets.setOffsetElementList([node.property], leftBracketToken, rightBracketToken, 1);
}
else {
const dotToken = sourceCode.getTokenBefore(node.property);
const propertyToken = sourceCode.getTokenAfter(dotToken);
offsets.setOffsetToken([dotToken, propertyToken], 1, objectToken);
}
},
MetaProperty(node) {
visitor.MemberExpression(node);
},
MethodDefinition(node) {
const firstToken = sourceCode.getFirstToken(node);
const keyTokens = getFirstAndLastTokens(sourceCode, node.key);
const prefixTokens = sourceCode.getTokensBetween(firstToken, keyTokens.firstToken);
if (node.computed) {
prefixTokens.pop(); // pop [
}
offsets.setOffsetToken(prefixTokens, 0, firstToken);
let lastKeyToken;
if (node.computed) {
const leftBracketToken = sourceCode.getTokenBefore(keyTokens.firstToken);
const rightBracketToken = (lastKeyToken = sourceCode.getTokenAfter(keyTokens.lastToken));
offsets.setOffsetToken(leftBracketToken, 0, firstToken);
offsets.setOffsetElementList([node.key], leftBracketToken, rightBracketToken, 1);
}
else {
offsets.setOffsetToken(keyTokens.firstToken, 0, firstToken);
lastKeyToken = keyTokens.lastToken;
}
if (node.value) {
const initToken = sourceCode.getFirstToken(node.value);
offsets.setOffsetToken([...sourceCode.getTokensBetween(lastKeyToken, initToken), initToken], 1, lastKeyToken);
}
},
Property(node) {
visitor.MethodDefinition(node);
},
NewExpression(node) {
const typeArguments = node.typeArguments ??
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Support old typescript-eslint
node.typeParameters;
const newToken = sourceCode.getFirstToken(node);
const calleeTokens = getFirstAndLastTokens(sourceCode, node.callee);
offsets.setOffsetToken(calleeTokens.firstToken, 1, newToken);
if (typeArguments) {
offsets.setOffsetToken(sourceCode.getFirstToken(typeArguments), 1, calleeTokens.firstToken);
}
const leftParenBefore = typeArguments || calleeTokens.lastToken;
if (node.arguments.length || leftParenBefore.range[1] < node.range[1]) {
const rightParenToken = sourceCode.getLastToken(node);
const leftParenToken = sourceCode.getTokenAfter(leftParenBefore);
offsets.setOffsetToken(leftParenToken, 1, calleeTokens.firstToken);
offsets.setOffsetElementList(node.arguments, leftParenToken, rightParenToken, 1);
}
},
ObjectExpression(node) {
const firstToken = sourceCode.getFirstToken(node);
const rightToken = sourceCode.getTokenAfter(node.properties[node.properties.length - 1] || firstToken, { filter: isClosingBraceToken, includeComments: false });
offsets.setOffsetElementList(node.properties, firstToken, rightToken, 1);
},
ObjectPattern(node) {
visitor.ObjectExpression(node);
},
PropertyDefinition(node) {
visitor.MethodDefinition(node);
},
ReturnStatement(node) {
if (node.argument) {
const firstToken = sourceCode.getFirstToken(node);
const nextToken = sourceCode.getTokenAfter(firstToken);
offsets.setOffsetToken(nextToken, 1, firstToken);
}
},
ThrowStatement(node) {
visitor.ReturnStatement(node);
},
SequenceExpression(node) {
const firstToken = sourceCode.getFirstToken(node);
offsets.setOffsetElementList(node.expressions, firstToken, null, 0);
},
SwitchCase(node) {
const caseToken = sourceCode.getFirstToken(node);
if (node.test != null) {
const testTokens = getFirstAndLastTokens(sourceCode, node.test);
const colonToken = sourceCode.getTokenAfter(testTokens.lastToken);
offsets.setOffsetToken([testTokens.firstToken, colonToken], 1, caseToken);
}
else {
const colonToken = sourceCode.getTokenAfter(caseToken);
offsets.setOffsetToken(colonToken, 1, caseToken);
}
if (node.consequent.length === 1 && node.consequent[0].type === 'BlockStatement') {
offsets.setOffsetToken(sourceCode.getFirstToken(node.consequent[0]), 0, caseToken);
}
else {
for (const statement of node.consequent) {
offsets.setOffsetToken(getFirstAndLastTokens(sourceCode, statement).firstToken, 1, caseToken);
}
}
},
SwitchStatement(node) {
const switchToken = sourceCode.getFirstToken(node);
const { firstToken: leftParenToken, lastToken: rightParenToken } = getFirstAndLastTokens(sourceCode, node.discriminant);
const leftBraceToken = sourceCode.getTokenAfter(rightParenToken);
const rightBraceToken = sourceCode.getLastToken(node);
offsets.setOffsetToken(leftParenToken, 1, switchToken);
offsets.setOffsetElementList([node.discriminant], leftParenToken, rightParenToken, 1);
offsets.setOffsetToken(leftBraceToken, 0, switchToken);
offsets.setOffsetElementList(node.cases, leftBraceToken, rightBraceToken, options.switchCase);
},
TaggedTemplateExpression(node) {
const tagTokens = getFirstAndLastTokens(sourceCode, node.tag);
offsets.setOffsetToken(sourceCode.getFirstToken(node.quasi), 1, tagTokens.firstToken);
},
TemplateLiteral(node) {
const firstToken = sourceCode.getFirstToken(node);
const quasiTokens = node.quasis.slice(1).map((n) => sourceCode.getFirstToken(n));
const expressionToken = node.quasis.slice(0, -1).map((n) => sourceCode.getTokenAfter(n));
offsets.setOffsetToken(quasiTokens, 0, firstToken);
offsets.setOffsetToken(expressionToken, 1, firstToken);
},
TryStatement(node) {
const tryToken = sourceCode.getFirstToken(node);
const tryBlockToken = sourceCode.getFirstToken(node.block);
offsets.setOffsetToken(tryBlockToken, 0, tryToken);
if (node.handler != null) {
const catchToken = sourceCode.getFirstToken(node.handler);
offsets.setOffsetToken(catchToken, 0, tryToken);
}
if (node.finalizer != null) {
const finallyToken = sourceCode.getTokenBefore(node.finalizer);
const finallyBlockToken = sourceCode.getFirstToken(node.finalizer);
offsets.setOffsetToken([finallyToken, finallyBlockToken], 0, tryToken);
}
},
UpdateExpression(node) {
const firstToken = sourceCode.getFirstToken(node);
const nextToken = sourceCode.getTokenAfter(firstToken);
offsets.setOffsetToken(nextToken, 1, firstToken);
},
VariableDeclaration(node) {
offsets.setOffsetElementList(node.declarations, sourceCode.getFirstToken(node), null, 1);
},
VariableDeclarator(node) {
if (node.init != null) {
const idToken = sourceCode.getFirstToken(node);
const eqToken = sourceCode.getTokenAfter(node.id);
const initToken = sourceCode.getTokenAfter(eqToken);
offsets.setOffsetToken([eqToken, initToken], 1, idToken);
}
},
WhileStatement(node) {
const firstToken = sourceCode.getFirstToken(node);
const leftParenToken = sourceCode.getTokenAfter(firstToken);
const rightParenToken = sourceCode.getTokenBefore(node.body, {
filter: isClosingParenToken,
includeComments: false
});
offsets.setOffsetToken(leftParenToken, 1, firstToken);
offsets.setOffsetToken(rightParenToken, 0, leftParenToken);
const bodyFirstToken = sourceCode.getFirstToken(node.body);
offsets.setOffsetToken(bodyFirstToken, isOpeningBraceToken(bodyFirstToken) ? 0 : 1, firstToken);
},
WithStatement(node) {
visitor.WhileStatement(node);
},
YieldExpression(node) {
if (node.argument != null) {
const [yieldToken, secondToken] = sourceCode.getFirstTokens(node, {
count: 2,
includeComments: false
});
offsets.setOffsetToken(secondToken, 1, yieldToken);
if (node.delegate) {
offsets.setOffsetToken(sourceCode.getTokenAfter(secondToken), 1, yieldToken);
}
}
},
// ----------------------------------------------------------------------
// SINGLE TOKEN NODES
// ----------------------------------------------------------------------
DebuggerStatement() {
// noop
},
Identifier() {
// noop
},
ImportDefaultSpecifier() {
// noop
},
Literal() {
// noop
},
PrivateIdentifier() {
// noop
},
Super() {
// noop
},
TemplateElement() {
// noop
},
ThisExpression() {
// noop
},
// ----------------------------------------------------------------------
// WRAPPER NODES
// ----------------------------------------------------------------------
ExpressionStatement() {
// noop
},
ChainExpression() {
// noop
},
EmptyStatement() {
// noop
}
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore
const commonVisitor = {
':statement, PropertyDefinition'(node) {
const firstToken = sourceCode.getFirstToken(node);
const lastToken = sourceCode.getLastToken(node);
if (isSemicolonToken(lastToken) && firstToken !== lastToken) {
const next = sourceCode.getTokenAfter(lastToken);
if (!next || lastToken.loc.start.line < next.loc.start.line) {
// End of line semicolons
offsets.setOffsetToken(lastToken, 0, firstToken);
}
}
},
':expression'(node) {
// Proc parentheses.
let leftToken = sourceCode.getTokenBefore(node);
let rightToken = sourceCode.getTokenAfter(node);
let firstToken = sourceCode.getFirstToken(node);
while (leftToken &&
isOpeningParenToken(leftToken) &&
rightToken &&
isClosingParenToken(rightToken)) {
offsets.setOffsetToken(firstToken, 1, leftToken);
offsets.setOffsetToken(rightToken, 0, leftToken);
firstToken = leftToken;
leftToken = sourceCode.getTokenBefore(leftToken);
rightToken = sourceCode.getTokenAfter(rightToken);
}
}
};
const v = visitor;
return {
...v,
...commonVisitor
};
}
/**
* Checks whether given text is known button type
*/
function isOptionalToken(token) {
return token.type === 'Punctuator' && token.value === '?.';
}