UNPKG

@html-eslint/eslint-plugin

Version:
158 lines (145 loc) 3.56 kB
/** * @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, } ); }, };