/* @flow */ "use strict" const keywordSets = require("../reference/keywordSets") const isNumbery = require("./isNumbery") const isStandardSyntaxValue = require("./isStandardSyntaxValue") const isValidFontSize = require("./isValidFontSize") const isVariable = require("./isVariable") const postcssValueParser = require("postcss-value-parser") const nodeTypesToCheck = new Set([ "word", "string", "space", "div" ]) function joinValueNodes(firstNode, secondNode, charactersBetween) { firstNode.value = firstNode.value + charactersBetween + secondNode.value return firstNode } /** * Get the font-families within a `font` shorthand property value. * * @param {string} value * @return {object} Collection font-family nodes */ module.exports = function findFontFamily(value/*: string*/)/*: Array*/ { const fontFamilies = [] const valueNodes = postcssValueParser(value) // Handle `inherit`, `initial` and etc if (valueNodes.nodes.length === 1 && keywordSets.basicKeywords.has(valueNodes.nodes[0].value.toLowerCase())) { return [valueNodes.nodes[0]] } let needMergeNodesByValue = false let mergeCharacters = null valueNodes.walk((valueNode, index, nodes) => { if (valueNode.type === "function") { return false } if (!nodeTypesToCheck.has(valueNode.type)) { return } const valueLowerCase = valueNode.value.toLowerCase() // Ignore non standard syntax if (!isStandardSyntaxValue(valueLowerCase)) { return } // Ignore variables if (isVariable(valueLowerCase)) { return } // Ignore keywords for other font parts if (keywordSets.fontShorthandKeywords.has(valueLowerCase) && !keywordSets.fontFamilyKeywords.has(valueLowerCase)) { return } // Ignore font-sizes if (isValidFontSize(valueNode.value)) { return } // Ignore anything come after a /, because it's a line-height if (nodes[index - 1] && nodes[index - 1].value === "/" && nodes[index - 2] && isValidFontSize(nodes[index - 2].value)) { return } // Ignore number values if (isNumbery(valueLowerCase)) { return } // Detect when a space or comma is dividing a list of font-families, and save the joining character. if ((valueNode.type === "space" || valueNode.type === "div" && valueNode.value !== ",") && fontFamilies.length !== 0) { needMergeNodesByValue = true mergeCharacters = valueNode.value return } else if (valueNode.type === "space" || valueNode.type === "div") { return } const fontFamily = valueNode if (needMergeNodesByValue) { joinValueNodes(fontFamilies[fontFamilies.length - 1], valueNode, mergeCharacters) needMergeNodesByValue = false mergeCharacters = null } else { fontFamilies.push(fontFamily) } }) return fontFamilies }