// NOTICE: This file is generated by Rollup. To modify it, // please instead edit the ESM counterpart and rebuild with Rollup (npm run build). 'use strict'; const valueParser = require('postcss-value-parser'); const keywords = require('../../reference/keywords.cjs'); const validateTypes = require('../../utils/validateTypes.cjs'); const nodeFieldIndices = require('../../utils/nodeFieldIndices.cjs'); const getDeclarationValue = require('../../utils/getDeclarationValue.cjs'); const isNumbery = require('../../utils/isNumbery.cjs'); const isStandardSyntaxValue = require('../../utils/isStandardSyntaxValue.cjs'); const isVariable = require('../../utils/isVariable.cjs'); const optionsMatches = require('../../utils/optionsMatches.cjs'); const report = require('../../utils/report.cjs'); const ruleMessages = require('../../utils/ruleMessages.cjs'); const setDeclarationValue = require('../../utils/setDeclarationValue.cjs'); const validateOptions = require('../../utils/validateOptions.cjs'); const ruleName = 'font-weight-notation'; const messages = ruleMessages(ruleName, { expected: (type) => `Expected ${type} font-weight notation`, expectedWithActual: (actual, expected) => `Expected "${actual}" to be "${expected}"`, }); const meta = { url: 'https://stylelint.io/user-guide/rules/font-weight-notation', fixable: true, }; const NORMAL_KEYWORD = 'normal'; const NAMED_TO_NUMERIC = new Map([ ['normal', '400'], ['bold', '700'], ]); const NUMERIC_TO_NAMED = new Map([ ['400', 'normal'], ['700', 'bold'], ]); /** @type {import('stylelint').CoreRules[ruleName]} */ const rule = (primary, secondaryOptions) => { return (root, result) => { const validOptions = validateOptions( result, ruleName, { actual: primary, possible: ['numeric', 'named-where-possible'], }, { actual: secondaryOptions, possible: { ignore: ['relative'], }, optional: true, }, ); if (!validOptions) { return; } const ignoreRelative = optionsMatches(secondaryOptions, 'ignore', 'relative'); root.walkDecls(/^font(-weight)?$/i, (decl) => { const isFontShorthandProp = decl.prop.toLowerCase() === 'font'; const parsedValue = valueParser(getDeclarationValue(decl)); const valueNodes = parsedValue.nodes; const hasNumericFontWeight = valueNodes.some((node, index, nodes) => { return isNumbery(node.value) && !isDivNode(nodes[index - 1]); }); for (const [index, valueNode] of valueNodes.entries()) { if (!isPossibleFontWeightNode(valueNode, index, valueNodes)) continue; const { value } = valueNode; if (isFontShorthandProp) { if (value.toLowerCase() === NORMAL_KEYWORD && hasNumericFontWeight) { continue; // Not `normal` for font-weight } if (checkWeight(decl, valueNode, parsedValue)) { break; // Stop traverse if font-weight is processed } } checkWeight(decl, valueNode, parsedValue); } }); /** @import { Node, ParsedValue } from 'postcss-value-parser' */ /** * @param {import('postcss').Declaration} decl * @param {Node} weightValueNode * @param {ParsedValue} parsedValue * @returns {true | undefined} */ function checkWeight(decl, weightValueNode, parsedValue) { const weightValue = weightValueNode.value; if (!isStandardSyntaxValue(weightValue)) { return; } if (isVariable(weightValue)) { return; } const lowerWeightValue = weightValue.toLowerCase(); if (ignoreRelative && keywords.fontWeightRelativeKeywords.has(lowerWeightValue)) { return; } const fixer = (/** @type {string} */ value) => () => { weightValueNode.value = value; setDeclarationValue(decl, parsedValue.toString()); }; if (primary === 'numeric') { if (!isNumbery(lowerWeightValue) && keywords.fontWeightNonNumericKeywords.has(lowerWeightValue)) { const numericValue = NAMED_TO_NUMERIC.get(lowerWeightValue); const fix = numericValue ? fixer(numericValue) : undefined; const msg = numericValue ? messages.expectedWithActual(weightValue, numericValue) : messages.expected('numeric'); complain(msg, weightValueNode, fix); return true; } } else if (primary === 'named-where-possible') { if (isNumbery(lowerWeightValue) && NUMERIC_TO_NAMED.has(lowerWeightValue)) { const namedValue = NUMERIC_TO_NAMED.get(lowerWeightValue); // microsoft/TypeScript#13086 validateTypes.assertString(namedValue); const fix = fixer(namedValue); complain(messages.expectedWithActual(weightValue, namedValue), weightValueNode, fix); return true; } } /** * @param {string} message * @param {import('postcss-value-parser').Node} valueNode * @param {(() => void) | undefined} fix */ function complain(message, valueNode, fix) { const index = nodeFieldIndices.declarationValueIndex(decl) + valueNode.sourceIndex; const endIndex = index + valueNode.value.length; report({ ruleName, result, message, node: decl, index, endIndex, fix: { apply: fix, node: decl, }, }); } } }; }; /** * @param {Node | undefined} node * @returns {boolean} */ function isDivNode(node) { return node !== undefined && node.type === 'div'; } /** * @param {Node} node * @param {number} index * @param {Node[]} nodes * @returns {boolean} */ function isPossibleFontWeightNode(node, index, nodes) { if (node.type !== 'word') return false; // Exclude `/` format like `16px/3`. if (isDivNode(nodes[index - 1])) return false; if (isDivNode(nodes[index + 1])) return false; return true; } rule.ruleName = ruleName; rule.messages = messages; rule.meta = meta; module.exports = rule;