--- /dev/null
+/* @flow */
+"use strict";
+
+const isNumbery = require("./isNumbery");
+const isStandardSyntaxValue = require("./isStandardSyntaxValue");
+const isValidFontSize = require("./isValidFontSize");
+const isVariable = require("./isVariable");
+const keywordSets = require("../reference/keywordSets");
+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<Object>*/ {
+ 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 <font-size>/, 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;
+};