eslint-plugin-vue
Version:
Official ESLint plugin for Vue.js
1,041 lines (1,039 loc) • 47 kB
JavaScript
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_indent_utils = require('./indent-utils.js');
const require_indent_ts = require('./indent-ts.js');
let _eslint_community_eslint_utils = require("@eslint-community/eslint-utils");
//#region lib/utils/indent-common.ts
const LT_CHAR = /[\n\r\u2028\u2029]/;
const LINES = /[^\n\r\u2028\u2029]+(?:$|\r\n|[\n\r\u2028\u2029])/g;
const BLOCK_COMMENT_PREFIX = /^\s*\*/;
const ITERATION_OPTS = Object.freeze({
includeComments: true,
filter: require_indent_utils.isNotWhitespace
});
const PREFORMATTED_ELEMENT_NAMES = new Set(["pre", "textarea"]);
/**
* Normalize options.
*/
function parseOptions(type, options, defaultOptions) {
const ret = Object.assign({
indentChar: " ",
indentSize: 2,
baseIndent: 0,
attribute: 1,
closeBracket: {
startTag: 0,
endTag: 0,
selfClosingTag: 0
},
switchCase: 0,
alignAttributesVertically: true,
ignores: []
}, defaultOptions);
if (Number.isSafeInteger(type)) ret.indentSize = Number(type);
else if (type === "tab") {
ret.indentChar = " ";
ret.indentSize = 1;
}
if (options.baseIndent != null && Number.isSafeInteger(options.baseIndent)) ret.baseIndent = options.baseIndent;
if (options.attribute != null && Number.isSafeInteger(options.attribute)) ret.attribute = options.attribute;
if (Number.isSafeInteger(options.closeBracket)) {
const num = Number(options.closeBracket);
ret.closeBracket = {
startTag: num,
endTag: num,
selfClosingTag: num
};
} else if (options.closeBracket) ret.closeBracket = Object.assign({
startTag: 0,
endTag: 0,
selfClosingTag: 0
}, options.closeBracket);
if (options.switchCase != null && Number.isSafeInteger(options.switchCase)) ret.switchCase = options.switchCase;
if (options.alignAttributesVertically != null) ret.alignAttributesVertically = options.alignAttributesVertically;
if (options.ignores != null) ret.ignores = options.ignores;
return ret;
}
/**
* Check whether the node is at the beginning of line.
*/
function isBeginningOfLine(node, index, nodes) {
if (node != null) for (let i = index - 1; i >= 0; --i) {
const prevNode = nodes[i];
if (prevNode == null) continue;
return node.loc.start.line !== prevNode.loc.end.line;
}
return false;
}
/**
* Check whether a given token is a closing token which triggers unindent.
*/
function isClosingToken(token) {
return token != null && (token.type === "HTMLEndTagOpen" || token.type === "VExpressionEnd" || token.type === "Punctuator" && (token.value === ")" || token.value === "}" || token.value === "]"));
}
/**
* Checks whether a given token is a optional token.
*/
function isOptionalToken(token) {
return token.type === "Punctuator" && token.value === "?.";
}
/**
* Creates AST event handlers for html-indent.
*/
function defineVisitor(context, tokenStore, defaultOptions) {
if (!context.filename.endsWith(".vue")) return {};
const options = parseOptions(context.options[0], context.options[1] || {}, defaultOptions);
const sourceCode = context.sourceCode;
const offsets = /* @__PURE__ */ new Map();
const ignoreTokens = /* @__PURE__ */ new Set();
/**
* Set offset to the given tokens.
*/
function setOffset(token, offset, baseToken) {
if (!token || token === baseToken) return;
if (Array.isArray(token)) for (const t of token) {
if (!t || t === baseToken) continue;
offsets.set(t, {
baseToken,
offset,
baseline: false,
expectedIndent: void 0
});
}
else offsets.set(token, {
baseToken,
offset,
baseline: false,
expectedIndent: void 0
});
}
/**
* Copy offset to the given tokens from srcToken.
*/
function copyOffset(token, srcToken) {
if (!token) return;
const offsetData = offsets.get(srcToken);
if (!offsetData) return;
setOffset(token, offsetData.offset, offsetData.baseToken);
if (offsetData.baseline) setBaseline(token);
const o = offsets.get(token);
o.expectedIndent = offsetData.expectedIndent;
}
/**
* Set baseline flag to the given token.
*/
function setBaseline(token) {
const offsetInfo = offsets.get(token);
if (offsetInfo != null) offsetInfo.baseline = true;
}
/**
* Sets preformatted tokens to the given element node.
*/
function setPreformattedTokens(node) {
const endToken = node.endTag && tokenStore.getFirstToken(node.endTag) || tokenStore.getTokenAfter(node);
const cursorOptions = {
includeComments: true,
filter: (token) => token != null && (token.type === "HTMLText" || token.type === "HTMLRCDataText" || token.type === "HTMLTagOpen" || token.type === "HTMLEndTagOpen" || token.type === "HTMLComment")
};
const contentTokens = endToken ? tokenStore.getTokensBetween(node.startTag, endToken, cursorOptions) : tokenStore.getTokensAfter(node.startTag, cursorOptions);
for (const token of contentTokens) ignoreTokens.add(token);
ignoreTokens.add(endToken);
}
/**
* Get the first and last tokens of the given node.
* If the node is parenthesized, this gets the outermost parentheses.
* The `borderOffset` value is used to prevent false positive in the following case: `(a) => {}` The parentheses are enclosing the whole parameter part rather than the first parameter, but this offset parameter is needed to distinguish.
*/
function getFirstAndLastTokens(node, borderOffset = 0) {
borderOffset = Math.trunc(borderOffset);
let firstToken = tokenStore.getFirstToken(node);
let lastToken = tokenStore.getLastToken(node);
let t, u;
while ((t = tokenStore.getTokenBefore(firstToken)) != null && (u = tokenStore.getTokenAfter(lastToken)) != null && (0, _eslint_community_eslint_utils.isOpeningParenToken)(t) && (0, _eslint_community_eslint_utils.isClosingParenToken)(u) && t.range[0] >= borderOffset) {
firstToken = t;
lastToken = u;
}
return {
firstToken,
lastToken
};
}
/**
* Process the given node list.
* The first node is offsetted from the given left token.
* Rest nodes are adjusted to the first node.
*
* @param alignVertically The flag to align vertically. If `false`, this doesn't align vertically even if the first node is not at beginning of line.
*/
function processNodeList(nodeList, left, right, offset, alignVertically = true) {
let t;
const leftToken = left && tokenStore.getFirstToken(left);
const rightToken = right && tokenStore.getFirstToken(right);
if (nodeList.length > 0) {
let baseToken = null;
let lastToken = left;
const alignTokensBeforeBaseToken = [];
const alignTokens = [];
for (const node of nodeList) {
if (node == null) continue;
const elementTokens = getFirstAndLastTokens(node, lastToken == null ? 0 : lastToken.range[1]);
if (lastToken != null) {
t = lastToken;
while ((t = tokenStore.getTokenAfter(t, ITERATION_OPTS)) != null && t.range[1] <= elementTokens.firstToken.range[0]) if (baseToken == null) alignTokensBeforeBaseToken.push(t);
else alignTokens.push(t);
}
if (baseToken == null) baseToken = elementTokens.firstToken;
else alignTokens.push(elementTokens.firstToken);
lastToken = elementTokens.lastToken;
}
if (rightToken != null && lastToken != null) {
t = lastToken;
while ((t = tokenStore.getTokenAfter(t, ITERATION_OPTS)) != null && t.range[1] <= rightToken.range[0]) if (baseToken == null) alignTokensBeforeBaseToken.push(t);
else alignTokens.push(t);
}
if (leftToken != null) setOffset(alignTokensBeforeBaseToken, offset, leftToken);
if (baseToken != null) {
if (leftToken != null) setOffset(baseToken, offset, leftToken);
if (nodeList.some(isBeginningOfLine)) setBaseline(baseToken);
if (alignVertically === false && leftToken != null) setOffset(alignTokens, offset, leftToken);
else setOffset(alignTokens, 0, baseToken);
}
}
if (rightToken != null && leftToken != null) setOffset(rightToken, 0, leftToken);
}
/**
* Process the given node as body.
* The body node maybe a block statement or an expression node.
*/
function processMaybeBlock(node, baseToken) {
const firstToken = getFirstAndLastTokens(node).firstToken;
setOffset(firstToken, (0, _eslint_community_eslint_utils.isOpeningBraceToken)(firstToken) ? 0 : 1, baseToken);
}
/**
* Process semicolons of the given statement node.
*/
function processSemicolons(node) {
const firstToken = tokenStore.getFirstToken(node);
const lastToken = tokenStore.getLastToken(node);
if ((0, _eslint_community_eslint_utils.isSemicolonToken)(lastToken) && firstToken !== lastToken) setOffset(lastToken, 0, firstToken);
const info = offsets.get(firstToken);
const prevToken = tokenStore.getTokenBefore(firstToken);
if (info != null && prevToken && (0, _eslint_community_eslint_utils.isSemicolonToken)(prevToken) && prevToken.loc.end.line === firstToken.loc.start.line) offsets.set(prevToken, info);
}
/**
* Find the head of chaining nodes.
*/
function getChainHeadToken(node) {
const type = node.type;
while (node.parent && node.parent.type === type) {
if ((0, _eslint_community_eslint_utils.isOpeningParenToken)(tokenStore.getTokenBefore(node))) break;
node = node.parent;
}
return tokenStore.getFirstToken(node);
}
/**
* Check whether a given token is the first token of:
*
* - ExpressionStatement
* - VExpressionContainer
* - A parameter of CallExpression/NewExpression
* - An element of ArrayExpression
* - An expression of SequenceExpression
*/
function isBeginningOfElement(token, belongingNode) {
let node = belongingNode;
while (node != null && node.parent != null) {
const parent = node.parent;
if (parent.type.endsWith("Statement") || parent.type.endsWith("Declaration")) return parent.range[0] === token.range[0];
if (parent.type === "VExpressionContainer") {
if (node.range[0] !== token.range[0]) return false;
if ((0, _eslint_community_eslint_utils.isOpeningParenToken)(tokenStore.getTokenBefore(belongingNode))) return false;
return true;
}
if (parent.type === "CallExpression" || parent.type === "NewExpression") {
const openParen = tokenStore.getTokenAfter(parent.callee, _eslint_community_eslint_utils.isNotClosingParenToken);
return parent.arguments.some((param) => getFirstAndLastTokens(param, openParen.range[1]).firstToken.range[0] === token.range[0]);
}
if (parent.type === "ArrayExpression") return parent.elements.some((element) => element != null && getFirstAndLastTokens(element).firstToken.range[0] === token.range[0]);
if (parent.type === "SequenceExpression") return parent.expressions.some((expr) => getFirstAndLastTokens(expr).firstToken.range[0] === token.range[0]);
node = parent;
}
return false;
}
/**
* Set the base indentation to a given top-level AST node.
*/
function processTopLevelNode(node, expectedIndent) {
const token = tokenStore.getFirstToken(node);
const offsetInfo = offsets.get(token);
if (offsetInfo == null) offsets.set(token, {
baseToken: null,
offset: 0,
baseline: false,
expectedIndent
});
else offsetInfo.expectedIndent = expectedIndent;
}
/**
* Ignore all tokens of the given node.
*/
function ignore(node) {
for (const token of tokenStore.getTokens(node)) {
offsets.delete(token);
ignoreTokens.add(token);
}
}
/**
* Define functions to ignore nodes into the given visitor.
*/
function processIgnores(visitor) {
for (const ignorePattern of options.ignores) {
const key = `${ignorePattern}:exit`;
if (visitor.hasOwnProperty(key)) {
const handler = visitor[key];
visitor[key] = function(node, ...args) {
const ret = handler.call(this, node, ...args);
ignore(node);
return ret;
};
} else visitor[key] = ignore;
}
return visitor;
}
/**
* Calculate correct indentation of the line of the given tokens.
*/
function getExpectedIndents(tokens) {
const expectedIndents = [];
for (const [i, token] of tokens.entries()) {
const offsetInfo = offsets.get(token);
if (offsetInfo != null) if (offsetInfo.expectedIndent == null) {
const baseOffsetInfo = offsets.get(offsetInfo.baseToken);
if (baseOffsetInfo != null && baseOffsetInfo.expectedIndent != null && (i === 0 || !baseOffsetInfo.baseline)) {
expectedIndents.push(baseOffsetInfo.expectedIndent + offsetInfo.offset * options.indentSize);
if (baseOffsetInfo.baseline) break;
}
} else expectedIndents.push(offsetInfo.expectedIndent);
}
if (expectedIndents.length === 0) return null;
return {
expectedIndent: expectedIndents[0],
expectedBaseIndent: Math.min(...expectedIndents)
};
}
/**
* Get the text of the indentation part of the line which the given token is on.
*/
function getIndentText(firstToken) {
const text = sourceCode.text;
let i = firstToken.range[0] - 1;
while (i >= 0 && !LT_CHAR.test(text[i])) i -= 1;
return text.slice(i + 1, firstToken.range[0]);
}
/**
* Define the function which fixes the problem.
*/
function defineFix(token, actualIndent, expectedIndent) {
if (token.type === "Block" && token.loc.start.line !== token.loc.end.line) {
const lines = sourceCode.getText(token).match(LINES) || [];
const firstLine = lines.shift();
if (lines.every((l) => BLOCK_COMMENT_PREFIX.test(l))) return (fixer) => {
const range = [token.range[0] - actualIndent, token.range[1]];
const indent = options.indentChar.repeat(expectedIndent);
return fixer.replaceTextRange(range, `${indent}${firstLine}${lines.map((l) => l.replace(BLOCK_COMMENT_PREFIX, `${indent} *`)).join("")}`);
};
}
return (fixer) => {
const range = [token.range[0] - actualIndent, token.range[0]];
const indent = options.indentChar.repeat(expectedIndent);
return fixer.replaceTextRange(range, indent);
};
}
/**
* Validate the given token with the pre-calculated expected indentation.
*/
function validateCore(token, expectedIndent, optionalExpectedIndents) {
const line = token.loc.start.line;
const indentText = getIndentText(token);
if (indentText.trim() !== "") return;
const actualIndent = token.loc.start.column;
const unit = options.indentChar === " " ? "tab" : "space";
for (const [i, char] of [...indentText].entries()) if (char !== options.indentChar) {
context.report({
loc: {
start: {
line,
column: i
},
end: {
line,
column: i + 1
}
},
message: "Expected {{expected}} character, but found {{actual}} character.",
data: {
expected: JSON.stringify(options.indentChar),
actual: JSON.stringify(char)
},
fix: defineFix(token, actualIndent, expectedIndent)
});
return;
}
if (actualIndent !== expectedIndent && (optionalExpectedIndents == null || !optionalExpectedIndents.includes(actualIndent))) context.report({
loc: {
start: {
line,
column: 0
},
end: {
line,
column: actualIndent
}
},
message: "Expected indentation of {{expectedIndent}} {{unit}}{{expectedIndentPlural}} but found {{actualIndent}} {{unit}}{{actualIndentPlural}}.",
data: {
expectedIndent,
actualIndent,
unit,
expectedIndentPlural: expectedIndent === 1 ? "" : "s",
actualIndentPlural: actualIndent === 1 ? "" : "s"
},
fix: defineFix(token, actualIndent, expectedIndent)
});
}
/**
* Get the expected indent of comments.
*/
function getCommentExpectedIndents(nextToken, nextExpectedIndent, lastExpectedIndent) {
if (typeof lastExpectedIndent === "number" && isClosingToken(nextToken)) {
if (nextExpectedIndent === lastExpectedIndent) return [nextExpectedIndent + options.indentSize, nextExpectedIndent];
return [lastExpectedIndent, nextExpectedIndent];
}
return [nextExpectedIndent];
}
/**
* Validate indentation of the line that the given tokens are on.
* @param tokens The tokens on the same line to validate.
* @param comments The comments which are on the immediately previous lines of the tokens.
* @param lastToken The last validated token. Comments can adjust to the token.
*/
function validate(tokens, comments, lastToken) {
const firstToken = tokens[0];
const actualIndent = firstToken.loc.start.column;
const expectedIndents = getExpectedIndents(tokens);
if (!expectedIndents) return;
const expectedBaseIndent = expectedIndents.expectedBaseIndent;
const expectedIndent = expectedIndents.expectedIndent;
const baseline = /* @__PURE__ */ new Set();
for (const token of tokens) {
const offsetInfo = offsets.get(token);
if (offsetInfo != null) if (offsetInfo.baseline) {
offsetInfo.expectedIndent = options.indentChar === " " ? Math.max(0, token.loc.start.column + expectedBaseIndent - actualIndent) : expectedBaseIndent + (token === tokens[0] ? 0 : 1);
baseline.add(token);
} else if (baseline.has(offsetInfo.baseToken)) {
offsetInfo.expectedIndent = offsets.get(offsetInfo.baseToken).expectedIndent;
baseline.add(token);
} else offsetInfo.expectedIndent = expectedBaseIndent;
}
if (ignoreTokens.has(firstToken)) return;
const lastOffsetInfo = offsets.get(lastToken);
const commentOptionalExpectedIndents = getCommentExpectedIndents(firstToken, expectedIndent, lastOffsetInfo && lastOffsetInfo.expectedIndent);
for (const comment of comments) {
const commentExpectedIndents = getExpectedIndents([comment]);
validateCore(comment, commentExpectedIndents ? commentExpectedIndents.expectedIndent : commentOptionalExpectedIndents[0], commentOptionalExpectedIndents);
}
validateCore(firstToken, expectedIndent);
}
const knownNodes = /* @__PURE__ */ new Set();
const visitor = {
VAttribute(node) {
const keyToken = tokenStore.getFirstToken(node);
const eqToken = tokenStore.getTokenAfter(node.key);
if (eqToken != null && eqToken.range[1] <= node.range[1]) {
setOffset(eqToken, 1, keyToken);
const valueToken = tokenStore.getTokenAfter(eqToken);
if (valueToken != null && valueToken.range[1] <= node.range[1]) setOffset(valueToken, 1, keyToken);
}
},
VElement(node) {
if (PREFORMATTED_ELEMENT_NAMES.has(node.name)) {
const startTagToken = tokenStore.getFirstToken(node);
setOffset(node.endTag && tokenStore.getFirstToken(node.endTag), 0, startTagToken);
setPreformattedTokens(node);
} else {
const offset = node.parent.type !== "VElement" ? options.baseIndent : 1;
processNodeList(node.children.filter(require_indent_utils.isNotEmptyTextNode), node.startTag, node.endTag, offset, false);
}
},
VEndTag(node) {
const element = node.parent;
const startTagOpenToken = tokenStore.getFirstToken(element.startTag);
const closeToken = tokenStore.getLastToken(node);
if (closeToken.type.endsWith("TagClose")) setOffset(closeToken, options.closeBracket.endTag, startTagOpenToken);
},
VExpressionContainer(node) {
if (node.expression != null && node.range[0] !== node.expression.range[0]) {
const startQuoteToken = tokenStore.getFirstToken(node);
const endQuoteToken = tokenStore.getLastToken(node);
setOffset(tokenStore.getTokenAfter(startQuoteToken), 1, startQuoteToken);
setOffset(endQuoteToken, 0, startQuoteToken);
}
},
VFilter(node) {
const idToken = tokenStore.getFirstToken(node);
const lastToken = tokenStore.getLastToken(node);
if ((0, _eslint_community_eslint_utils.isClosingParenToken)(lastToken)) {
const leftParenToken = tokenStore.getTokenAfter(node.callee);
setOffset(leftParenToken, 1, idToken);
processNodeList(node.arguments, leftParenToken, lastToken, 1);
}
},
VFilterSequenceExpression(node) {
if (node.filters.length === 0) return;
const firstToken = tokenStore.getFirstToken(node);
const tokens = [];
for (const filter of node.filters) tokens.push(tokenStore.getTokenBefore(filter, require_indent_utils.isPipeOperator), tokenStore.getFirstToken(filter));
setOffset(tokens, 1, firstToken);
},
VForExpression(node) {
const firstToken = tokenStore.getFirstToken(node);
const lastOfLeft = require_indent_utils.last(node.left) || firstToken;
const inToken = tokenStore.getTokenAfter(lastOfLeft, _eslint_community_eslint_utils.isNotClosingParenToken);
const rightToken = tokenStore.getFirstToken(node.right);
if ((0, _eslint_community_eslint_utils.isOpeningParenToken)(firstToken)) {
const rightToken = tokenStore.getTokenAfter(lastOfLeft, _eslint_community_eslint_utils.isClosingParenToken);
processNodeList(node.left, firstToken, rightToken, 1);
}
setOffset(inToken, 1, firstToken);
setOffset(rightToken, 1, inToken);
},
VOnExpression(node) {
processNodeList(node.body, null, null, 0);
},
VStartTag(node) {
const openToken = tokenStore.getFirstToken(node);
const closeToken = tokenStore.getLastToken(node);
processNodeList(node.attributes, openToken, null, options.attribute, options.alignAttributesVertically);
if (closeToken != null && closeToken.type.endsWith("TagClose")) setOffset(closeToken, closeToken.type === "HTMLSelfClosingTagClose" ? options.closeBracket.selfClosingTag : options.closeBracket.startTag, openToken);
},
VText(node) {
const tokens = tokenStore.getTokens(node, require_indent_utils.isNotWhitespace);
const firstTokenInfo = offsets.get(tokenStore.getFirstToken(node));
for (const token of tokens) offsets.set(token, Object.assign({}, firstTokenInfo));
},
VIdentifier() {},
VLiteral() {},
VDirectiveKey() {},
VSlotScopeExpression() {},
"ArrayExpression, ArrayPattern"(node) {
const firstToken = tokenStore.getFirstToken(node);
const rightToken = tokenStore.getTokenAfter(node.elements.at(-1) || firstToken, _eslint_community_eslint_utils.isClosingBracketToken);
processNodeList(node.elements, firstToken, rightToken, 1);
},
ArrowFunctionExpression(node) {
const firstToken = tokenStore.getFirstToken(node);
const secondToken = tokenStore.getTokenAfter(firstToken);
const leftToken = node.async ? secondToken : firstToken;
const arrowToken = tokenStore.getTokenBefore(node.body, _eslint_community_eslint_utils.isArrowToken);
if (node.async) setOffset(secondToken, 1, firstToken);
if ((0, _eslint_community_eslint_utils.isOpeningParenToken)(leftToken)) {
const rightToken = tokenStore.getTokenAfter(require_indent_utils.last(node.params) || leftToken, _eslint_community_eslint_utils.isClosingParenToken);
processNodeList(node.params, leftToken, rightToken, 1);
}
setOffset(arrowToken, 1, firstToken);
processMaybeBlock(node.body, firstToken);
},
"AssignmentExpression, AssignmentPattern, BinaryExpression, LogicalExpression"(node) {
const leftToken = getChainHeadToken(node);
const opToken = tokenStore.getTokenAfter(node.left, _eslint_community_eslint_utils.isNotClosingParenToken);
const rightToken = tokenStore.getTokenAfter(opToken);
const prevToken = tokenStore.getTokenBefore(leftToken);
const shouldIndent = prevToken == null || prevToken.loc.end.line === leftToken.loc.start.line || isBeginningOfElement(leftToken, node);
setOffset([opToken, rightToken], shouldIndent ? 1 : 0, leftToken);
},
"AwaitExpression, RestElement, SpreadElement, UnaryExpression"(node) {
const firstToken = tokenStore.getFirstToken(node);
setOffset(tokenStore.getTokenAfter(firstToken), 1, firstToken);
},
"BlockStatement, ClassBody"(node) {
processNodeList(node.body, tokenStore.getFirstToken(node), tokenStore.getLastToken(node), 1);
},
StaticBlock(node) {
const firstToken = tokenStore.getFirstToken(node);
let next = tokenStore.getTokenAfter(firstToken);
while (next && (0, _eslint_community_eslint_utils.isNotOpeningBraceToken)(next)) {
setOffset(next, 0, firstToken);
next = tokenStore.getTokenAfter(next);
}
setOffset(next, 0, firstToken);
processNodeList(node.body, next, tokenStore.getLastToken(node), 1);
},
"BreakStatement, ContinueStatement, ReturnStatement, ThrowStatement"(node) {
if ((node.type === "ReturnStatement" || node.type === "ThrowStatement") && node.argument != null || (node.type === "BreakStatement" || node.type === "ContinueStatement") && node.label != null) {
const firstToken = tokenStore.getFirstToken(node);
setOffset(tokenStore.getTokenAfter(firstToken), 1, firstToken);
}
},
CallExpression(node) {
const typeArguments = "typeArguments" in node ? node.typeArguments : node.typeParameters;
const firstToken = tokenStore.getFirstToken(node);
const rightToken = tokenStore.getLastToken(node);
const leftToken = tokenStore.getTokenAfter(typeArguments || node.callee, _eslint_community_eslint_utils.isOpeningParenToken);
if (typeArguments) setOffset(tokenStore.getFirstToken(typeArguments), 1, firstToken);
for (const optionalToken of tokenStore.getTokensBetween(tokenStore.getLastToken(typeArguments || node.callee), leftToken, isOptionalToken)) setOffset(optionalToken, 1, firstToken);
setOffset(leftToken, 1, firstToken);
processNodeList(node.arguments, leftToken, rightToken, 1);
},
ImportExpression(node) {
const firstToken = tokenStore.getFirstToken(node);
const rightToken = tokenStore.getLastToken(node);
const leftToken = tokenStore.getTokenAfter(firstToken, _eslint_community_eslint_utils.isOpeningParenToken);
setOffset(leftToken, 1, firstToken);
processNodeList([node.source], leftToken, rightToken, 1);
},
CatchClause(node) {
const firstToken = tokenStore.getFirstToken(node);
const bodyToken = tokenStore.getFirstToken(node.body);
if (node.param != null) {
const leftToken = tokenStore.getTokenAfter(firstToken);
const rightToken = tokenStore.getTokenAfter(node.param);
setOffset(leftToken, 1, firstToken);
processNodeList([node.param], leftToken, rightToken, 1);
}
setOffset(bodyToken, 0, firstToken);
},
"ClassDeclaration, ClassExpression"(node) {
const firstToken = tokenStore.getFirstToken(node);
const bodyToken = tokenStore.getFirstToken(node.body);
if (node.id != null) setOffset(tokenStore.getFirstToken(node.id), 1, firstToken);
if (node.superClass != null) {
const extendsToken = tokenStore.getTokenBefore(node.superClass, require_indent_utils.isExtendsKeyword);
const superClassToken = tokenStore.getTokenAfter(extendsToken);
setOffset(extendsToken, 1, firstToken);
setOffset(superClassToken, 1, extendsToken);
}
setOffset(bodyToken, 0, firstToken);
},
ConditionalExpression(node) {
const prevToken = tokenStore.getTokenBefore(node);
const firstToken = tokenStore.getFirstToken(node);
const questionToken = tokenStore.getTokenAfter(node.test, _eslint_community_eslint_utils.isNotClosingParenToken);
const consequentToken = tokenStore.getTokenAfter(questionToken);
const colonToken = tokenStore.getTokenAfter(node.consequent, _eslint_community_eslint_utils.isNotClosingParenToken);
const alternateToken = tokenStore.getTokenAfter(colonToken);
if (prevToken && prevToken.loc.end.line !== node.loc.start.line && node.test.loc.end.line === node.consequent.loc.start.line) setOffset([
questionToken,
consequentToken,
colonToken,
alternateToken
], 0, firstToken);
else {
setOffset([questionToken, colonToken], 1, firstToken);
setOffset([consequentToken, alternateToken], 1, questionToken);
}
},
DoWhileStatement(node) {
const doToken = tokenStore.getFirstToken(node);
const whileToken = tokenStore.getTokenAfter(node.body, _eslint_community_eslint_utils.isNotClosingParenToken);
const leftToken = tokenStore.getTokenAfter(whileToken);
const testToken = tokenStore.getTokenAfter(leftToken);
const lastToken = tokenStore.getLastToken(node);
const rightToken = (0, _eslint_community_eslint_utils.isSemicolonToken)(lastToken) ? tokenStore.getTokenBefore(lastToken) : lastToken;
processMaybeBlock(node.body, doToken);
setOffset(whileToken, 0, doToken);
setOffset(leftToken, 1, whileToken);
setOffset(testToken, 1, leftToken);
setOffset(rightToken, 0, leftToken);
},
ExportAllDeclaration(node) {
const exportToken = tokenStore.getFirstToken(node);
const tokens = [...tokenStore.getTokensBetween(exportToken, node.source), tokenStore.getFirstToken(node.source)];
if (node.exported) {
const starToken = tokens.find(require_indent_utils.isWildcard);
const asToken = tokenStore.getTokenAfter(starToken);
const exportedToken = tokenStore.getTokenAfter(asToken);
const afterTokens = tokens.slice(tokens.indexOf(exportedToken) + 1);
setOffset(starToken, 1, exportToken);
setOffset(asToken, 1, starToken);
setOffset(exportedToken, 1, starToken);
setOffset(afterTokens, 1, exportToken);
} else setOffset(tokens, 1, exportToken);
const lastToken = tokenStore.getLastToken(node, _eslint_community_eslint_utils.isNotSemicolonToken);
const assertionTokens = tokenStore.getTokensBetween(node.source, lastToken);
if (assertionTokens.length > 0) {
const assertToken = assertionTokens.shift();
setOffset(assertToken, 0, exportToken);
const assertionOpen = assertionTokens.shift();
if (assertionOpen) {
setOffset(assertionOpen, 1, assertToken);
processNodeList(assertionTokens, assertionOpen, lastToken, 1);
}
}
},
ExportDefaultDeclaration(node) {
const exportToken = tokenStore.getFirstToken(node);
const defaultToken = tokenStore.getFirstToken(node, 1);
const declarationToken = getFirstAndLastTokens(node.declaration).firstToken;
setOffset([defaultToken, declarationToken], 1, exportToken);
},
ExportNamedDeclaration(node) {
const exportToken = tokenStore.getFirstToken(node);
if (node.declaration) setOffset(tokenStore.getFirstToken(node, 1), 1, exportToken);
else {
const firstSpecifier = node.specifiers[0];
if (!firstSpecifier || firstSpecifier.type === "ExportSpecifier") {
const leftBraceTokens = firstSpecifier ? tokenStore.getTokensBetween(exportToken, firstSpecifier) : [tokenStore.getTokenAfter(exportToken)];
const rightBraceToken = node.source ? tokenStore.getTokenBefore(node.source, _eslint_community_eslint_utils.isClosingBraceToken) : tokenStore.getLastToken(node, _eslint_community_eslint_utils.isClosingBraceToken);
setOffset(leftBraceTokens, 0, exportToken);
processNodeList(node.specifiers, require_indent_utils.last(leftBraceTokens), rightBraceToken, 1);
if (node.source) {
setOffset([...tokenStore.getTokensBetween(rightBraceToken, node.source), sourceCode.getFirstToken(node.source)], 1, exportToken);
const lastToken = tokenStore.getLastToken(node, _eslint_community_eslint_utils.isNotSemicolonToken);
const assertionTokens = tokenStore.getTokensBetween(node.source, lastToken);
if (assertionTokens.length > 0) {
const assertToken = assertionTokens.shift();
setOffset(assertToken, 0, exportToken);
const assertionOpen = assertionTokens.shift();
if (assertionOpen) {
setOffset(assertionOpen, 1, assertToken);
processNodeList(assertionTokens, assertionOpen, lastToken, 1);
}
}
}
}
}
},
"ExportSpecifier, ImportSpecifier"(node) {
const tokens = tokenStore.getTokens(node);
let firstToken = tokens.shift();
if (firstToken.value === "type") {
const typeToken = firstToken;
firstToken = tokens.shift();
setOffset(firstToken, 0, typeToken);
}
setOffset(tokens, 1, firstToken);
},
"ForInStatement, ForOfStatement"(node) {
const forToken = tokenStore.getFirstToken(node);
const awaitToken = node.type === "ForOfStatement" && node.await && tokenStore.getTokenAfter(forToken) || null;
const leftParenToken = tokenStore.getTokenAfter(awaitToken || forToken);
const leftToken = tokenStore.getTokenAfter(leftParenToken);
const inToken = tokenStore.getTokenAfter(leftToken, _eslint_community_eslint_utils.isNotClosingParenToken);
const rightToken = tokenStore.getTokenAfter(inToken);
const rightParenToken = tokenStore.getTokenBefore(node.body, _eslint_community_eslint_utils.isNotOpeningParenToken);
if (awaitToken != null) setOffset(awaitToken, 0, forToken);
setOffset(leftParenToken, 1, forToken);
setOffset(leftToken, 1, leftParenToken);
setOffset(inToken, 1, leftToken);
setOffset(rightToken, 1, leftToken);
setOffset(rightParenToken, 0, leftParenToken);
processMaybeBlock(node.body, forToken);
},
ForStatement(node) {
const forToken = tokenStore.getFirstToken(node);
const leftParenToken = tokenStore.getTokenAfter(forToken);
const rightParenToken = tokenStore.getTokenBefore(node.body, _eslint_community_eslint_utils.isNotOpeningParenToken);
setOffset(leftParenToken, 1, forToken);
processNodeList([
node.init,
node.test,
node.update
], leftParenToken, rightParenToken, 1);
processMaybeBlock(node.body, forToken);
},
"FunctionDeclaration, FunctionExpression"(node) {
const firstToken = tokenStore.getFirstToken(node);
let leftParenToken, bodyBaseToken;
if ((0, _eslint_community_eslint_utils.isOpeningParenToken)(firstToken)) {
leftParenToken = firstToken;
bodyBaseToken = tokenStore.getFirstToken(node.parent);
} else {
let nextToken = tokenStore.getTokenAfter(firstToken);
let nextTokenOffset = 0;
while (nextToken && !(0, _eslint_community_eslint_utils.isOpeningParenToken)(nextToken) && nextToken.value !== "<") {
if (nextToken.value === "*" || node.id && nextToken.range[0] === node.id.range[0]) nextTokenOffset = 1;
setOffset(nextToken, nextTokenOffset, firstToken);
nextToken = tokenStore.getTokenAfter(nextToken);
}
leftParenToken = nextToken;
bodyBaseToken = firstToken;
}
if (!(0, _eslint_community_eslint_utils.isOpeningParenToken)(leftParenToken) && node.typeParameters) leftParenToken = tokenStore.getTokenAfter(node.typeParameters);
const rightParenToken = tokenStore.getTokenAfter(node.params.at(-1) || leftParenToken, _eslint_community_eslint_utils.isClosingParenToken);
setOffset(leftParenToken, 1, bodyBaseToken);
processNodeList(node.params, leftParenToken, rightParenToken, 1);
setOffset(tokenStore.getFirstToken(node.body), 0, bodyBaseToken);
},
IfStatement(node) {
const ifToken = tokenStore.getFirstToken(node);
const ifLeftParenToken = tokenStore.getTokenAfter(ifToken);
const ifRightParenToken = tokenStore.getTokenBefore(node.consequent, _eslint_community_eslint_utils.isClosingParenToken);
setOffset(ifLeftParenToken, 1, ifToken);
setOffset(ifRightParenToken, 0, ifLeftParenToken);
processMaybeBlock(node.consequent, ifToken);
if (node.alternate != null) {
const elseToken = tokenStore.getTokenAfter(node.consequent, _eslint_community_eslint_utils.isNotClosingParenToken);
setOffset(elseToken, 0, ifToken);
processMaybeBlock(node.alternate, elseToken);
}
},
ImportDeclaration(node) {
const importToken = tokenStore.getFirstToken(node);
const tokens = tokenStore.getTokensBetween(importToken, node.source);
const fromIndex = tokens.map((t) => t.value).lastIndexOf("from");
const { fromToken, beforeTokens, afterTokens } = fromIndex === -1 ? {
fromToken: null,
beforeTokens: [...tokens, tokenStore.getFirstToken(node.source)],
afterTokens: []
} : {
fromToken: tokens[fromIndex],
beforeTokens: tokens.slice(0, fromIndex),
afterTokens: [...tokens.slice(fromIndex + 1), tokenStore.getFirstToken(node.source)]
};
const namedSpecifiers = [];
for (const specifier of node.specifiers) if (specifier.type === "ImportSpecifier") namedSpecifiers.push(specifier);
else {
const removeTokens = tokenStore.getTokens(specifier);
removeTokens.shift();
for (const token of removeTokens) {
const i = beforeTokens.indexOf(token);
if (i !== -1) beforeTokens.splice(i, 1);
}
}
const lastNamedSpecifier = namedSpecifiers.at(-1);
if (lastNamedSpecifier) {
const leftBrace = tokenStore.getTokenBefore(namedSpecifiers[0]);
const rightBrace = tokenStore.getTokenAfter(lastNamedSpecifier, _eslint_community_eslint_utils.isClosingBraceToken);
processNodeList(namedSpecifiers, leftBrace, rightBrace, 1);
for (const token of [...tokenStore.getTokensBetween(leftBrace, rightBrace), rightBrace]) {
const i = beforeTokens.indexOf(token);
if (i !== -1) beforeTokens.splice(i, 1);
}
}
if (beforeTokens.every((t) => (0, _eslint_community_eslint_utils.isOpeningBraceToken)(t) || (0, _eslint_community_eslint_utils.isClosingBraceToken)(t))) setOffset(beforeTokens, 0, importToken);
else setOffset(beforeTokens, 1, importToken);
if (fromToken) {
setOffset(fromToken, 1, importToken);
setOffset(afterTokens, 0, fromToken);
}
const lastToken = tokenStore.getLastToken(node, _eslint_community_eslint_utils.isNotSemicolonToken);
const assertionTokens = tokenStore.getTokensBetween(node.source, lastToken);
if (assertionTokens.length > 0) {
const assertToken = assertionTokens.shift();
setOffset(assertToken, 0, importToken);
const assertionOpen = assertionTokens.shift();
if (assertionOpen) {
setOffset(assertionOpen, 1, assertToken);
processNodeList(assertionTokens, assertionOpen, lastToken, 1);
}
}
},
ImportNamespaceSpecifier(node) {
const tokens = tokenStore.getTokens(node);
setOffset(tokens, 1, tokens.shift());
},
LabeledStatement(node) {
const labelToken = tokenStore.getFirstToken(node);
const colonToken = tokenStore.getTokenAfter(labelToken);
setOffset([colonToken, tokenStore.getTokenAfter(colonToken)], 1, labelToken);
},
"MemberExpression, MetaProperty"(node) {
const objectToken = tokenStore.getFirstToken(node);
if (node.type === "MemberExpression" && node.computed) {
const leftBracketToken = tokenStore.getTokenBefore(node.property, _eslint_community_eslint_utils.isOpeningBracketToken);
const propertyToken = tokenStore.getTokenAfter(leftBracketToken);
const rightBracketToken = tokenStore.getTokenAfter(node.property, _eslint_community_eslint_utils.isClosingBracketToken);
for (const optionalToken of tokenStore.getTokensBetween(tokenStore.getLastToken(node.object), leftBracketToken, isOptionalToken)) setOffset(optionalToken, 1, objectToken);
setOffset(leftBracketToken, 1, objectToken);
setOffset(propertyToken, 1, leftBracketToken);
setOffset(rightBracketToken, 0, leftBracketToken);
} else {
const dotToken = tokenStore.getTokenBefore(node.property);
setOffset([dotToken, tokenStore.getTokenAfter(dotToken)], 1, objectToken);
}
},
"MethodDefinition, Property, PropertyDefinition"(node) {
const firstToken = tokenStore.getFirstToken(node);
const keyTokens = getFirstAndLastTokens(node.key);
const prefixTokens = tokenStore.getTokensBetween(firstToken, keyTokens.firstToken);
if (node.computed) prefixTokens.pop();
setOffset(prefixTokens, 0, firstToken);
let lastKeyToken;
if (node.computed) {
const leftBracketToken = tokenStore.getTokenBefore(keyTokens.firstToken);
const rightBracketToken = lastKeyToken = tokenStore.getTokenAfter(keyTokens.lastToken);
setOffset(leftBracketToken, 0, firstToken);
processNodeList([node.key], leftBracketToken, rightBracketToken, 1);
} else {
setOffset(keyTokens.firstToken, 0, firstToken);
lastKeyToken = keyTokens.lastToken;
}
if (node.value != null) {
const initToken = tokenStore.getFirstToken(node.value);
setOffset([...tokenStore.getTokensBetween(lastKeyToken, initToken), initToken], 1, lastKeyToken);
}
},
NewExpression(node) {
const typeArguments = "typeArguments" in node ? node.typeArguments : node.typeParameters;
const newToken = tokenStore.getFirstToken(node);
const calleeToken = tokenStore.getTokenAfter(newToken);
const rightToken = tokenStore.getLastToken(node);
const leftToken = (0, _eslint_community_eslint_utils.isClosingParenToken)(rightToken) ? tokenStore.getFirstTokenBetween(typeArguments || node.callee, rightToken, _eslint_community_eslint_utils.isOpeningParenToken) : null;
if (typeArguments) setOffset(tokenStore.getFirstToken(typeArguments), 1, calleeToken);
setOffset(calleeToken, 1, newToken);
if (leftToken != null) {
setOffset(leftToken, 1, calleeToken);
processNodeList(node.arguments, leftToken, rightToken, 1);
}
},
"ObjectExpression, ObjectPattern"(node) {
const firstToken = tokenStore.getFirstToken(node);
const rightToken = tokenStore.getTokenAfter(node.properties.at(-1) || firstToken, _eslint_community_eslint_utils.isClosingBraceToken);
processNodeList(node.properties, firstToken, rightToken, 1);
},
SequenceExpression(node) {
processNodeList(node.expressions, null, null, 0);
},
SwitchCase(node) {
const caseToken = tokenStore.getFirstToken(node);
if (node.test == null) setOffset(tokenStore.getTokenAfter(caseToken), 1, caseToken);
else setOffset([tokenStore.getTokenAfter(caseToken), tokenStore.getTokenAfter(node.test, _eslint_community_eslint_utils.isNotClosingParenToken)], 1, caseToken);
if (node.consequent.length === 1 && node.consequent[0].type === "BlockStatement") setOffset(tokenStore.getFirstToken(node.consequent[0]), 0, caseToken);
else if (node.consequent.length > 0) {
setOffset(tokenStore.getFirstToken(node.consequent[0]), 1, caseToken);
processNodeList(node.consequent, null, null, 0);
}
},
SwitchStatement(node) {
const switchToken = tokenStore.getFirstToken(node);
const leftParenToken = tokenStore.getTokenAfter(switchToken);
const discriminantToken = tokenStore.getTokenAfter(leftParenToken);
const leftBraceToken = tokenStore.getTokenAfter(node.discriminant, _eslint_community_eslint_utils.isOpeningBraceToken);
const rightParenToken = tokenStore.getTokenBefore(leftBraceToken);
const rightBraceToken = tokenStore.getLastToken(node);
setOffset(leftParenToken, 1, switchToken);
setOffset(discriminantToken, 1, leftParenToken);
setOffset(rightParenToken, 0, leftParenToken);
setOffset(leftBraceToken, 0, switchToken);
processNodeList(node.cases, leftBraceToken, rightBraceToken, options.switchCase);
},
TaggedTemplateExpression(node) {
const tagTokens = getFirstAndLastTokens(node.tag, node.range[0]);
setOffset(tokenStore.getTokenAfter(tagTokens.lastToken), 1, tagTokens.firstToken);
},
TemplateLiteral(node) {
const firstToken = tokenStore.getFirstToken(node);
const quasiTokens = node.quasis.slice(1).map((n) => tokenStore.getFirstToken(n));
const expressionToken = node.quasis.slice(0, -1).map((n) => tokenStore.getTokenAfter(n));
setOffset(quasiTokens, 0, firstToken);
setOffset(expressionToken, 1, firstToken);
},
TryStatement(node) {
const tryToken = tokenStore.getFirstToken(node);
setOffset(tokenStore.getFirstToken(node.block), 0, tryToken);
if (node.handler != null) setOffset(tokenStore.getFirstToken(node.handler), 0, tryToken);
if (node.finalizer != null) setOffset([tokenStore.getTokenBefore(node.finalizer), tokenStore.getFirstToken(node.finalizer)], 0, tryToken);
},
UpdateExpression(node) {
const firstToken = tokenStore.getFirstToken(node);
setOffset(tokenStore.getTokenAfter(firstToken), 1, firstToken);
},
VariableDeclaration(node) {
processNodeList(node.declarations, tokenStore.getFirstToken(node), null, 1);
},
VariableDeclarator(node) {
if (node.init != null) {
const idToken = tokenStore.getFirstToken(node);
const eqToken = tokenStore.getTokenAfter(node.id);
setOffset([eqToken, tokenStore.getTokenAfter(eqToken)], 1, idToken);
}
},
"WhileStatement, WithStatement"(node) {
const firstToken = tokenStore.getFirstToken(node);
const leftParenToken = tokenStore.getTokenAfter(firstToken);
const rightParenToken = tokenStore.getTokenBefore(node.body, _eslint_community_eslint_utils.isClosingParenToken);
setOffset(leftParenToken, 1, firstToken);
setOffset(rightParenToken, 0, leftParenToken);
processMaybeBlock(node.body, firstToken);
},
YieldExpression(node) {
if (node.argument != null) {
const yieldToken = tokenStore.getFirstToken(node);
setOffset(tokenStore.getTokenAfter(yieldToken), 1, yieldToken);
if (node.delegate) setOffset(tokenStore.getTokenAfter(yieldToken, 1), 1, yieldToken);
}
},
DebuggerStatement() {},
Identifier() {},
ImportDefaultSpecifier() {},
Literal() {},
PrivateIdentifier() {},
Super() {},
TemplateElement() {},
ThisExpression() {},
ExpressionStatement() {},
ChainExpression() {},
EmptyStatement() {},
":statement, PropertyDefinition"(node) {
processSemicolons(node);
},
":expression"(node) {
let leftToken = tokenStore.getTokenBefore(node);
let rightToken = tokenStore.getTokenAfter(node);
let firstToken = tokenStore.getFirstToken(node);
while (leftToken && rightToken && (0, _eslint_community_eslint_utils.isOpeningParenToken)(leftToken) && (0, _eslint_community_eslint_utils.isClosingParenToken)(rightToken)) {
setOffset(firstToken, 1, leftToken);
setOffset(rightToken, 0, leftToken);
firstToken = leftToken;
leftToken = tokenStore.getTokenBefore(leftToken);
rightToken = tokenStore.getTokenAfter(rightToken);
}
},
...require_indent_ts.defineVisitor({
processNodeList,
tokenStore,
setOffset,
copyOffset,
processSemicolons,
getFirstAndLastTokens
}),
"*:exit"(node) {
if (!knownNodes.has(node.type)) ignore(node);
},
Program(node) {
const firstToken = node.tokens[0];
const baseIndent = firstToken != null && firstToken.type === "Punctuator" && firstToken.value === "<script>" ? options.indentSize * options.baseIndent : 0;
for (const statement of node.body) processTopLevelNode(statement, baseIndent);
},
"VElement[parent.type!='VElement']"(node) {
processTopLevelNode(node, 0);
},
":matches(Program, VElement[parent.type!='VElement']):exit"(node) {
let comments = [];
let tokensOnSameLine = [];
let isBesideMultilineToken = false;
let lastValidatedToken = null;
for (const token of tokenStore.getTokens(node, ITERATION_OPTS)) {
const tokenStartLine = token.loc.start.line;
if (tokensOnSameLine.length === 0 || tokensOnSameLine[0].loc.start.line === tokenStartLine) tokensOnSameLine.push(token);
else if (tokensOnSameLine.every(require_indent_utils.isComment)) {
comments.push(tokensOnSameLine[0]);
isBesideMultilineToken = require_indent_utils.last(tokensOnSameLine).loc.end.line === tokenStartLine;
tokensOnSameLine = [token];
} else {
if (!isBesideMultilineToken) {
validate(tokensOnSameLine, comments, lastValidatedToken);
lastValidatedToken = tokensOnSameLine[0];
}
isBesideMultilineToken = require_indent_utils.last(tokensOnSameLine).loc.end.line === tokenStartLine;
tokensOnSameLine = [token];
comments = [];
}
}
if (tokensOnSameLine.some(require_indent_utils.isNotComment)) validate(tokensOnSameLine, comments, lastValidatedToken);
}
};
for (const key of Object.keys(visitor)) for (const nodeName of key.split(/\s*,\s*/gu).map((s) => s.trim()).filter((s) => /[a-z]+/i.test(s))) knownNodes.add(nodeName);
return processIgnores(visitor);
}
//#endregion
exports.defineVisitor = defineVisitor;