font.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. "use strict";
  2. const parsers = require("../parsers");
  3. const fontStyle = require("./fontStyle");
  4. const fontVariant = require("./fontVariant");
  5. const fontWeight = require("./fontWeight");
  6. const fontSize = require("./fontSize");
  7. const lineHeight = require("./lineHeight");
  8. const fontFamily = require("./fontFamily");
  9. const property = "font";
  10. module.exports.shorthandFor = new Map([
  11. [fontStyle.property, fontStyle],
  12. [fontVariant.property, fontVariant],
  13. [fontWeight.property, fontWeight],
  14. [fontSize.property, fontSize],
  15. [lineHeight.property, lineHeight],
  16. [fontFamily.property, fontFamily]
  17. ]);
  18. module.exports.parse = (v, opt = {}) => {
  19. const { globalObject } = opt;
  20. if (v === "") {
  21. return v;
  22. } else if (parsers.hasCalcFunc(v)) {
  23. v = parsers.resolveCalc(v);
  24. }
  25. if (!parsers.isValidPropertyValue(property, v)) {
  26. return;
  27. }
  28. const { AST_TYPES } = parsers;
  29. const [fontBlock, ...families] = parsers.splitValue(v, {
  30. delimiter: ","
  31. });
  32. const [fontBlockA, fontBlockB] = parsers.splitValue(fontBlock, {
  33. delimiter: "/"
  34. });
  35. const font = {
  36. [fontStyle.property]: "normal",
  37. [fontVariant.property]: "normal",
  38. [fontWeight.property]: "normal"
  39. };
  40. const fontFamilies = new Set();
  41. if (fontBlockB) {
  42. const [lineB, ...familiesB] = fontBlockB.trim().split(" ");
  43. if (!lineB || !familiesB.length) {
  44. return;
  45. }
  46. const lineHeightB = lineHeight.parse(lineB, {
  47. global
  48. });
  49. if (typeof lineHeightB !== "string") {
  50. return;
  51. }
  52. const familyB = fontFamily.parse(familiesB.join(" "), {
  53. globalObject,
  54. caseSensitive: true
  55. });
  56. if (typeof familyB === "string") {
  57. fontFamilies.add(familyB);
  58. } else {
  59. return;
  60. }
  61. const parts = parsers.splitValue(fontBlockA.trim());
  62. const properties = [
  63. fontStyle.property,
  64. fontVariant.property,
  65. fontWeight.property,
  66. fontSize.property
  67. ];
  68. for (const part of parts) {
  69. if (part === "normal") {
  70. continue;
  71. } else {
  72. for (const longhand of properties) {
  73. switch (longhand) {
  74. case fontSize.property: {
  75. const parsedValue = fontSize.parse(part, {
  76. globalObject
  77. });
  78. if (typeof parsedValue === "string") {
  79. font[longhand] = parsedValue;
  80. }
  81. break;
  82. }
  83. case fontStyle.property:
  84. case fontWeight.property: {
  85. if (font[longhand] === "normal") {
  86. const longhandItem = module.exports.shorthandFor.get(longhand);
  87. const parsedValue = longhandItem.parse(part, {
  88. globalObject
  89. });
  90. if (typeof parsedValue === "string") {
  91. font[longhand] = parsedValue;
  92. }
  93. }
  94. break;
  95. }
  96. case fontVariant.property: {
  97. if (font[longhand] === "normal") {
  98. const parsedValue = fontVariant.parse(part, {
  99. globalObject
  100. });
  101. if (typeof parsedValue === "string") {
  102. if (parsedValue === "small-cap") {
  103. font[longhand] = parsedValue;
  104. } else if (parsedValue !== "normal") {
  105. return;
  106. }
  107. }
  108. }
  109. break;
  110. }
  111. default:
  112. }
  113. }
  114. }
  115. }
  116. if (Object.hasOwn(font, fontSize.property)) {
  117. font[lineHeight.property] = lineHeightB;
  118. } else {
  119. return;
  120. }
  121. } else {
  122. const revParts = parsers.splitValue(fontBlockA.trim()).toReversed();
  123. if (revParts.length === 1) {
  124. const [part] = revParts;
  125. const value = parsers.parsePropertyValue(property, part, {
  126. globalObject,
  127. inArray: true
  128. });
  129. if (Array.isArray(value) && value.length === 1) {
  130. const [{ name, type }] = value;
  131. if (type === AST_TYPES.GLOBAL_KEYWORD) {
  132. return {
  133. [fontStyle.property]: name,
  134. [fontVariant.property]: name,
  135. [fontWeight.property]: name,
  136. [fontSize.property]: name,
  137. [lineHeight.property]: name,
  138. [fontFamily.property]: name
  139. };
  140. }
  141. }
  142. return;
  143. }
  144. const properties = [
  145. fontStyle.property,
  146. fontVariant.property,
  147. fontWeight.property,
  148. lineHeight.property
  149. ];
  150. for (const longhand of properties) {
  151. font[longhand] = "normal";
  152. }
  153. const revFontFamily = [];
  154. let fontSizeA;
  155. for (const part of revParts) {
  156. if (fontSizeA) {
  157. if (/^normal$/i.test(part)) {
  158. continue;
  159. } else {
  160. for (const longhand of properties) {
  161. switch (longhand) {
  162. case fontStyle.property:
  163. case fontWeight.property:
  164. case lineHeight.property: {
  165. if (font[longhand] === "normal") {
  166. const longhandItem = module.exports.shorthandFor.get(longhand);
  167. const parsedValue = longhandItem.parse(part, {
  168. globalObject
  169. });
  170. if (typeof parsedValue === "string") {
  171. font[longhand] = parsedValue;
  172. }
  173. }
  174. break;
  175. }
  176. case fontVariant.property: {
  177. if (font[longhand] === "normal") {
  178. const parsedValue = fontVariant.parse(part, {
  179. globalObject
  180. });
  181. if (typeof parsedValue === "string") {
  182. if (parsedValue === "small-cap") {
  183. font[longhand] = parsedValue;
  184. } else if (parsedValue !== "normal") {
  185. return;
  186. }
  187. }
  188. }
  189. break;
  190. }
  191. default:
  192. }
  193. }
  194. }
  195. } else {
  196. const parsedFontSize = fontSize.parse(part, {
  197. globalObject
  198. });
  199. if (typeof parsedFontSize === "string") {
  200. fontSizeA = parsedFontSize;
  201. } else {
  202. const parsedFontFamily = fontFamily.parse(part, {
  203. globalObject,
  204. caseSensitive: true
  205. });
  206. if (typeof parsedFontFamily === "string") {
  207. revFontFamily.push(parsedFontFamily);
  208. } else {
  209. return;
  210. }
  211. }
  212. }
  213. }
  214. const family = fontFamily.parse(revFontFamily.toReversed().join(" "), {
  215. globalObject,
  216. caseSensitive: true
  217. });
  218. if (fontSizeA && family) {
  219. font[fontSize.property] = fontSizeA;
  220. fontFamilies.add(fontFamily.parse(family));
  221. } else {
  222. return;
  223. }
  224. }
  225. for (const family of families) {
  226. const parsedFontFamily = fontFamily.parse(family, {
  227. globalObject,
  228. caseSensitive: true
  229. });
  230. if (parsedFontFamily) {
  231. fontFamilies.add(parsedFontFamily);
  232. } else {
  233. return;
  234. }
  235. }
  236. font[fontFamily.property] = [...fontFamilies].join(", ");
  237. return font;
  238. };
  239. module.exports.definition = {
  240. set(v) {
  241. v = parsers.prepareValue(v);
  242. if (v === "" || parsers.hasVarFunc(v)) {
  243. for (const [key] of module.exports.shorthandFor) {
  244. this._setProperty(key, "");
  245. }
  246. this._setProperty(property, v);
  247. } else {
  248. const obj = module.exports.parse(v, {
  249. globalObject: this._global
  250. });
  251. if (!obj) {
  252. return;
  253. }
  254. const priority = this._priorities.get(property) ?? "";
  255. const str = new Set();
  256. for (const [key] of module.exports.shorthandFor) {
  257. const val = obj[key];
  258. if (typeof val === "string") {
  259. this._setProperty(key, val, priority);
  260. if (val && val !== "normal" && !str.has(val)) {
  261. if (key === lineHeight.property) {
  262. str.add(`/ ${val}`);
  263. } else {
  264. str.add(val);
  265. }
  266. }
  267. }
  268. }
  269. this._setProperty(property, [...str].join(" "), priority);
  270. }
  271. },
  272. get() {
  273. const val = this.getPropertyValue(property);
  274. if (parsers.hasVarFunc(val)) {
  275. return val;
  276. }
  277. const str = new Set();
  278. for (const [key] of module.exports.shorthandFor) {
  279. const v = this.getPropertyValue(key);
  280. if (parsers.hasVarFunc(v)) {
  281. return "";
  282. }
  283. if (v && v !== "normal" && !str.has(v)) {
  284. if (key === lineHeight.property) {
  285. str.add(`/ ${v}`);
  286. } else {
  287. str.add(`${v}`);
  288. }
  289. }
  290. }
  291. return [...str].join(" ");
  292. },
  293. enumerable: true,
  294. configurable: true
  295. };
  296. module.exports.property = property;