findFontFamily.cjs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. // NOTICE: This file is generated by Rollup. To modify it,
  2. // please instead edit the ESM counterpart and rebuild with Rollup (npm run build).
  3. 'use strict';
  4. const valueParser = require('postcss-value-parser');
  5. const keywords = require('../reference/keywords.cjs');
  6. const validateTypes = require('./validateTypes.cjs');
  7. const isNumbery = require('./isNumbery.cjs');
  8. const isStandardSyntaxValue = require('./isStandardSyntaxValue.cjs');
  9. const isValidFontSize = require('./isValidFontSize.cjs');
  10. const isVariable = require('./isVariable.cjs');
  11. const nodeTypesToCheck = new Set(['word', 'string', 'space', 'div']);
  12. /** @typedef {import('postcss-value-parser').Node} Node */
  13. /**
  14. * @param {Node} firstNode
  15. * @param {Node} secondNode
  16. * @param {string | null} charactersBetween
  17. * @returns {Node}
  18. */
  19. function joinValueNodes(firstNode, secondNode, charactersBetween) {
  20. firstNode.value = firstNode.value + charactersBetween + secondNode.value;
  21. return firstNode;
  22. }
  23. /**
  24. * @param {Node} valueNode
  25. * @returns {valueNode is postcssValueParser.DivNode & { value: ',' }}
  26. */
  27. const isCommaDiv = (valueNode) => valueNode.type === 'div' && valueNode.value === ',';
  28. /**
  29. * Get the font-families within a `font` shorthand property value.
  30. *
  31. * @param {string} value
  32. * @returns {Node[]} Collection font-family nodes
  33. */
  34. function findFontFamily(value) {
  35. /** @type {Node[]} */
  36. const fontFamilies = [];
  37. const valueNodes = valueParser(value);
  38. const { nodes: children } = valueNodes;
  39. // Handle `inherit`, `initial` and etc
  40. if (children.length === 1 && children[0] && keywords.basicKeywords.has(children[0].value.toLowerCase())) {
  41. return [children[0]];
  42. }
  43. let needMergeNodesByValue = false;
  44. /** @type {string | null} */
  45. let mergeCharacters = null;
  46. valueNodes.walk((valueNode, index, nodes) => {
  47. if (valueNode.type === 'function') {
  48. return false;
  49. }
  50. if (!nodeTypesToCheck.has(valueNode.type)) {
  51. return;
  52. }
  53. const valueLowerCase = valueNode.value.toLowerCase();
  54. // Ignore non standard syntax
  55. if (!isStandardSyntaxValue(valueLowerCase)) {
  56. return;
  57. }
  58. // Ignore variables
  59. if (isVariable(valueLowerCase)) {
  60. return;
  61. }
  62. const isFontFamilyKeyword = keywords.fontFamilyKeywords.has(valueLowerCase);
  63. // Ignore keywords for other font parts
  64. if (!isFontFamilyKeyword && keywords.fontShorthandKeywords.has(valueLowerCase)) {
  65. return;
  66. }
  67. // Ignore font-sizes
  68. // NOTE: `math` is a keyword for both `font-family` and `font-size`.
  69. if (!isFontFamilyKeyword && isValidFontSize(valueNode.value)) {
  70. return;
  71. }
  72. const nextNode = nodes[index + 1];
  73. const prevNode = nodes[index - 1];
  74. const prevPrevNode = nodes[index - 2];
  75. const allPrevNodes = nodes.slice(0, index);
  76. // When the value is a keyword for both `font-family` and `font-size` (e.g. `math`),
  77. // if its next node is a comma, or if it is already the last node, or if there is any comma before it,
  78. // then treat it as a `font-family` keyword, otherwise treat it as a `font-size` keyword.
  79. if (
  80. isFontFamilyKeyword &&
  81. keywords.fontSizeKeywords.has(valueLowerCase) &&
  82. !(!nextNode || isCommaDiv(nextNode) || allPrevNodes.find(isCommaDiv))
  83. )
  84. return;
  85. // Ignore anything come after a <font-size>/, because it's a line-height
  86. if (prevNode && prevNode.value === '/' && prevPrevNode && isValidFontSize(prevPrevNode.value)) {
  87. return;
  88. }
  89. // Ignore number values
  90. if (isNumbery(valueLowerCase)) {
  91. return;
  92. }
  93. // Detect when a space or comma is dividing a list of font-families, and save the joining character.
  94. if (
  95. (valueNode.type === 'space' || (valueNode.type === 'div' && valueNode.value !== ',')) &&
  96. fontFamilies.length !== 0
  97. ) {
  98. needMergeNodesByValue = true;
  99. mergeCharacters = valueNode.value;
  100. return;
  101. }
  102. if (valueNode.type === 'space' || valueNode.type === 'div') {
  103. return;
  104. }
  105. const fontFamily = valueNode;
  106. if (needMergeNodesByValue) {
  107. const lastFontFamily = fontFamilies[fontFamilies.length - 1];
  108. validateTypes.assert(lastFontFamily);
  109. joinValueNodes(lastFontFamily, fontFamily, mergeCharacters);
  110. needMergeNodesByValue = false;
  111. mergeCharacters = null;
  112. } else {
  113. fontFamilies.push(fontFamily);
  114. }
  115. });
  116. return fontFamilies;
  117. }
  118. module.exports = findFontFamily;