.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / stylelint / lib / rules / font-family-name-quotes / index.js
1 "use strict";
2
3 const findFontFamily = require("../../utils/findFontFamily");
4 const isStandardSyntaxValue = require("../../utils/isStandardSyntaxValue");
5 const isVariable = require("../../utils/isVariable");
6 const keywordSets = require("../../reference/keywordSets");
7 const report = require("../../utils/report");
8 const ruleMessages = require("../../utils/ruleMessages");
9 const validateOptions = require("../../utils/validateOptions");
10
11 const ruleName = "font-family-name-quotes";
12
13 const messages = ruleMessages(ruleName, {
14   expected: family => `Expected quotes around "${family}"`,
15   rejected: family => `Unexpected quotes around "${family}"`
16 });
17
18 function isSystemFontKeyword(font) {
19   if (font.indexOf("-apple-") === 0) {
20     return true;
21   }
22   if (font === "BlinkMacSystemFont") {
23     return true;
24   }
25   return false;
26 }
27
28 // "To avoid mistakes in escaping, it is recommended to quote font family names
29 // that contain white space, digits, or punctuation characters other than hyphens"
30 // (https://www.w3.org/TR/CSS2/fonts.html#font-family-prop)
31 function quotesRecommended(family) {
32   return !/^[-a-zA-Z]+$/.test(family);
33 }
34
35 // Quotes are required if the family is not a valid CSS identifier
36 // (regexes from https://mathiasbynens.be/notes/unquoted-font-family)
37 function quotesRequired(family) {
38   return family.split(/\s+/).some(word => {
39     return (
40       /^(-?\d|--)/.test(word) || !/^[-_a-zA-Z0-9\u00A0-\u10FFFF]+$/.test(word)
41     );
42   });
43 }
44
45 const rule = function(expectation) {
46   return (root, result) => {
47     const validOptions = validateOptions(result, ruleName, {
48       actual: expectation,
49       possible: [
50         "always-where-required",
51         "always-where-recommended",
52         "always-unless-keyword"
53       ]
54     });
55     if (!validOptions) {
56       return;
57     }
58
59     root.walkDecls(/^font(-family)?$/i, decl => {
60       const fontFamilies = findFontFamily(decl.value);
61
62       if (fontFamilies.length === 0) {
63         return;
64       }
65
66       fontFamilies.forEach(fontFamilyNode => {
67         let rawFamily = fontFamilyNode.value;
68
69         if (fontFamilyNode.quote) {
70           rawFamily = fontFamilyNode.quote + rawFamily + fontFamilyNode.quote;
71         }
72
73         checkFamilyName(rawFamily, decl);
74       });
75     });
76
77     function checkFamilyName(rawFamily, decl) {
78       if (!isStandardSyntaxValue(rawFamily)) {
79         return;
80       }
81       if (isVariable(rawFamily)) {
82         return;
83       }
84
85       const hasQuotes = rawFamily[0] === "'" || rawFamily[0] === '"';
86
87       // Clean the family of its quotes
88       const family = rawFamily.replace(/^['"]|['"]$/g, "");
89
90       // Disallow quotes around (case-insensitive) keywords
91       // and system font keywords in all cases
92       if (
93         keywordSets.fontFamilyKeywords.has(family.toLowerCase()) ||
94         isSystemFontKeyword(family)
95       ) {
96         if (hasQuotes) {
97           return complain(messages.rejected(family), family, decl);
98         }
99         return;
100       }
101
102       const required = quotesRequired(family);
103       const recommended = quotesRecommended(family);
104
105       switch (expectation) {
106         case "always-unless-keyword":
107           if (!hasQuotes) {
108             return complain(messages.expected(family), family, decl);
109           }
110           return;
111
112         case "always-where-recommended":
113           if (!recommended && hasQuotes) {
114             return complain(messages.rejected(family), family, decl);
115           }
116           if (recommended && !hasQuotes) {
117             return complain(messages.expected(family), family, decl);
118           }
119           return;
120
121         case "always-where-required":
122           if (!required && hasQuotes) {
123             return complain(messages.rejected(family), family, decl);
124           }
125           if (required && !hasQuotes) {
126             return complain(messages.expected(family), family, decl);
127           }
128           return;
129       }
130     }
131
132     function complain(message, family, decl) {
133       report({
134         result,
135         ruleName,
136         message,
137         node: decl,
138         word: family
139       });
140     }
141   };
142 };
143
144 rule.ruleName = ruleName;
145 rule.messages = messages;
146 module.exports = rule;