fix(lint): enforce error typography composition

This commit is contained in:
Alexandre Brandizzi 2026-07-24 11:36:30 -03:00
parent 392af77b91
commit 620ac65153
2 changed files with 58 additions and 6 deletions

View file

@ -0,0 +1,55 @@
function containsVpErrorToken(node) {
if (!node || typeof node !== "object") {
return false;
}
if (node.type === "Literal" && typeof node.value === "string") {
return node.value.split(/\s+/u).includes("vp-error");
}
if (node.type === "TemplateElement") {
return node.value.raw.split(/\s+/u).includes("vp-error");
}
return Object.entries(node).some(([key, value]) => {
if (key === "parent") {
return false;
}
if (Array.isArray(value)) {
return value.some(containsVpErrorToken);
}
return containsVpErrorToken(value);
});
}
const rule = {
meta: {
type: "problem",
docs: {
description: "Require the Text component for legacy vp-error styling",
},
schema: [],
messages: {
useText:
'Use the Text component for the vp-error class; pair the error message with variant="error" so feedback stays accessible and consistent.',
},
},
create(context) {
return {
JSXAttribute(node) {
if (node.name?.name !== "className" || !containsVpErrorToken(node.value)) {
return;
}
const elementName = node.parent?.name;
if (elementName?.type === "JSXIdentifier" && elementName.name === "Text") {
return;
}
context.report({ node, messageId: "useText" });
},
};
},
};
export default rule;

View file

@ -6,10 +6,12 @@ import globals from "globals";
import tseslint from "typescript-eslint";
import noNonBooleanJsxAnd from "./eslint-rules/no-non-boolean-jsx-and.js";
import noVpErrorOutsideText from "./eslint-rules/no-vp-error-outside-text.js";
const localRules = {
rules: {
"no-non-boolean-jsx-and": noNonBooleanJsxAnd,
"no-vp-error-outside-text": noVpErrorOutsideText,
},
};
@ -72,6 +74,7 @@ export default tseslint.config(
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
"@typescript-eslint/no-explicit-any": "warn",
"seahaven/no-non-boolean-jsx-and": "error",
"seahaven/no-vp-error-outside-text": "error",
"no-restricted-syntax": [
"error",
{
@ -86,12 +89,6 @@ export default tseslint.config(
message:
"Use the shared Text component so typography semantics, family, tone, and feedback behavior stay consistent.",
},
{
selector:
"JSXOpeningElement:not([name.name='Text']) > JSXAttribute[name.name='className'][value.type='Literal'][value.value=/vp-error/]",
message:
'Use the Text component for the vp-error class; pair the error message with variant="error" so feedback stays accessible and consistent.',
},
],
},
},