routie dev init since i didn't adhere to any proper guidance up until now
This commit is contained in:
+355
@@ -0,0 +1,355 @@
|
||||
'use strict';
|
||||
|
||||
const require_runtime = require('../_virtual/_rolldown/runtime.js');
|
||||
const require_index = require('../utils/index.js');
|
||||
|
||||
//#region lib/rules/v-on-handler-style.js
|
||||
/**
|
||||
* @author Yosuke Ota <https://github.com/ota-meshi>
|
||||
* See LICENSE file in root directory for full license.
|
||||
*/
|
||||
var require_v_on_handler_style = /* @__PURE__ */ require_runtime.__commonJSMin(((exports, module) => {
|
||||
const utils = require_index.default;
|
||||
/**
|
||||
* @typedef {import('eslint').ReportDescriptorFix} ReportDescriptorFix
|
||||
* @typedef {'method' | 'inline' | 'inline-function'} HandlerKind
|
||||
* @typedef {object} ObjectOption
|
||||
* @property {boolean} [ignoreIncludesComment]
|
||||
*/
|
||||
/**
|
||||
* @param {RuleContext} context
|
||||
*/
|
||||
function parseOptions(context) {
|
||||
/** @type {[HandlerKind | HandlerKind[] | undefined, ObjectOption | undefined]} */
|
||||
const options = context.options;
|
||||
/** @type {HandlerKind[]} */
|
||||
const allows = [];
|
||||
if (options[0]) if (Array.isArray(options[0])) allows.push(...options[0]);
|
||||
else allows.push(options[0]);
|
||||
else allows.push("method", "inline-function");
|
||||
return {
|
||||
allows,
|
||||
ignoreIncludesComment: !!(options[1] || {}).ignoreIncludesComment
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Check whether the given token is a quote.
|
||||
* @param {Token} token The token to check.
|
||||
* @returns {boolean} `true` if the token is a quote.
|
||||
*/
|
||||
function isQuote(token) {
|
||||
return token != null && token.type === "Punctuator" && (token.value === "\"" || token.value === "'");
|
||||
}
|
||||
/**
|
||||
* Check whether the given node is an identifier call expression. e.g. `foo()`
|
||||
* @param {Expression} node The node to check.
|
||||
* @returns {node is CallExpression & {callee: Identifier}}
|
||||
*/
|
||||
function isIdentifierCallExpression(node) {
|
||||
if (node.type !== "CallExpression") return false;
|
||||
if (node.optional) return false;
|
||||
return node.callee.type === "Identifier";
|
||||
}
|
||||
/**
|
||||
* Returns a call expression node if the given VOnExpression or BlockStatement consists
|
||||
* of only a single identifier call expression.
|
||||
* e.g.
|
||||
* @click="foo()"
|
||||
* @click="{ foo() }"
|
||||
* @click="foo();;"
|
||||
* @param {VOnExpression | BlockStatement} node
|
||||
* @returns {CallExpression & {callee: Identifier} | null}
|
||||
*/
|
||||
function getIdentifierCallExpression(node) {
|
||||
/** @type {ExpressionStatement} */
|
||||
let exprStatement;
|
||||
let body = node.body;
|
||||
while (true) {
|
||||
const statements = body.filter((st) => st.type !== "EmptyStatement");
|
||||
if (statements.length !== 1) return null;
|
||||
const statement = statements[0];
|
||||
if (statement.type === "ExpressionStatement") {
|
||||
exprStatement = statement;
|
||||
break;
|
||||
}
|
||||
if (statement.type === "BlockStatement") {
|
||||
body = statement.body;
|
||||
continue;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
const expression = exprStatement.expression;
|
||||
if (!isIdentifierCallExpression(expression)) return null;
|
||||
return expression;
|
||||
}
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
docs: {
|
||||
description: "enforce writing style for handlers in `v-on` directives",
|
||||
categories: void 0,
|
||||
url: "https://eslint.vuejs.org/rules/v-on-handler-style.html"
|
||||
},
|
||||
fixable: "code",
|
||||
schema: [{ oneOf: [{ enum: ["inline", "inline-function"] }, {
|
||||
type: "array",
|
||||
items: [{ const: "method" }, { enum: ["inline", "inline-function"] }],
|
||||
uniqueItems: true,
|
||||
additionalItems: false,
|
||||
minItems: 2,
|
||||
maxItems: 2
|
||||
}] }, {
|
||||
type: "object",
|
||||
properties: { ignoreIncludesComment: { type: "boolean" } },
|
||||
additionalProperties: false
|
||||
}],
|
||||
messages: {
|
||||
preferMethodOverInline: "Prefer method handler over inline handler in v-on.",
|
||||
preferMethodOverInlineWithoutIdCall: "Prefer method handler over inline handler in v-on. Note that you may need to create a new method.",
|
||||
preferMethodOverInlineFunction: "Prefer method handler over inline function in v-on.",
|
||||
preferMethodOverInlineFunctionWithoutIdCall: "Prefer method handler over inline function in v-on. Note that you may need to create a new method.",
|
||||
preferInlineOverMethod: "Prefer inline handler over method handler in v-on.",
|
||||
preferInlineOverInlineFunction: "Prefer inline handler over inline function in v-on.",
|
||||
preferInlineOverInlineFunctionWithMultipleParams: "Prefer inline handler over inline function in v-on. Note that the custom event must be changed to a single payload.",
|
||||
preferInlineFunctionOverMethod: "Prefer inline function over method handler in v-on.",
|
||||
preferInlineFunctionOverInline: "Prefer inline function over inline handler in v-on."
|
||||
}
|
||||
},
|
||||
create(context) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const { allows, ignoreIncludesComment } = parseOptions(context);
|
||||
/** @type {Set<VElement>} */
|
||||
const upperElements = /* @__PURE__ */ new Set();
|
||||
/** @type {Map<string, number>} */
|
||||
const methodParamCountMap = /* @__PURE__ */ new Map();
|
||||
/** @type {Identifier[]} */
|
||||
const $eventIdentifiers = [];
|
||||
/**
|
||||
* Verify for inline handler.
|
||||
* @param {VOnExpression} node
|
||||
* @param {HandlerKind} kind
|
||||
* @returns {boolean} Returns `true` if reported.
|
||||
*/
|
||||
function verifyForInlineHandler(node, kind) {
|
||||
switch (kind) {
|
||||
case "method": return verifyCanUseMethodHandlerForInlineHandler(node);
|
||||
case "inline-function":
|
||||
reportCanUseInlineFunctionForInlineHandler(node);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Report for method handler.
|
||||
* @param {Identifier} node
|
||||
* @param {HandlerKind} kind
|
||||
* @returns {boolean} Returns `true` if reported.
|
||||
*/
|
||||
function reportForMethodHandler(node, kind) {
|
||||
switch (kind) {
|
||||
case "inline":
|
||||
case "inline-function":
|
||||
context.report({
|
||||
node,
|
||||
messageId: kind === "inline" ? "preferInlineOverMethod" : "preferInlineFunctionOverMethod"
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Verify for inline function handler.
|
||||
* @param {ArrowFunctionExpression | FunctionExpression} node
|
||||
* @param {HandlerKind} kind
|
||||
* @returns {boolean} Returns `true` if reported.
|
||||
*/
|
||||
function verifyForInlineFunction(node, kind) {
|
||||
switch (kind) {
|
||||
case "method": return verifyCanUseMethodHandlerForInlineFunction(node);
|
||||
case "inline":
|
||||
reportCanUseInlineHandlerForInlineFunction(node);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Get token information for the given VExpressionContainer node.
|
||||
* @param {VExpressionContainer} node
|
||||
*/
|
||||
function getVExpressionContainerTokenInfo(node) {
|
||||
const tokens = sourceCode.parserServices.getTemplateBodyTokenStore().getTokens(node, { includeComments: true });
|
||||
const firstToken = tokens[0];
|
||||
const lastToken = tokens.at(-1);
|
||||
if (!lastToken) return {
|
||||
rangeWithoutQuotes: [0, 0],
|
||||
hasComment: false,
|
||||
hasQuote: false
|
||||
};
|
||||
const hasQuote = isQuote(firstToken);
|
||||
return {
|
||||
rangeWithoutQuotes: hasQuote ? [firstToken.range[1], lastToken.range[0]] : [firstToken.range[0], lastToken.range[1]],
|
||||
get hasComment() {
|
||||
return tokens.some((token) => token.type === "Block" || token.type === "Line");
|
||||
},
|
||||
hasQuote
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Checks whether the given node refers to a variable of the element.
|
||||
* @param {Expression | VOnExpression} node
|
||||
*/
|
||||
function hasReferenceUpperElementVariable(node) {
|
||||
for (const element of upperElements) for (const vv of element.variables) for (const reference of vv.references) {
|
||||
const { range } = reference.id;
|
||||
if (node.range[0] <= range[0] && range[1] <= node.range[1]) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Check if `v-on:click="foo()"` can be converted to `v-on:click="foo"` and report if it can.
|
||||
* @param {VOnExpression} node
|
||||
* @returns {boolean} Returns `true` if reported.
|
||||
*/
|
||||
function verifyCanUseMethodHandlerForInlineHandler(node) {
|
||||
const { rangeWithoutQuotes, hasComment } = getVExpressionContainerTokenInfo(node.parent);
|
||||
if (ignoreIncludesComment && hasComment) return false;
|
||||
const idCallExpr = getIdentifierCallExpression(node);
|
||||
if ((!idCallExpr || idCallExpr.arguments.length > 0) && hasReferenceUpperElementVariable(node)) return false;
|
||||
context.report({
|
||||
node,
|
||||
messageId: idCallExpr ? "preferMethodOverInline" : "preferMethodOverInlineWithoutIdCall",
|
||||
fix: (fixer) => {
|
||||
if (hasComment || !idCallExpr || idCallExpr.arguments.length > 0) return null;
|
||||
const paramCount = methodParamCountMap.get(idCallExpr.callee.name);
|
||||
if (paramCount != null && paramCount > 0) return null;
|
||||
return fixer.replaceTextRange(rangeWithoutQuotes, sourceCode.getText(idCallExpr.callee));
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Check if `v-on:click="() => foo()"` can be converted to `v-on:click="foo"` and report if it can.
|
||||
* @param {ArrowFunctionExpression | FunctionExpression} node
|
||||
* @returns {boolean} Returns `true` if reported.
|
||||
*/
|
||||
function verifyCanUseMethodHandlerForInlineFunction(node) {
|
||||
const { rangeWithoutQuotes, hasComment } = getVExpressionContainerTokenInfo(node.parent);
|
||||
if (ignoreIncludesComment && hasComment) return false;
|
||||
/** @type {CallExpression & {callee: Identifier} | null} */
|
||||
let idCallExpr = null;
|
||||
if (node.body.type === "BlockStatement") idCallExpr = getIdentifierCallExpression(node.body);
|
||||
else if (isIdentifierCallExpression(node.body)) idCallExpr = node.body;
|
||||
if ((!idCallExpr || !isSameParamsAndArgs(idCallExpr)) && hasReferenceUpperElementVariable(node)) return false;
|
||||
context.report({
|
||||
node,
|
||||
messageId: idCallExpr ? "preferMethodOverInlineFunction" : "preferMethodOverInlineFunctionWithoutIdCall",
|
||||
fix: (fixer) => {
|
||||
if (hasComment || !idCallExpr) return null;
|
||||
if (!isSameParamsAndArgs(idCallExpr)) return null;
|
||||
const paramCount = methodParamCountMap.get(idCallExpr.callee.name);
|
||||
if (paramCount != null && paramCount !== idCallExpr.arguments.length) return null;
|
||||
return fixer.replaceTextRange(rangeWithoutQuotes, sourceCode.getText(idCallExpr.callee));
|
||||
}
|
||||
});
|
||||
return true;
|
||||
/**
|
||||
* Checks whether parameters are passed as arguments as-is.
|
||||
* @param {CallExpression} expression
|
||||
*/
|
||||
function isSameParamsAndArgs(expression) {
|
||||
return node.params.length === expression.arguments.length && node.params.every((param, index) => {
|
||||
if (param.type !== "Identifier") return false;
|
||||
const arg = expression.arguments[index];
|
||||
if (!arg || arg.type !== "Identifier") return false;
|
||||
return param.name === arg.name;
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Report `v-on:click="foo()"` can be converted to `v-on:click="()=>foo()"`.
|
||||
* @param {VOnExpression} node
|
||||
* @returns {void}
|
||||
*/
|
||||
function reportCanUseInlineFunctionForInlineHandler(node) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "preferInlineFunctionOverInline",
|
||||
*fix(fixer) {
|
||||
if ($eventIdentifiers.some(({ range }) => node.range[0] <= range[0] && range[1] <= node.range[1])) return;
|
||||
const { rangeWithoutQuotes, hasQuote } = getVExpressionContainerTokenInfo(node.parent);
|
||||
if (!hasQuote) return;
|
||||
yield fixer.insertTextBeforeRange(rangeWithoutQuotes, "() => ");
|
||||
const tokenStore = sourceCode.parserServices.getTemplateBodyTokenStore();
|
||||
const firstToken = tokenStore.getFirstToken(node);
|
||||
const lastToken = tokenStore.getLastToken(node);
|
||||
if (firstToken.value === "{" && lastToken.value === "}") return;
|
||||
if (lastToken.value !== ";" && node.body.length === 1 && node.body[0].type === "ExpressionStatement") return;
|
||||
yield fixer.insertTextBefore(firstToken, "{");
|
||||
yield fixer.insertTextAfter(lastToken, "}");
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Report `v-on:click="() => foo()"` can be converted to `v-on:click="foo()"`.
|
||||
* @param {ArrowFunctionExpression | FunctionExpression} node
|
||||
* @returns {void}
|
||||
*/
|
||||
function reportCanUseInlineHandlerForInlineFunction(node) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: node.params.length > 1 ? "preferInlineOverInlineFunctionWithMultipleParams" : "preferInlineOverInlineFunction",
|
||||
fix: node.params.length > 0 ? null : (fixer) => {
|
||||
let text = sourceCode.getText(node.body);
|
||||
if (node.body.type === "BlockStatement") text = text.slice(1, -1);
|
||||
return fixer.replaceText(node, text);
|
||||
}
|
||||
});
|
||||
}
|
||||
return utils.defineTemplateBodyVisitor(context, {
|
||||
VElement(node) {
|
||||
upperElements.add(node);
|
||||
},
|
||||
"VElement:exit"(node) {
|
||||
upperElements.delete(node);
|
||||
},
|
||||
"VAttribute[directive=true][key.name.name='on'][key.argument!=null] > VExpressionContainer.value:exit"(node) {
|
||||
const expression = node.expression;
|
||||
if (!expression) return;
|
||||
switch (expression.type) {
|
||||
case "VOnExpression":
|
||||
if (allows[0] === "inline") return;
|
||||
for (const allow of allows) if (verifyForInlineHandler(expression, allow)) return;
|
||||
break;
|
||||
case "Identifier":
|
||||
if (allows[0] === "method") return;
|
||||
for (const allow of allows) if (reportForMethodHandler(expression, allow)) return;
|
||||
break;
|
||||
case "ArrowFunctionExpression":
|
||||
case "FunctionExpression":
|
||||
if (allows[0] === "inline-function") return;
|
||||
for (const allow of allows) if (verifyForInlineFunction(expression, allow)) return;
|
||||
break;
|
||||
default: return;
|
||||
}
|
||||
},
|
||||
...allows.includes("inline-function") ? { "Identifier[name=\"$event\"]"(node) {
|
||||
$eventIdentifiers.push(node);
|
||||
} } : {}
|
||||
}, allows.includes("method") ? utils.defineVueVisitor(context, { onVueObjectEnter(node) {
|
||||
for (const method of utils.iterateProperties(node, new Set(["methods"]))) {
|
||||
if (method.type !== "object") continue;
|
||||
const value = method.property.value;
|
||||
if (value.type === "FunctionExpression" || value.type === "ArrowFunctionExpression") methodParamCountMap.set(method.name, value.params.some((p) => p.type === "RestElement") ? Number.POSITIVE_INFINITY : value.params.length);
|
||||
}
|
||||
} }) : {});
|
||||
}
|
||||
};
|
||||
}));
|
||||
|
||||
//#endregion
|
||||
Object.defineProperty(exports, 'default', {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return require_v_on_handler_style();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user