1 | 'use strict';
|
2 |
|
3 | const declarationValueIndex = require('../../utils/declarationValueIndex');
|
4 | const isNumbery = require('../../utils/isNumbery');
|
5 | const isStandardSyntaxValue = require('../../utils/isStandardSyntaxValue');
|
6 | const isVariable = require('../../utils/isVariable');
|
7 | const keywordSets = require('../../reference/keywordSets');
|
8 | const optionsMatches = require('../../utils/optionsMatches');
|
9 | const postcss = require('postcss');
|
10 | const report = require('../../utils/report');
|
11 | const ruleMessages = require('../../utils/ruleMessages');
|
12 | const validateOptions = require('../../utils/validateOptions');
|
13 |
|
14 | const ruleName = 'font-weight-notation';
|
15 |
|
16 | const messages = ruleMessages(ruleName, {
|
17 | expected: (type) => `Expected ${type} font-weight notation`,
|
18 | invalidNamed: (name) => `Unexpected invalid font-weight name "${name}"`,
|
19 | });
|
20 |
|
21 | const INHERIT_KEYWORD = 'inherit';
|
22 | const INITIAL_KEYWORD = 'initial';
|
23 | const NORMAL_KEYWORD = 'normal';
|
24 | const WEIGHTS_WITH_KEYWORD_EQUIVALENTS = ['400', '700'];
|
25 |
|
26 | function rule(expectation, options) {
|
27 | return (root, result) => {
|
28 | const validOptions = validateOptions(
|
29 | result,
|
30 | ruleName,
|
31 | {
|
32 | actual: expectation,
|
33 | possible: ['numeric', 'named-where-possible'],
|
34 | },
|
35 | {
|
36 | actual: options,
|
37 | possible: {
|
38 | ignore: ['relative'],
|
39 | },
|
40 | optional: true,
|
41 | },
|
42 | );
|
43 |
|
44 | if (!validOptions) {
|
45 | return;
|
46 | }
|
47 |
|
48 | root.walkDecls((decl) => {
|
49 | if (decl.prop.toLowerCase() === 'font-weight') {
|
50 | checkWeight(decl.value, decl);
|
51 | }
|
52 |
|
53 | if (decl.prop.toLowerCase() === 'font') {
|
54 | checkFont(decl);
|
55 | }
|
56 | });
|
57 |
|
58 | function checkFont(decl) {
|
59 | const valueList = postcss.list.space(decl.value);
|
60 |
|
61 |
|
62 |
|
63 | const hasNumericFontWeight = valueList.some(isNumbery);
|
64 |
|
65 | for (const value of postcss.list.space(decl.value)) {
|
66 | if (
|
67 | (value.toLowerCase() === NORMAL_KEYWORD && !hasNumericFontWeight) ||
|
68 | isNumbery(value) ||
|
69 | (value.toLowerCase() !== NORMAL_KEYWORD &&
|
70 | keywordSets.fontWeightKeywords.has(value.toLowerCase()))
|
71 | ) {
|
72 | checkWeight(value, decl);
|
73 |
|
74 | return;
|
75 | }
|
76 | }
|
77 | }
|
78 |
|
79 | function checkWeight(weightValue, decl) {
|
80 | if (!isStandardSyntaxValue(weightValue)) {
|
81 | return;
|
82 | }
|
83 |
|
84 | if (isVariable(weightValue)) {
|
85 | return;
|
86 | }
|
87 |
|
88 | if (
|
89 | weightValue.toLowerCase() === INHERIT_KEYWORD ||
|
90 | weightValue.toLowerCase() === INITIAL_KEYWORD
|
91 | ) {
|
92 | return;
|
93 | }
|
94 |
|
95 | if (
|
96 | optionsMatches(options, 'ignore', 'relative') &&
|
97 | keywordSets.fontWeightRelativeKeywords.has(weightValue.toLowerCase())
|
98 | ) {
|
99 | return;
|
100 | }
|
101 |
|
102 | const weightValueOffset = decl.value.indexOf(weightValue);
|
103 |
|
104 | if (expectation === 'numeric') {
|
105 | if (decl.parent.type === 'atrule' && decl.parent.name.toLowerCase() === 'font-face') {
|
106 | const weightValueNumbers = postcss.list.space(weightValue);
|
107 |
|
108 | if (!weightValueNumbers.every(isNumbery)) {
|
109 | return complain(messages.expected('numeric'));
|
110 | }
|
111 |
|
112 | return;
|
113 | }
|
114 |
|
115 | if (!isNumbery(weightValue)) {
|
116 | return complain(messages.expected('numeric'));
|
117 | }
|
118 | }
|
119 |
|
120 | if (expectation === 'named-where-possible') {
|
121 | if (isNumbery(weightValue)) {
|
122 | if (WEIGHTS_WITH_KEYWORD_EQUIVALENTS.includes(weightValue)) {
|
123 | complain(messages.expected('named'));
|
124 | }
|
125 |
|
126 | return;
|
127 | }
|
128 |
|
129 | if (
|
130 | !keywordSets.fontWeightKeywords.has(weightValue.toLowerCase()) &&
|
131 | weightValue.toLowerCase() !== NORMAL_KEYWORD
|
132 | ) {
|
133 | return complain(messages.invalidNamed(weightValue));
|
134 | }
|
135 | }
|
136 |
|
137 | function complain(message) {
|
138 | report({
|
139 | ruleName,
|
140 | result,
|
141 | message,
|
142 | node: decl,
|
143 | index: declarationValueIndex(decl) + weightValueOffset,
|
144 | });
|
145 | }
|
146 | }
|
147 | };
|
148 | }
|
149 |
|
150 | rule.ruleName = ruleName;
|
151 | rule.messages = messages;
|
152 | module.exports = rule;
|