Rule.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. var hasOwnProperty = Object.prototype.hasOwnProperty;
  2. var walk = require('css-tree').walk;
  3. var { hasNoChildren } = require('./utils');
  4. function cleanUnused(selectorList, usageData) {
  5. selectorList.children.each(function(selector, item, list) {
  6. var shouldRemove = false;
  7. walk(selector, function(node) {
  8. // ignore nodes in nested selectors
  9. if (this.selector === null || this.selector === selectorList) {
  10. switch (node.type) {
  11. case 'SelectorList':
  12. // TODO: remove toLowerCase when pseudo selectors will be normalized
  13. // ignore selectors inside :not()
  14. if (this.function === null || this.function.name.toLowerCase() !== 'not') {
  15. if (cleanUnused(node, usageData)) {
  16. shouldRemove = true;
  17. }
  18. }
  19. break;
  20. case 'ClassSelector':
  21. if (usageData.whitelist !== null &&
  22. usageData.whitelist.classes !== null &&
  23. !hasOwnProperty.call(usageData.whitelist.classes, node.name)) {
  24. shouldRemove = true;
  25. }
  26. if (usageData.blacklist !== null &&
  27. usageData.blacklist.classes !== null &&
  28. hasOwnProperty.call(usageData.blacklist.classes, node.name)) {
  29. shouldRemove = true;
  30. }
  31. break;
  32. case 'IdSelector':
  33. if (usageData.whitelist !== null &&
  34. usageData.whitelist.ids !== null &&
  35. !hasOwnProperty.call(usageData.whitelist.ids, node.name)) {
  36. shouldRemove = true;
  37. }
  38. if (usageData.blacklist !== null &&
  39. usageData.blacklist.ids !== null &&
  40. hasOwnProperty.call(usageData.blacklist.ids, node.name)) {
  41. shouldRemove = true;
  42. }
  43. break;
  44. case 'TypeSelector':
  45. // TODO: remove toLowerCase when type selectors will be normalized
  46. // ignore universal selectors
  47. if (node.name.charAt(node.name.length - 1) !== '*') {
  48. if (usageData.whitelist !== null &&
  49. usageData.whitelist.tags !== null &&
  50. !hasOwnProperty.call(usageData.whitelist.tags, node.name.toLowerCase())) {
  51. shouldRemove = true;
  52. }
  53. if (usageData.blacklist !== null &&
  54. usageData.blacklist.tags !== null &&
  55. hasOwnProperty.call(usageData.blacklist.tags, node.name.toLowerCase())) {
  56. shouldRemove = true;
  57. }
  58. }
  59. break;
  60. }
  61. }
  62. });
  63. if (shouldRemove) {
  64. list.remove(item);
  65. }
  66. });
  67. return selectorList.children.isEmpty();
  68. }
  69. module.exports = function cleanRule(node, item, list, options) {
  70. if (hasNoChildren(node.prelude) || hasNoChildren(node.block)) {
  71. list.remove(item);
  72. return;
  73. }
  74. var usageData = options.usage;
  75. if (usageData && (usageData.whitelist !== null || usageData.blacklist !== null)) {
  76. cleanUnused(node.prelude, usageData);
  77. if (hasNoChildren(node.prelude)) {
  78. list.remove(item);
  79. return;
  80. }
  81. }
  82. };