3 const _ = require("lodash");
4 const declarationValueIndex = require("../../utils/declarationValueIndex");
5 const isNumbery = require("../../utils/isNumbery");
6 const isStandardSyntaxValue = require("../../utils/isStandardSyntaxValue");
7 const isVariable = require("../../utils/isVariable");
8 const keywordSets = require("../../reference/keywordSets");
9 const optionsMatches = require("../../utils/optionsMatches");
10 const postcss = require("postcss");
11 const report = require("../../utils/report");
12 const ruleMessages = require("../../utils/ruleMessages");
13 const validateOptions = require("../../utils/validateOptions");
15 const ruleName = "font-weight-notation";
17 const messages = ruleMessages(ruleName, {
18 expected: type => `Expected ${type} font-weight notation`,
19 invalidNamed: name => `Unexpected invalid font-weight name "${name}"`
22 const INHERIT_KEYWORD = "inherit";
23 const INITIAL_KEYWORD = "initial";
24 const NORMAL_KEYWORD = "normal";
25 const WEIGHTS_WITH_KEYWORD_EQUIVALENTS = ["400", "700"];
27 const rule = function(expectation, options) {
28 return (root, result) => {
29 const validOptions = validateOptions(
34 possible: ["numeric", "named-where-possible"]
48 root.walkDecls(decl => {
49 if (decl.prop.toLowerCase() === "font-weight") {
50 checkWeight(decl.value, decl);
53 if (decl.prop.toLowerCase() === "font") {
58 function checkFont(decl) {
59 const valueList = postcss.list.space(decl.value);
60 // We do not need to more carefully distinguish font-weight
61 // numbers from unitless line-heights because line-heights in
62 // `font` values need to be part of a font-size/line-height pair
63 const hasNumericFontWeight = valueList.some(isNumbery);
65 for (const value of postcss.list.space(decl.value)) {
67 (value.toLowerCase() === NORMAL_KEYWORD && !hasNumericFontWeight) ||
69 (value.toLowerCase() !== NORMAL_KEYWORD &&
70 keywordSets.fontWeightKeywords.has(value.toLowerCase()))
72 checkWeight(value, decl);
78 function checkWeight(weightValue, decl) {
79 if (!isStandardSyntaxValue(weightValue)) {
82 if (isVariable(weightValue)) {
86 weightValue.toLowerCase() === INHERIT_KEYWORD ||
87 weightValue.toLowerCase() === INITIAL_KEYWORD
93 optionsMatches(options, "ignore", "relative") &&
94 keywordSets.fontWeightRelativeKeywords.has(weightValue.toLowerCase())
99 const weightValueOffset = decl.value.indexOf(weightValue);
101 if (expectation === "numeric") {
102 if (!isNumbery(weightValue)) {
103 return complain(messages.expected("numeric"));
107 if (expectation === "named-where-possible") {
108 if (isNumbery(weightValue)) {
109 if (_.includes(WEIGHTS_WITH_KEYWORD_EQUIVALENTS, weightValue)) {
110 complain(messages.expected("named"));
115 !keywordSets.fontWeightKeywords.has(weightValue.toLowerCase()) &&
116 weightValue.toLowerCase() !== NORMAL_KEYWORD
118 return complain(messages.invalidNamed(weightValue));
123 function complain(message) {
129 index: declarationValueIndex(decl) + weightValueOffset
136 rule.ruleName = ruleName;
137 rule.messages = messages;
138 module.exports = rule;