processSelector.js 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. var generate = require('css-tree').generate;
  2. var specificity = require('./specificity');
  3. var nonFreezePseudoElements = {
  4. 'first-letter': true,
  5. 'first-line': true,
  6. 'after': true,
  7. 'before': true
  8. };
  9. var nonFreezePseudoClasses = {
  10. 'link': true,
  11. 'visited': true,
  12. 'hover': true,
  13. 'active': true,
  14. 'first-letter': true,
  15. 'first-line': true,
  16. 'after': true,
  17. 'before': true
  18. };
  19. module.exports = function freeze(node, usageData) {
  20. var pseudos = Object.create(null);
  21. var hasPseudo = false;
  22. node.prelude.children.each(function(simpleSelector) {
  23. var tagName = '*';
  24. var scope = 0;
  25. simpleSelector.children.each(function(node) {
  26. switch (node.type) {
  27. case 'ClassSelector':
  28. if (usageData && usageData.scopes) {
  29. var classScope = usageData.scopes[node.name] || 0;
  30. if (scope !== 0 && classScope !== scope) {
  31. throw new Error('Selector can\'t has classes from different scopes: ' + generate(simpleSelector));
  32. }
  33. scope = classScope;
  34. }
  35. break;
  36. case 'PseudoClassSelector':
  37. var name = node.name.toLowerCase();
  38. if (!nonFreezePseudoClasses.hasOwnProperty(name)) {
  39. pseudos[':' + name] = true;
  40. hasPseudo = true;
  41. }
  42. break;
  43. case 'PseudoElementSelector':
  44. var name = node.name.toLowerCase();
  45. if (!nonFreezePseudoElements.hasOwnProperty(name)) {
  46. pseudos['::' + name] = true;
  47. hasPseudo = true;
  48. }
  49. break;
  50. case 'TypeSelector':
  51. tagName = node.name.toLowerCase();
  52. break;
  53. case 'AttributeSelector':
  54. if (node.flags) {
  55. pseudos['[' + node.flags.toLowerCase() + ']'] = true;
  56. hasPseudo = true;
  57. }
  58. break;
  59. case 'WhiteSpace':
  60. case 'Combinator':
  61. tagName = '*';
  62. break;
  63. }
  64. });
  65. simpleSelector.compareMarker = specificity(simpleSelector).toString();
  66. simpleSelector.id = null; // pre-init property to avoid multiple hidden class
  67. simpleSelector.id = generate(simpleSelector);
  68. if (scope) {
  69. simpleSelector.compareMarker += ':' + scope;
  70. }
  71. if (tagName !== '*') {
  72. simpleSelector.compareMarker += ',' + tagName;
  73. }
  74. });
  75. // add property to all rule nodes to avoid multiple hidden class
  76. node.pseudoSignature = hasPseudo && Object.keys(pseudos).sort().join(',');
  77. };