ensureCompatibility.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. 'use strict';
  2. exports.__esModule = true;
  3. exports.pseudoElements = undefined;
  4. exports.default = ensureCompatibility;
  5. var _caniuseApi = require('caniuse-api');
  6. var _postcssSelectorParser = require('postcss-selector-parser');
  7. var _postcssSelectorParser2 = _interopRequireDefault(_postcssSelectorParser);
  8. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  9. var simpleSelectorRe = /^#?[-._a-z0-9 ]+$/i;
  10. var cssSel2 = 'css-sel2';
  11. var cssSel3 = 'css-sel3';
  12. var cssGencontent = 'css-gencontent';
  13. var cssFirstLetter = 'css-first-letter';
  14. var cssFirstLine = 'css-first-line';
  15. var cssInOutOfRange = 'css-in-out-of-range';
  16. var pseudoElements = exports.pseudoElements = {
  17. ':active': cssSel2,
  18. ':after': cssGencontent,
  19. ':before': cssGencontent,
  20. ':checked': cssSel3,
  21. ':default': 'css-default-pseudo',
  22. ':dir': 'css-dir-pseudo',
  23. ':disabled': cssSel3,
  24. ':empty': cssSel3,
  25. ':enabled': cssSel3,
  26. ':first-child': cssSel2,
  27. ':first-letter': cssFirstLetter,
  28. ':first-line': cssFirstLine,
  29. ':first-of-type': cssSel3,
  30. ':focus': cssSel2,
  31. ':focus-within': 'css-focus-within',
  32. ':has': 'css-has',
  33. ':hover': cssSel2,
  34. ':in-range': cssInOutOfRange,
  35. ':indeterminate': 'css-indeterminate-pseudo',
  36. ':lang': cssSel2,
  37. ':last-child': cssSel3,
  38. ':last-of-type': cssSel3,
  39. ':matches': 'css-matches-pseudo',
  40. ':not': cssSel3,
  41. ':nth-child': cssSel3,
  42. ':nth-last-child': cssSel3,
  43. ':nth-last-of-type': cssSel3,
  44. ':nth-of-type': cssSel3,
  45. ':only-child': cssSel3,
  46. ':only-of-type': cssSel3,
  47. ':optional': 'css-optional-pseudo',
  48. ':out-of-range': cssInOutOfRange,
  49. ':placeholder-shown': 'css-placeholder-shown',
  50. ':root': cssSel3,
  51. ':target': cssSel3,
  52. '::after': cssGencontent,
  53. '::backdrop': 'dialog',
  54. '::before': cssGencontent,
  55. '::first-letter': cssFirstLetter,
  56. '::first-line': cssFirstLine,
  57. '::marker': 'css-marker-pseudo',
  58. '::placeholder': 'css-placeholder',
  59. '::selection': 'css-selection'
  60. };
  61. function isCssMixin(selector) {
  62. return selector[selector.length - 1] === ':';
  63. }
  64. function ensureCompatibility(selectors, browsers, compatibilityCache) {
  65. // Should not merge mixins
  66. if (selectors.some(isCssMixin)) {
  67. return false;
  68. }
  69. return selectors.every(function (selector) {
  70. if (simpleSelectorRe.test(selector)) {
  71. return true;
  72. }
  73. if (compatibilityCache && selector in compatibilityCache) {
  74. return compatibilityCache[selector];
  75. }
  76. var compatible = true;
  77. (0, _postcssSelectorParser2.default)(function (ast) {
  78. ast.walk(function (node) {
  79. var type = node.type,
  80. value = node.value;
  81. if (type === 'pseudo') {
  82. var entry = pseudoElements[value];
  83. if (entry && compatible) {
  84. compatible = (0, _caniuseApi.isSupported)(entry, browsers);
  85. }
  86. }
  87. if (type === 'combinator') {
  88. if (~value.indexOf('~')) {
  89. compatible = (0, _caniuseApi.isSupported)(cssSel3, browsers);
  90. }
  91. if (~value.indexOf('>') || ~value.indexOf('+')) {
  92. compatible = (0, _caniuseApi.isSupported)(cssSel2, browsers);
  93. }
  94. }
  95. if (type === 'attribute' && node.attribute) {
  96. // [foo]
  97. if (!node.operator) {
  98. compatible = (0, _caniuseApi.isSupported)(cssSel2, browsers);
  99. }
  100. if (value) {
  101. // [foo="bar"], [foo~="bar"], [foo|="bar"]
  102. if (~['=', '~=', '|='].indexOf(node.operator)) {
  103. compatible = (0, _caniuseApi.isSupported)(cssSel2, browsers);
  104. }
  105. // [foo^="bar"], [foo$="bar"], [foo*="bar"]
  106. if (~['^=', '$=', '*='].indexOf(node.operator)) {
  107. compatible = (0, _caniuseApi.isSupported)(cssSel3, browsers);
  108. }
  109. }
  110. // [foo="bar" i]
  111. if (node.insensitive) {
  112. compatible = (0, _caniuseApi.isSupported)('css-case-insensitive', browsers);
  113. }
  114. }
  115. if (!compatible) {
  116. // If this node was not compatible,
  117. // break out early from walking the rest
  118. return false;
  119. }
  120. });
  121. }).process(selector);
  122. if (compatibilityCache) {
  123. compatibilityCache[selector] = compatible;
  124. }
  125. return compatible;
  126. });
  127. }