Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 

168 строки
3.4 KiB

  1. 'use strict';
  2. /**
  3. * @param {string | undefined} value
  4. * @return {string | undefined}
  5. */
  6. function trimValue(value) {
  7. return value ? value.trim() : value;
  8. }
  9. /**
  10. * @param {{nodes: import('postcss').Node[]}} node
  11. * @return {boolean}
  12. */
  13. function empty(node) {
  14. return !node.nodes.filter((child) => child.type !== 'comment').length;
  15. }
  16. /**
  17. * @param {import('postcss').AnyNode} nodeA
  18. * @param {import('postcss').AnyNode} nodeB
  19. * @return {boolean}
  20. */
  21. function equals(nodeA, nodeB) {
  22. const a = /** @type {any} */ (nodeA);
  23. const b = /** @type {any} */ (nodeB);
  24. if (a.type !== b.type) {
  25. return false;
  26. }
  27. if (a.important !== b.important) {
  28. return false;
  29. }
  30. if ((a.raws && !b.raws) || (!a.raws && b.raws)) {
  31. return false;
  32. }
  33. switch (a.type) {
  34. case 'rule':
  35. if (a.selector !== b.selector) {
  36. return false;
  37. }
  38. break;
  39. case 'atrule':
  40. if (a.name !== b.name || a.params !== b.params) {
  41. return false;
  42. }
  43. if (a.raws && trimValue(a.raws.before) !== trimValue(b.raws.before)) {
  44. return false;
  45. }
  46. if (
  47. a.raws &&
  48. trimValue(a.raws.afterName) !== trimValue(b.raws.afterName)
  49. ) {
  50. return false;
  51. }
  52. break;
  53. case 'decl':
  54. if (a.prop !== b.prop || a.value !== b.value) {
  55. return false;
  56. }
  57. if (a.raws && trimValue(a.raws.before) !== trimValue(b.raws.before)) {
  58. return false;
  59. }
  60. break;
  61. }
  62. if (a.nodes) {
  63. if (a.nodes.length !== b.nodes.length) {
  64. return false;
  65. }
  66. for (let i = 0; i < a.nodes.length; i++) {
  67. if (!equals(a.nodes[i], b.nodes[i])) {
  68. return false;
  69. }
  70. }
  71. }
  72. return true;
  73. }
  74. /**
  75. * @param {import('postcss').Rule} last
  76. * @param {import('postcss').AnyNode[]} nodes
  77. * @return {void}
  78. */
  79. function dedupeRule(last, nodes) {
  80. let index = nodes.indexOf(last) - 1;
  81. while (index >= 0) {
  82. const node = nodes[index--];
  83. if (node && node.type === 'rule' && node.selector === last.selector) {
  84. last.each((child) => {
  85. if (child.type === 'decl') {
  86. dedupeNode(child, node.nodes);
  87. }
  88. });
  89. if (empty(node)) {
  90. node.remove();
  91. }
  92. }
  93. }
  94. }
  95. /**
  96. * @param {import('postcss').AtRule | import('postcss').Declaration} last
  97. * @param {import('postcss').AnyNode[]} nodes
  98. * @return {void}
  99. */
  100. function dedupeNode(last, nodes) {
  101. let index = nodes.includes(last) ? nodes.indexOf(last) - 1 : nodes.length - 1;
  102. while (index >= 0) {
  103. const node = nodes[index--];
  104. if (node && equals(node, last)) {
  105. node.remove();
  106. }
  107. }
  108. }
  109. /**
  110. * @param {import('postcss').AnyNode} root
  111. * @return {void}
  112. */
  113. function dedupe(root) {
  114. const { nodes } =
  115. /** @type {import('postcss').Container<import('postcss').ChildNode>} */ (
  116. root
  117. );
  118. if (!nodes) {
  119. return;
  120. }
  121. let index = nodes.length - 1;
  122. while (index >= 0) {
  123. let last = nodes[index--];
  124. if (!last || !last.parent) {
  125. continue;
  126. }
  127. dedupe(last);
  128. if (last.type === 'rule') {
  129. dedupeRule(last, nodes);
  130. } else if (last.type === 'atrule' || last.type === 'decl') {
  131. dedupeNode(last, nodes);
  132. }
  133. }
  134. }
  135. /**
  136. * @type {import('postcss').PluginCreator<void>}
  137. * @return {import('postcss').Plugin}
  138. */
  139. function pluginCreator() {
  140. return {
  141. postcssPlugin: 'postcss-discard-duplicates',
  142. OnceExit(css) {
  143. dedupe(css);
  144. },
  145. };
  146. }
  147. pluginCreator.postcss = true;
  148. module.exports = pluginCreator;