@html-eslint/eslint-plugin
Version:
ESLint plugin for HTML
158 lines (145 loc) • 3.56 kB
JavaScript
/**
* @import {
* ScriptTag,
* StyleTag,
* Tag
* } from "@html-eslint/types"
* @import {RuleModule} from "../types"
* @typedef {"camelCase"
* | "snake_case"
* | "PascalCase"
* | "kebab-case"
* | "regex"} Option1
*
*
* @typedef {Object} Option2
* @property {string} pattern
* @property {string} [flags]
*/
const { RULE_CATEGORY } = require("../constants");
const {
isCamelCase,
isSnakeCase,
isPascalCase,
isKebabCase,
} = require("./utils/naming");
const { findAttr, isAttributesEmpty, hasTemplate } = require("./utils/node");
const { createVisitors } = require("./utils/visitors");
const { getRuleUrl } = require("./utils/rule");
const MESSAGE_IDS = {
WRONG: "wrong",
};
const CONVENTIONS = {
CAMEL_CASE: "camelCase",
SNAKE_CASE: "snake_case",
PASCAL_CASE: "PascalCase",
KEBAB_CASE: "kebab-case",
REGEX: "regex",
};
const CONVENTION_CHECKERS = {
[CONVENTIONS.CAMEL_CASE]: isCamelCase,
[CONVENTIONS.SNAKE_CASE]: isSnakeCase,
[CONVENTIONS.PASCAL_CASE]: isPascalCase,
[CONVENTIONS.KEBAB_CASE]: isKebabCase,
};
/** @type {RuleModule<[Option1, Option2]>} */
module.exports = {
meta: {
type: "code",
docs: {
description: "Enforce consistent naming of id attributes",
category: RULE_CATEGORY.STYLE,
recommended: false,
url: getRuleUrl("id-naming-convention"),
},
fixable: null,
schema: [
{
enum: Object.values(CONVENTIONS),
},
{
type: "object",
properties: {
pattern: { type: "string" },
flags: { type: "string" },
},
additionalProperties: false,
},
],
messages: {
[MESSAGE_IDS.WRONG]:
"The id '{{actual}}' is not matched with the {{convention}}.",
},
},
create(context) {
const convention =
context.options && context.options.length
? context.options[0]
: CONVENTIONS.SNAKE_CASE;
const checkNaming =
convention === CONVENTIONS.REGEX
? (/** @type string */ name) =>
new RegExp(
context.options[1].pattern,
context.options[1].flags || ""
).test(name)
: CONVENTION_CHECKERS[convention];
/** @param {Tag | ScriptTag | StyleTag} node */
function check(node) {
if (isAttributesEmpty(node)) {
return;
}
const idAttr = findAttr(node, "id");
if (
idAttr &&
idAttr.value &&
!hasTemplate(idAttr.value) &&
!checkNaming(idAttr.value.value)
) {
context.report({
node: idAttr,
data: {
actual: idAttr.value.value,
convention,
},
messageId: MESSAGE_IDS.WRONG,
});
}
}
/** @param {Tag | ScriptTag | StyleTag} node */
function checkInTemplate(node) {
if (isAttributesEmpty(node)) {
return;
}
const idAttr = findAttr(node, "id");
if (
idAttr &&
idAttr.value &&
!hasTemplate(idAttr.value) &&
!checkNaming(idAttr.value.value)
) {
context.report({
node: idAttr,
data: {
actual: idAttr.value.value,
convention,
},
messageId: MESSAGE_IDS.WRONG,
});
}
}
return createVisitors(
context,
{
Tag: check,
ScriptTag: check,
StyleTag: check,
},
{
Tag: checkInTemplate,
ScriptTag: checkInTemplate,
StyleTag: checkInTemplate,
}
);
},
};