UNPKG

3.03 kBJavaScriptView Raw
1'use strict';
2
3const isNumbery = require('./isNumbery');
4const isStandardSyntaxValue = require('./isStandardSyntaxValue');
5const isValidFontSize = require('./isValidFontSize');
6const isVariable = require('./isVariable');
7const keywordSets = require('../reference/keywordSets');
8const postcssValueParser = require('postcss-value-parser');
9
10const nodeTypesToCheck = new Set(['word', 'string', 'space', 'div']);
11
12/** @typedef {import('postcss-value-parser').Node} Node */
13
14/**
15 *
16 * @param {Node} firstNode
17 * @param {Node} secondNode
18 * @param {string | null} charactersBetween
19 *
20 * @returns {Node}
21 */
22function joinValueNodes(firstNode, secondNode, charactersBetween) {
23 firstNode.value = firstNode.value + charactersBetween + secondNode.value;
24
25 return firstNode;
26}
27
28/**
29 * Get the font-families within a `font` shorthand property value.
30 *
31 * @param {string} value
32 * @return {object} Collection font-family nodes
33 */
34module.exports = function findFontFamily(value) {
35 /** @type {Node[]} */
36 const fontFamilies = [];
37
38 const valueNodes = postcssValueParser(value);
39
40 // Handle `inherit`, `initial` and etc
41 if (
42 valueNodes.nodes.length === 1 &&
43 keywordSets.basicKeywords.has(valueNodes.nodes[0].value.toLowerCase())
44 ) {
45 return [valueNodes.nodes[0]];
46 }
47
48 let needMergeNodesByValue = false;
49 /** @type {string | null} */
50 let mergeCharacters = null;
51
52 valueNodes.walk((valueNode, index, nodes) => {
53 if (valueNode.type === 'function') {
54 return false;
55 }
56
57 if (!nodeTypesToCheck.has(valueNode.type)) {
58 return;
59 }
60
61 const valueLowerCase = valueNode.value.toLowerCase();
62
63 // Ignore non standard syntax
64 if (!isStandardSyntaxValue(valueLowerCase)) {
65 return;
66 }
67
68 // Ignore variables
69 if (isVariable(valueLowerCase)) {
70 return;
71 }
72
73 // Ignore keywords for other font parts
74 if (
75 keywordSets.fontShorthandKeywords.has(valueLowerCase) &&
76 !keywordSets.fontFamilyKeywords.has(valueLowerCase)
77 ) {
78 return;
79 }
80
81 // Ignore font-sizes
82 if (isValidFontSize(valueNode.value)) {
83 return;
84 }
85
86 // Ignore anything come after a <font-size>/, because it's a line-height
87 if (
88 nodes[index - 1] &&
89 nodes[index - 1].value === '/' &&
90 nodes[index - 2] &&
91 isValidFontSize(nodes[index - 2].value)
92 ) {
93 return;
94 }
95
96 // Ignore number values
97 if (isNumbery(valueLowerCase)) {
98 return;
99 }
100
101 // Detect when a space or comma is dividing a list of font-families, and save the joining character.
102 if (
103 (valueNode.type === 'space' || (valueNode.type === 'div' && valueNode.value !== ',')) &&
104 fontFamilies.length !== 0
105 ) {
106 needMergeNodesByValue = true;
107 mergeCharacters = valueNode.value;
108
109 return;
110 }
111
112 if (valueNode.type === 'space' || valueNode.type === 'div') {
113 return;
114 }
115
116 const fontFamily = valueNode;
117
118 if (needMergeNodesByValue) {
119 joinValueNodes(fontFamilies[fontFamilies.length - 1], valueNode, mergeCharacters);
120 needMergeNodesByValue = false;
121 mergeCharacters = null;
122 } else {
123 fontFamilies.push(fontFamily);
124 }
125 });
126
127 return fontFamilies;
128};