index.es.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import postcss from 'postcss';
  2. import parser from 'postcss-values-parser';
  3. import { lab2rgb } from '@csstools/convert-colors';
  4. function _slicedToArray(arr, i) {
  5. return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
  6. }
  7. function _arrayWithHoles(arr) {
  8. if (Array.isArray(arr)) return arr;
  9. }
  10. function _iterableToArrayLimit(arr, i) {
  11. var _arr = [];
  12. var _n = true;
  13. var _d = false;
  14. var _e = undefined;
  15. try {
  16. for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
  17. _arr.push(_s.value);
  18. if (i && _arr.length === i) break;
  19. }
  20. } catch (err) {
  21. _d = true;
  22. _e = err;
  23. } finally {
  24. try {
  25. if (!_n && _i["return"] != null) _i["return"]();
  26. } finally {
  27. if (_d) throw _e;
  28. }
  29. }
  30. return _arr;
  31. }
  32. function _nonIterableRest() {
  33. throw new TypeError("Invalid attempt to destructure non-iterable instance");
  34. }
  35. var index = postcss.plugin('postcss-color-gray', opts => root => {
  36. // walk all declarations likely containing a gray() function
  37. root.walkDecls(decl => {
  38. if (hasGrayFunction(decl)) {
  39. const originalValue = decl.value; // parse the declaration value
  40. const ast = parser(originalValue).parse(); // walk every node in the value that contains a gray() function
  41. ast.walk(node => {
  42. const _getFunctionGrayArgs = getFunctionGrayArgs(node),
  43. _getFunctionGrayArgs2 = _slicedToArray(_getFunctionGrayArgs, 2),
  44. lightness = _getFunctionGrayArgs2[0],
  45. alpha = _getFunctionGrayArgs2[1];
  46. if (lightness !== undefined) {
  47. // rename the gray() function to rgb()
  48. node.value = 'rgb'; // convert the lab gray lightness into rgb
  49. const _lab2rgb$map = lab2rgb(lightness, 0, 0).map(channel => Math.max(Math.min(Math.round(channel * 2.55), 255), 0)),
  50. _lab2rgb$map2 = _slicedToArray(_lab2rgb$map, 3),
  51. r = _lab2rgb$map2[0],
  52. g = _lab2rgb$map2[1],
  53. b = _lab2rgb$map2[2]; // preserve the slash nodes within rgb()
  54. const openingSlash = node.first;
  55. const closingSlash = node.last;
  56. node.removeAll() // replace the contents of rgb with `(r,g,b`
  57. .append(openingSlash).append(parser.number({
  58. value: r
  59. })).append(parser.comma({
  60. value: ','
  61. })).append(parser.number({
  62. value: g
  63. })).append(parser.comma({
  64. value: ','
  65. })).append(parser.number({
  66. value: b
  67. })); // if an alpha channel was defined
  68. if (alpha < 1) {
  69. // rename the rgb() function to rgba()
  70. node.value += 'a';
  71. node // append the contents of rgba with `,a`
  72. .append(parser.comma({
  73. value: ','
  74. })).append(parser.number({
  75. value: alpha
  76. }));
  77. } // append the contents of rgb/rgba with `)`
  78. node.append(closingSlash);
  79. }
  80. });
  81. const modifiedValue = ast.toString(); // if the modified value has changed from the original value
  82. if (originalValue !== modifiedValue) {
  83. // if the original gray() color is to be preserved
  84. if (Object(opts).preserve) {
  85. // insert the declaration value with the fallback before the current declaration
  86. decl.cloneBefore({
  87. value: modifiedValue
  88. });
  89. } else {
  90. // otherwise, overwrite the declaration value with the fallback
  91. decl.value = modifiedValue;
  92. }
  93. }
  94. }
  95. });
  96. }); // return whether a string contains a gray() function
  97. const hasGrayFunctionRegExp = /(^|[^\w-])gray\(/i;
  98. const hasGrayFunction = decl => hasGrayFunctionRegExp.test(Object(decl).value); // return whether a node matches a specific type
  99. const isNumber = node => Object(node).type === 'number';
  100. const isOperator = node => Object(node).type === 'operator';
  101. const isFunction = node => Object(node).type === 'func';
  102. const isCalcRegExp = /^calc$/i;
  103. const isFunctionCalc = node => isFunction(node) && isCalcRegExp.test(node.value);
  104. const isGrayRegExp = /^gray$/i;
  105. const isFunctionGrayWithArgs = node => isFunction(node) && isGrayRegExp.test(node.value) && node.nodes && node.nodes.length;
  106. const isNumberPercentage = node => isNumber(node) && node.unit === '%';
  107. const isNumberUnitless = node => isNumber(node) && node.unit === '';
  108. const isOperatorSlash = node => isOperator(node) && node.value === '/'; // return valid values from a node, otherwise undefined
  109. const getNumberUnitless = node => isNumberUnitless(node) ? Number(node.value) : undefined;
  110. const getOperatorSlash = node => isOperatorSlash(node) ? null : undefined;
  111. const getAlpha = node => isFunctionCalc(node) ? String(node) : isNumberUnitless(node) ? Number(node.value) : isNumberPercentage(node) ? Number(node.value) / 100 : undefined; // return valid arguments from a gray() function
  112. const functionalGrayArgs = [getNumberUnitless, getOperatorSlash, getAlpha];
  113. const getFunctionGrayArgs = node => {
  114. const validArgs = []; // if the node is a gray() function with arguments
  115. if (isFunctionGrayWithArgs(node)) {
  116. // get all the gray() function arguments between `(` and `)`
  117. const nodes = node.nodes.slice(1, -1); // validate each argument
  118. for (const index in nodes) {
  119. const arg = typeof functionalGrayArgs[index] === 'function' ? functionalGrayArgs[index](nodes[index]) : undefined; // if the argument was validated
  120. if (arg !== undefined) {
  121. // push any non-null argument to the valid arguments array
  122. if (arg !== null) {
  123. validArgs.push(arg);
  124. }
  125. } else {
  126. // otherwise, return an empty array
  127. return [];
  128. }
  129. } // return the valid arguments array
  130. return validArgs;
  131. } else {
  132. // otherwise, return an empty array
  133. return [];
  134. }
  135. };
  136. export default index;