css-font-parser
Version:
A parser for the CSS font value
1 lines • 8.97 kB
Source Map (JSON)
{"version":3,"file":"css-font-parser.cjs","sources":["../src/parser.js"],"sourcesContent":["/**\n * @enum {number}\n */\nconst states = {\n VARIATION: 1,\n LINE_HEIGHT: 2,\n FONT_FAMILY: 3,\n BEFORE_FONT_FAMILY: 4,\n AFTER_OBLIQUE: 5,\n ESCAPING: 6,\n IDENTIFIER: 7,\n HEXESCAPING: 8,\n};\n\n/**\n * Returns true if the identifier is valid.\n * @param {string} identifier\n * @return {boolean}\n */\nfunction isValidIdentifier(identifier) {\n return !/^(-?\\d|--)/.test(identifier);\n}\n\n/**\n * Attempt to parse a string as an identifier. Return\n * a normalized identifier, or null when the string\n * contains an invalid identifier.\n *\n * @param {string} str\n * @return {string|null}\n */\nfunction parseIdentifier(str) {\n const identifiers = [];\n let buffer = \"\";\n let hex = \"\";\n let state = states.IDENTIFIER;\n\n for (let c, i = 0; (c = str.charAt(i)); i++) {\n if (/[a-zA-Z\\d\\xa0-\\uffff_-]/.test(c) && state === states.IDENTIFIER) {\n buffer += c;\n } else if (c === \"\\\\\" && state === states.IDENTIFIER) {\n state = states.ESCAPING;\n } else if (c === \" \" && state === states.IDENTIFIER) {\n if (buffer !== \"\") {\n if (isValidIdentifier(buffer)) {\n identifiers.push(buffer);\n buffer = \"\";\n } else {\n return null;\n }\n }\n } else if (state === states.ESCAPING) {\n if (/[0-9a-f]/i.test(c)) {\n hex += c;\n state = states.HEXESCAPING;\n } else {\n buffer += c;\n state = states.IDENTIFIER;\n }\n } else if (state === states.HEXESCAPING) {\n if (/[0-9a-f]/i.test(c) && hex.length < 6) {\n hex += c;\n } else {\n buffer += String.fromCodePoint(parseInt(hex, 16));\n buffer += c;\n hex = \"\";\n state = states.IDENTIFIER;\n }\n } else {\n return null;\n }\n }\n\n if (buffer !== \"\") {\n if (isValidIdentifier(buffer)) {\n identifiers.push(buffer);\n } else {\n return null;\n }\n }\n\n return identifiers.join(\" \");\n}\n\n/**\n * @param {string} input\n * @param {states} initialState\n * @return {Object|null}\n */\nfunction parse(input, initialState) {\n let state = initialState;\n let buffer = \"\";\n const result = {\n \"font-family\": [],\n };\n\n for (let c, i = 0; (c = input.charAt(i)); i += 1) {\n if (state === states.BEFORE_FONT_FAMILY && (c === '\"' || c === \"'\")) {\n let index = i + 1;\n\n // consume the entire string\n do {\n index = input.indexOf(c, index) + 1;\n if (!index) {\n // If a string is not closed by a ' or \" return null.\n return null;\n }\n } while (input.charAt(index - 2) === \"\\\\\");\n\n result[\"font-family\"].push(\n input.slice(i + 1, index - 1).replace(/\\\\('|\")/g, \"$1\")\n );\n\n i = index - 1;\n state = states.FONT_FAMILY;\n buffer = \"\";\n } else if (state === states.FONT_FAMILY && c === \",\") {\n state = states.BEFORE_FONT_FAMILY;\n buffer = \"\";\n } else if (state === states.BEFORE_FONT_FAMILY && c === \",\") {\n const identifier = parseIdentifier(buffer);\n\n if (identifier) {\n result[\"font-family\"].push(identifier);\n }\n buffer = \"\";\n } else if (state === states.AFTER_OBLIQUE && c === \" \") {\n if (/^(?:\\+|-)?(?:[0-9]*\\.)?[0-9]+(?:deg|grad|rad|turn)$/.test(buffer)) {\n result[\"font-style\"] += \" \" + buffer;\n buffer = \"\";\n } else {\n // The 'oblique' token was not followed by an angle.\n // Backtrack to allow the token to be parsed as VARIATION\n i -= 1;\n }\n state = states.VARIATION;\n } else if (state === states.VARIATION && (c === \" \" || c === \"/\")) {\n if (\n /^(?:(?:xx|x)-large|(?:xx|s)-small|small|large|medium)$/.test(buffer) ||\n /^(?:larg|small)er$/.test(buffer) ||\n /^(?:\\+|-)?(?:[0-9]*\\.)?[0-9]+(?:em|ex|ch|rem|vh|vw|vmin|vmax|px|mm|cm|in|pt|pc|%)$/.test(\n buffer\n )\n ) {\n state = c === \"/\" ? states.LINE_HEIGHT : states.BEFORE_FONT_FAMILY;\n result[\"font-size\"] = buffer;\n } else if (/^italic$/.test(buffer)) {\n result[\"font-style\"] = buffer;\n } else if (/^oblique$/.test(buffer)) {\n result[\"font-style\"] = buffer;\n state = states.AFTER_OBLIQUE;\n } else if (/^small-caps$/.test(buffer)) {\n result[\"font-variant\"] = buffer;\n } else if (/^(?:bold(?:er)?|lighter|normal)$/.test(buffer)) {\n result[\"font-weight\"] = buffer;\n } else if (\n /^[+-]?(?:[0-9]*\\.)?[0-9]+(?:e[+-]?(?:0|[1-9][0-9]*))?$/.test(buffer)\n ) {\n const num = parseFloat(buffer);\n if (num >= 1 && num <= 1000) {\n result[\"font-weight\"] = buffer;\n }\n } else if (\n /^(?:(?:ultra|extra|semi)-)?(?:condensed|expanded)$/.test(buffer)\n ) {\n result[\"font-stretch\"] = buffer;\n }\n buffer = \"\";\n } else if (state === states.LINE_HEIGHT && c === \" \") {\n if (\n /^(?:\\+|-)?([0-9]*\\.)?[0-9]+(?:em|ex|ch|rem|vh|vw|vmin|vmax|px|mm|cm|in|pt|pc|%)?$/.test(\n buffer\n )\n ) {\n result[\"line-height\"] = buffer;\n }\n state = states.BEFORE_FONT_FAMILY;\n buffer = \"\";\n } else {\n buffer += c;\n }\n }\n\n // This is for the case where a string was specified followed by\n // an identifier, but without a separating comma.\n if (state === states.FONT_FAMILY && !/^\\s*$/.test(buffer)) {\n return null;\n }\n\n if (state === states.BEFORE_FONT_FAMILY) {\n const identifier = parseIdentifier(buffer);\n\n if (identifier) {\n result[\"font-family\"].push(identifier);\n }\n }\n\n return result;\n}\n\nfunction parseFontFamily(str) {\n const result = parse(str, states.BEFORE_FONT_FAMILY);\n\n if (result !== null) {\n return result[\"font-family\"];\n } else {\n return null;\n }\n}\n\nfunction parseFont(str) {\n const result = parse(str, states.VARIATION);\n\n if (result !== null && result[\"font-size\"] && result[\"font-family\"].length) {\n return result;\n } else {\n return null;\n }\n}\n\nexport { parseFont, parseFontFamily };\n"],"names":["isValidIdentifier","identifier","test","parseIdentifier","str","c","identifiers","buffer","hex","state","i","charAt","push","length","String","fromCodePoint","parseInt","join","parse","input","initialState","result","parseFloat","num","index","indexOf","slice","replace"],"mappings":"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"}