diff --git a/src/rules/relative-font-units.js b/src/rules/relative-font-units.js index f11725f9..8d18862b 100644 --- a/src/rules/relative-font-units.js +++ b/src/rules/relative-font-units.js @@ -9,6 +9,7 @@ /** * @import { CSSRuleDefinition } from "../types.js" + * @import { CssNode, CssNodePlain } from "@eslint/css-tree" * @typedef {"allowedFontUnits"} RelativeFontUnitsMessageIds * @typedef {[{allowUnits?: string[]}]} RelativeFontUnitsOptions * @typedef {CSSRuleDefinition<{ RuleOptions: RelativeFontUnitsOptions, MessageIds: RelativeFontUnitsMessageIds}>} RelativeFontUnitsRuleDefinition @@ -46,6 +47,22 @@ const disallowedFontSizeKeywords = new Set([ "math", ]); +/** + * Checks whether a font size uses a unit or keyword that is not allowed. + * @param {CssNodePlain} value The font size value. + * @param {string[]} allowedFontUnits The relative units that are allowed. + * @returns {boolean} Whether the font size value is disallowed. + */ +function isDisallowedFontSize(value, allowedFontUnits) { + return ( + (value.type === "Dimension" && + !allowedFontUnits.includes(value.unit.toLowerCase())) || + (value.type === "Identifier" && + disallowedFontSizeKeywords.has(value.name.toLowerCase())) || + (value.type === "Percentage" && !allowedFontUnits.includes("%")) + ); +} + //----------------------------------------------------------------------------- // Rule Definition //----------------------------------------------------------------------------- @@ -90,6 +107,7 @@ export default /** @satisfies {RelativeFontUnitsRuleDefinition} */ ({ create(context) { const [{ allowUnits: allowedFontUnits }] = context.options; + const { lexer } = context.sourceCode; return { Declaration(node) { @@ -100,18 +118,7 @@ export default /** @satisfies {RelativeFontUnitsRuleDefinition} */ ({ ) { const value = node.value.children[0]; - if ( - (value.type === "Dimension" && - !allowedFontUnits.includes( - value.unit.toLowerCase(), - )) || - (value.type === "Identifier" && - disallowedFontSizeKeywords.has( - value.name.toLowerCase(), - )) || - (value.type === "Percentage" && - !allowedFontUnits.includes("%")) - ) { + if (isDisallowedFontSize(value, allowedFontUnits)) { context.report({ loc: value.loc, messageId: "allowedFontUnits", @@ -130,68 +137,22 @@ export default /** @satisfies {RelativeFontUnitsRuleDefinition} */ ({ node.value.children.length > 0 ) { const value = node.value; - - const dimensionNode = value.children.find( - child => child.type === "Dimension", - ); - const identifierNode = value.children.find( - child => - child.type === "Identifier" && - disallowedFontSizeKeywords.has( - child.name.toLowerCase(), - ), - ); - const percentageNode = value.children.find( - (child, index) => { - const isPercentage = - child.type === "Percentage"; - const previousNode = value.children[index - 1]; - - const previousNodeIsSlashOperator = - previousNode && - previousNode.type === "Operator" && - previousNode.value === "/"; - - return ( - isPercentage && !previousNodeIsSlashOperator - ); - }, - ); - - let location; - let shouldReport = false; - - const conditions = [ - { - check: - !allowedFontUnits.includes("%") && - percentageNode, - loc: percentageNode?.loc, - }, - { - check: identifierNode, - loc: identifierNode?.loc, - }, - { - check: - dimensionNode && - !allowedFontUnits.includes( - dimensionNode.unit.toLowerCase(), + const match = lexer.matchProperty("font", value); + const fontSizeNode = match.matched + ? value.children.find(child => + match.isProperty( + /** @type {CssNode} */ (child), + "font-size", ), - loc: dimensionNode?.loc, - }, - ]; - for (const condition of conditions) { - if (condition.check) { - shouldReport = true; - location = condition.loc; - break; - } - } + ) + : undefined; - if (shouldReport) { + if ( + fontSizeNode && + isDisallowedFontSize(fontSizeNode, allowedFontUnits) + ) { context.report({ - loc: location, + loc: fontSizeNode.loc, messageId: "allowedFontUnits", data: { allowedFontUnits: diff --git a/tests/rules/relative-font-units.test.js b/tests/rules/relative-font-units.test.js index 1b69276d..549f6eaa 100644 --- a/tests/rules/relative-font-units.test.js +++ b/tests/rules/relative-font-units.test.js @@ -28,6 +28,8 @@ ruleTester.run("relative-font-units", rule, { "a { font-size: 1rem; }", "a { font: 2rem Arial, sans-serif; }", "a { font: 1.2rem/2 Arial, sans-serif; }", + "a { font: oblique 10deg 1rem serif; }", + "a { font: 1rem math; }", "a { font-size: 1REM; }", "a { font-size: 1Rem; }", "a { font-size: 1rEm; }", @@ -764,6 +766,19 @@ ruleTester.run("relative-font-units", rule, { }, ], }, + { + code: "a { font: oblique 10deg 1em serif; }", + errors: [ + { + messageId: "allowedFontUnits", + line: 1, + column: 25, + endLine: 1, + endColumn: 28, + data: { allowedFontUnits: "rem" }, + }, + ], + }, { code: "a { font-size: xx-small; }", errors: [