提交学习笔记专用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

224 lines
5.3 KiB

  1. 'use strict';
  2. const valueParser = require('postcss-value-parser');
  3. const { getArguments } = require('cssnano-utils');
  4. const isColorStop = require('./isColorStop.js');
  5. const angles = {
  6. top: '0deg',
  7. right: '90deg',
  8. bottom: '180deg',
  9. left: '270deg',
  10. };
  11. /**
  12. * @param {valueParser.Dimension} a
  13. * @param {valueParser.Dimension} b
  14. * @return {boolean}
  15. */
  16. function isLessThan(a, b) {
  17. return (
  18. a.unit.toLowerCase() === b.unit.toLowerCase() &&
  19. parseFloat(a.number) >= parseFloat(b.number)
  20. );
  21. }
  22. /**
  23. * @param {import('postcss').Declaration} decl
  24. * @return {void}
  25. */
  26. function optimise(decl) {
  27. const value = decl.value;
  28. if (!value) {
  29. return;
  30. }
  31. const normalizedValue = value.toLowerCase();
  32. if (normalizedValue.includes('var(') || normalizedValue.includes('env(')) {
  33. return;
  34. }
  35. if (!normalizedValue.includes('gradient')) {
  36. return;
  37. }
  38. decl.value = valueParser(value)
  39. .walk((node) => {
  40. if (node.type !== 'function' || !node.nodes.length) {
  41. return false;
  42. }
  43. const lowerCasedValue = node.value.toLowerCase();
  44. if (
  45. lowerCasedValue === 'linear-gradient' ||
  46. lowerCasedValue === 'repeating-linear-gradient' ||
  47. lowerCasedValue === '-webkit-linear-gradient' ||
  48. lowerCasedValue === '-webkit-repeating-linear-gradient'
  49. ) {
  50. let args = getArguments(node);
  51. if (
  52. node.nodes[0].value.toLowerCase() === 'to' &&
  53. args[0].length === 3
  54. ) {
  55. node.nodes = node.nodes.slice(2);
  56. node.nodes[0].value =
  57. angles[
  58. /** @type {'top'|'right'|'bottom'|'left'}*/ (
  59. node.nodes[0].value.toLowerCase()
  60. )
  61. ];
  62. }
  63. /** @type {valueParser.Dimension | false} */
  64. let lastStop;
  65. args.forEach((arg, index) => {
  66. if (arg.length !== 3) {
  67. return;
  68. }
  69. let isFinalStop = index === args.length - 1;
  70. let thisStop = valueParser.unit(arg[2].value);
  71. if (lastStop === undefined) {
  72. lastStop = thisStop;
  73. if (
  74. !isFinalStop &&
  75. lastStop &&
  76. lastStop.number === '0' &&
  77. lastStop.unit.toLowerCase() !== 'deg'
  78. ) {
  79. arg[1].value = arg[2].value = '';
  80. }
  81. return;
  82. }
  83. if (lastStop && thisStop && isLessThan(lastStop, thisStop)) {
  84. arg[2].value = '0';
  85. }
  86. lastStop = thisStop;
  87. if (isFinalStop && arg[2].value === '100%') {
  88. arg[1].value = arg[2].value = '';
  89. }
  90. });
  91. return false;
  92. }
  93. if (
  94. lowerCasedValue === 'radial-gradient' ||
  95. lowerCasedValue === 'repeating-radial-gradient'
  96. ) {
  97. let args = getArguments(node);
  98. /** @type {valueParser.Dimension | false} */
  99. let lastStop;
  100. const hasAt = args[0].find((n) => n.value.toLowerCase() === 'at');
  101. args.forEach((arg, index) => {
  102. if (!arg[2] || (!index && hasAt)) {
  103. return;
  104. }
  105. let thisStop = valueParser.unit(arg[2].value);
  106. if (!lastStop) {
  107. lastStop = thisStop;
  108. return;
  109. }
  110. if (lastStop && thisStop && isLessThan(lastStop, thisStop)) {
  111. arg[2].value = '0';
  112. }
  113. lastStop = thisStop;
  114. });
  115. return false;
  116. }
  117. if (
  118. lowerCasedValue === '-webkit-radial-gradient' ||
  119. lowerCasedValue === '-webkit-repeating-radial-gradient'
  120. ) {
  121. let args = getArguments(node);
  122. /** @type {valueParser.Dimension | false} */
  123. let lastStop;
  124. args.forEach((arg) => {
  125. let color;
  126. let stop;
  127. if (arg[2] !== undefined) {
  128. if (arg[0].type === 'function') {
  129. color = `${arg[0].value}(${valueParser.stringify(arg[0].nodes)})`;
  130. } else {
  131. color = arg[0].value;
  132. }
  133. if (arg[2].type === 'function') {
  134. stop = `${arg[2].value}(${valueParser.stringify(arg[2].nodes)})`;
  135. } else {
  136. stop = arg[2].value;
  137. }
  138. } else {
  139. if (arg[0].type === 'function') {
  140. color = `${arg[0].value}(${valueParser.stringify(arg[0].nodes)})`;
  141. }
  142. color = arg[0].value;
  143. }
  144. color = color.toLowerCase();
  145. const colorStop =
  146. stop !== undefined
  147. ? isColorStop(color, stop.toLowerCase())
  148. : isColorStop(color);
  149. if (!colorStop || !arg[2]) {
  150. return;
  151. }
  152. let thisStop = valueParser.unit(arg[2].value);
  153. if (!lastStop) {
  154. lastStop = thisStop;
  155. return;
  156. }
  157. if (lastStop && thisStop && isLessThan(lastStop, thisStop)) {
  158. arg[2].value = '0';
  159. }
  160. lastStop = thisStop;
  161. });
  162. return false;
  163. }
  164. })
  165. .toString();
  166. }
  167. /**
  168. * @type {import('postcss').PluginCreator<void>}
  169. * @return {import('postcss').Plugin}
  170. */
  171. function pluginCreator() {
  172. return {
  173. postcssPlugin: 'postcss-minify-gradients',
  174. OnceExit(css) {
  175. css.walkDecls(optimise);
  176. },
  177. };
  178. }
  179. pluginCreator.postcss = true;
  180. module.exports = pluginCreator;