- Delete old Vite+Svelte frontend - Initialize new SvelteKit project with TypeScript - Configure Tailwind CSS v4 + DaisyUI - Implement JWT authentication with auto-refresh - Create login page with form validation (Zod) - Add protected route guards - Update Docker configuration for single-stage build - Add E2E tests with Playwright (6/11 passing) - Fix Svelte 5 reactivity with $state() runes Known issues: - 5 E2E tests failing (timing/async issues) - Token refresh implementation needs debugging - Validation error display timing
786 lines
38 KiB
JavaScript
786 lines
38 KiB
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 === '?.';
|
|
}
|