提交学习笔记专用
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.

158 lines
3.5 KiB

  1. 'use strict';
  2. const browserslist = require('browserslist');
  3. const valueParser = require('postcss-value-parser');
  4. const { getArguments } = require('cssnano-utils');
  5. /**
  6. * Return the greatest common divisor
  7. * of two numbers.
  8. *
  9. * @param {number} a
  10. * @param {number} b
  11. * @return {number}
  12. */
  13. function gcd(a, b) {
  14. return b ? gcd(b, a % b) : a;
  15. }
  16. /**
  17. * @param {number} a
  18. * @param {number} b
  19. * @return {[number, number]}
  20. */
  21. function aspectRatio(a, b) {
  22. const divisor = gcd(a, b);
  23. return [a / divisor, b / divisor];
  24. }
  25. /**
  26. * @param {valueParser.Node[]} args
  27. * @return {string}
  28. */
  29. function split(args) {
  30. return args.map((arg) => valueParser.stringify(arg)).join('');
  31. }
  32. /**
  33. * @param {valueParser.Node} node
  34. * @return {void}
  35. */
  36. function removeNode(node) {
  37. node.value = '';
  38. node.type = 'word';
  39. }
  40. /**
  41. * @param {unknown[]} items
  42. * @return {string}
  43. */
  44. function sortAndDedupe(items) {
  45. const a = [...new Set(items)];
  46. a.sort();
  47. return a.join();
  48. }
  49. /**
  50. * @param {boolean} legacy
  51. * @param {import('postcss').AtRule} rule
  52. * @return {void}
  53. */
  54. function transform(legacy, rule) {
  55. const ruleName = rule.name.toLowerCase();
  56. // We should re-arrange parameters only for `@media` and `@supports` at-rules
  57. if (!rule.params || !['media', 'supports'].includes(ruleName)) {
  58. return;
  59. }
  60. const params = valueParser(rule.params);
  61. params.walk((node, index) => {
  62. if (node.type === 'div') {
  63. node.before = node.after = '';
  64. } else if (node.type === 'function') {
  65. node.before = '';
  66. if (
  67. node.nodes[0] &&
  68. node.nodes[0].type === 'word' &&
  69. node.nodes[0].value.startsWith('--') &&
  70. node.nodes[2] === undefined
  71. ) {
  72. node.after = ' ';
  73. } else {
  74. node.after = '';
  75. }
  76. if (
  77. node.nodes[4] &&
  78. node.nodes[0].value.toLowerCase().indexOf('-aspect-ratio') === 3
  79. ) {
  80. const [a, b] = aspectRatio(
  81. Number(node.nodes[2].value),
  82. Number(node.nodes[4].value)
  83. );
  84. node.nodes[2].value = a.toString();
  85. node.nodes[4].value = b.toString();
  86. }
  87. } else if (node.type === 'space') {
  88. node.value = ' ';
  89. } else {
  90. const prevWord = params.nodes[index - 2];
  91. if (
  92. node.value.toLowerCase() === 'all' &&
  93. rule.name.toLowerCase() === 'media' &&
  94. !prevWord
  95. ) {
  96. const nextWord = params.nodes[index + 2];
  97. if (!legacy || nextWord) {
  98. removeNode(node);
  99. }
  100. if (nextWord && nextWord.value.toLowerCase() === 'and') {
  101. const nextSpace = params.nodes[index + 1];
  102. const secondSpace = params.nodes[index + 3];
  103. removeNode(nextWord);
  104. removeNode(nextSpace);
  105. removeNode(secondSpace);
  106. }
  107. }
  108. }
  109. }, true);
  110. rule.params = sortAndDedupe(getArguments(params).map(split));
  111. if (!rule.params.length) {
  112. rule.raws.afterName = '';
  113. }
  114. }
  115. const allBugBrowers = new Set(['ie 10', 'ie 11']);
  116. /**
  117. * @type {import('postcss').PluginCreator<browserslist.Options>}
  118. * @param {browserslist.Options} options
  119. * @return {import('postcss').Plugin}
  120. */
  121. function pluginCreator(options = {}) {
  122. const browsers = browserslist(null, {
  123. stats: options.stats,
  124. path: __dirname,
  125. env: options.env,
  126. });
  127. const hasAllBug = browsers.some((browser) => allBugBrowers.has(browser));
  128. return {
  129. postcssPlugin: 'postcss-minify-params',
  130. OnceExit(css) {
  131. css.walkAtRules((rule) => transform(hasAllBug, rule));
  132. },
  133. };
  134. }
  135. pluginCreator.postcss = true;
  136. module.exports = pluginCreator;