routie dev init since i didn't adhere to any proper guidance up until now
This commit is contained in:
+113
@@ -0,0 +1,113 @@
|
||||
const require_runtime = require('../_virtual/_rolldown/runtime.js');
|
||||
const require_index = require('../utils/index.js');
|
||||
const require_casing = require('../utils/casing.js');
|
||||
|
||||
//#region lib/rules/prefer-v-model.ts
|
||||
/**
|
||||
* @author Flo Edelmann
|
||||
* See LICENSE file in root directory for full license.
|
||||
*/
|
||||
var import_utils = /* @__PURE__ */ require_runtime.__toESM(require_index.default);
|
||||
/**
|
||||
* Get the static argument name of a directive, or `null` for dynamic arguments.
|
||||
*/
|
||||
const getStaticArgName = (directive) => directive.key.argument?.type === "VIdentifier" ? directive.key.argument.rawName : null;
|
||||
/**
|
||||
* Extract the prop name from an `update:propName` event directive argument.
|
||||
*/
|
||||
function getUpdateEventPropName(onDirective) {
|
||||
const argName = getStaticArgName(onDirective);
|
||||
if (!argName?.startsWith("update:")) return null;
|
||||
return argName.slice(7);
|
||||
}
|
||||
/**
|
||||
* Check if the event handler is a simple mirror assignment of the bind expression.
|
||||
* Matches: `bar = $event` or `(param) => bar = param`
|
||||
*/
|
||||
function isMirrorAssignment(bindExpr, onExpr, sourceCode) {
|
||||
const bindText = sourceCode.getText(bindExpr);
|
||||
if (onExpr.type === "VOnExpression") {
|
||||
const statements = onExpr.body;
|
||||
if (statements.length !== 1) return false;
|
||||
const stmt = statements[0];
|
||||
if (stmt.type !== "ExpressionStatement") return false;
|
||||
const expr = stmt.expression;
|
||||
if (expr.type !== "AssignmentExpression" || expr.operator !== "=") return false;
|
||||
return sourceCode.getText(expr.left) === bindText && expr.right.type === "Identifier" && expr.right.name === "$event";
|
||||
}
|
||||
if (onExpr.type === "ArrowFunctionExpression") {
|
||||
if (onExpr.params.length !== 1) return false;
|
||||
const param = onExpr.params[0];
|
||||
if (param.type !== "Identifier") return false;
|
||||
const body = onExpr.body;
|
||||
if (body.type !== "AssignmentExpression" || body.operator !== "=") return false;
|
||||
return sourceCode.getText(body.left) === bindText && body.right.type === "Identifier" && body.right.name === param.name;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
var prefer_v_model_default = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
docs: {
|
||||
description: "enforce using `v-model` instead of `:prop`/`@update:prop` pair",
|
||||
categories: void 0,
|
||||
url: "https://eslint.vuejs.org/rules/prefer-v-model.html"
|
||||
},
|
||||
fixable: null,
|
||||
hasSuggestions: true,
|
||||
schema: [],
|
||||
messages: {
|
||||
preferVModel: "Prefer `{{ vModelName }}` over the `:{{ propName }}`/`@update:{{ eventName }}` pair.",
|
||||
replaceWithVModel: "Replace with `{{ vModelName }}`."
|
||||
}
|
||||
},
|
||||
create(context) {
|
||||
const sourceCode = context.sourceCode;
|
||||
return import_utils.default.defineTemplateBodyVisitor(context, { VStartTag(node) {
|
||||
const element = node.parent;
|
||||
if (!import_utils.default.isCustomComponent(element)) return;
|
||||
const bindDirectives = [];
|
||||
const onDirectives = [];
|
||||
for (const attr of node.attributes) {
|
||||
if (!attr.directive) continue;
|
||||
if (attr.key.name.name === "bind" && getStaticArgName(attr) != null && attr.key.modifiers.length === 0) bindDirectives.push(attr);
|
||||
if (attr.key.name.name === "on" && getUpdateEventPropName(attr) != null && attr.key.modifiers.length === 0) onDirectives.push(attr);
|
||||
}
|
||||
for (const bindDir of bindDirectives) {
|
||||
const propName = getStaticArgName(bindDir);
|
||||
if (!propName) continue;
|
||||
const normalizedBindName = require_casing.camelCase(propName);
|
||||
const matchingOnDir = onDirectives.find((onDir) => require_casing.camelCase(getUpdateEventPropName(onDir)) === normalizedBindName);
|
||||
if (!matchingOnDir) continue;
|
||||
const bindExpr = bindDir.value?.expression;
|
||||
const onExpr = matchingOnDir.value?.expression;
|
||||
if (!bindExpr || bindExpr.type === "VFilterSequenceExpression" || bindExpr.type === "VForExpression" || bindExpr.type === "VOnExpression" || bindExpr.type === "VSlotScopeExpression" || !onExpr || !isMirrorAssignment(bindExpr, onExpr, sourceCode)) continue;
|
||||
const vModelName = normalizedBindName === "modelValue" ? "v-model" : `v-model:${propName}`;
|
||||
const eventName = getUpdateEventPropName(matchingOnDir) ?? propName;
|
||||
const vModelText = `${vModelName}=${sourceCode.getText(bindDir.value)}`;
|
||||
context.report({
|
||||
node: bindDir,
|
||||
messageId: "preferVModel",
|
||||
data: {
|
||||
vModelName,
|
||||
propName,
|
||||
eventName
|
||||
},
|
||||
suggest: [{
|
||||
messageId: "replaceWithVModel",
|
||||
data: { vModelName },
|
||||
*fix(fixer) {
|
||||
yield fixer.replaceText(bindDir, vModelText);
|
||||
const textBefore = sourceCode.getText().slice(0, matchingOnDir.range[0]);
|
||||
const removeStart = matchingOnDir.range[0] - (textBefore.length - textBefore.trimEnd().length);
|
||||
yield fixer.removeRange([removeStart, matchingOnDir.range[1]]);
|
||||
}
|
||||
}]
|
||||
});
|
||||
}
|
||||
} });
|
||||
}
|
||||
};
|
||||
|
||||
//#endregion
|
||||
exports.default = prefer_v_model_default;
|
||||
Reference in New Issue
Block a user