index.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. 'use strict';
  2. exports.__esModule = true;
  3. var _postcss = require('postcss');
  4. var _alphanumSort = require('alphanum-sort');
  5. var _alphanumSort2 = _interopRequireDefault(_alphanumSort);
  6. var _has = require('has');
  7. var _has2 = _interopRequireDefault(_has);
  8. var _postcssSelectorParser = require('postcss-selector-parser');
  9. var _postcssSelectorParser2 = _interopRequireDefault(_postcssSelectorParser);
  10. var _unquote = require('./lib/unquote');
  11. var _unquote2 = _interopRequireDefault(_unquote);
  12. var _canUnquote = require('./lib/canUnquote');
  13. var _canUnquote2 = _interopRequireDefault(_canUnquote);
  14. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  15. var pseudoElements = ['::before', '::after', '::first-letter', '::first-line'];
  16. function getParsed(selectors, callback) {
  17. return (0, _postcssSelectorParser2.default)(callback).process(selectors).result;
  18. }
  19. function attribute(selector) {
  20. if (selector.value) {
  21. // Join selectors that are split over new lines
  22. selector.value = selector.value.replace(/\\\n/g, '').trim();
  23. if ((0, _canUnquote2.default)(selector.value)) {
  24. selector.value = (0, _unquote2.default)(selector.value);
  25. }
  26. selector.operator = selector.operator.trim();
  27. }
  28. if (selector.raws && selector.raws.insensitive) {
  29. selector.raws.insensitive = '';
  30. }
  31. selector.attribute = selector.attribute.trim();
  32. }
  33. function combinator(selector) {
  34. var value = selector.value.trim();
  35. selector.value = value.length ? value : ' ';
  36. }
  37. var pseudoReplacements = {
  38. ':nth-child': ':first-child',
  39. ':nth-of-type': ':first-of-type',
  40. ':nth-last-child': ':last-child',
  41. ':nth-last-of-type': ':last-of-type'
  42. };
  43. function pseudo(selector) {
  44. if (selector.nodes.length === 1 && pseudoReplacements[selector.value]) {
  45. var first = selector.at(0);
  46. var one = first.at(0);
  47. if (first.length === 1) {
  48. if (one.value === '1') {
  49. selector.replaceWith(_postcssSelectorParser2.default.pseudo({
  50. value: pseudoReplacements[selector.value]
  51. }));
  52. }
  53. if (one.value === 'even') {
  54. one.value = '2n';
  55. }
  56. }
  57. if (first.length === 3) {
  58. var two = first.at(1);
  59. var three = first.at(2);
  60. if (one.value === '2n' && two.value === '+' && three.value === '1') {
  61. one.value = 'odd';
  62. two.remove();
  63. three.remove();
  64. }
  65. }
  66. return;
  67. }
  68. var uniques = [];
  69. selector.walk(function (child) {
  70. if (child.type === 'selector') {
  71. var childStr = String(child);
  72. if (!~uniques.indexOf(childStr)) {
  73. uniques.push(childStr);
  74. } else {
  75. child.remove();
  76. }
  77. }
  78. });
  79. if (~pseudoElements.indexOf(selector.value)) {
  80. selector.value = selector.value.slice(1);
  81. }
  82. }
  83. var tagReplacements = {
  84. from: '0%',
  85. '100%': 'to'
  86. };
  87. function tag(selector) {
  88. var value = selector.value;
  89. if ((0, _has2.default)(tagReplacements, value)) {
  90. selector.value = tagReplacements[value];
  91. }
  92. }
  93. function universal(selector) {
  94. var next = selector.next();
  95. if (next && next.type !== 'combinator') {
  96. selector.remove();
  97. }
  98. }
  99. var reducers = {
  100. attribute: attribute,
  101. combinator: combinator,
  102. pseudo: pseudo,
  103. tag: tag,
  104. universal: universal
  105. };
  106. function optimise(rule) {
  107. var selector = rule.raws.selector && rule.raws.selector.value === rule.selector ? rule.raws.selector.raw : rule.selector;
  108. // If the selector ends with a ':' it is likely a part of a custom mixin,
  109. // so just pass through.
  110. if (selector[selector.length - 1] === ':') {
  111. return;
  112. }
  113. rule.selector = getParsed(selector, function (selectors) {
  114. selectors.nodes = (0, _alphanumSort2.default)(selectors.nodes, { insensitive: true });
  115. var uniqueSelectors = [];
  116. selectors.walk(function (sel) {
  117. var type = sel.type;
  118. // Trim whitespace around the value
  119. sel.spaces.before = sel.spaces.after = '';
  120. if ((0, _has2.default)(reducers, type)) {
  121. reducers[type](sel);
  122. return;
  123. }
  124. var toString = String(sel);
  125. if (type === 'selector' && sel.parent.type !== 'pseudo') {
  126. if (!~uniqueSelectors.indexOf(toString)) {
  127. uniqueSelectors.push(toString);
  128. } else {
  129. sel.remove();
  130. }
  131. }
  132. });
  133. });
  134. }
  135. exports.default = (0, _postcss.plugin)('postcss-minify-selectors', function () {
  136. return function (css) {
  137. return css.walkRules(optimise);
  138. };
  139. });
  140. module.exports = exports['default'];