index.es.js 3.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. import browserslist from 'browserslist';
  2. import postcss from 'postcss';
  3. var index = postcss.plugin('postcss-browser-comments', opts => root => {
  4. // client browserslist
  5. const clientBrowserList = browserslist(Object(opts).browsers || null, {
  6. path: root.source && root.source.input && root.source.input.file
  7. }); // root children references
  8. const references = root.nodes.slice(0); // for each child node of the root children references
  9. for (let node of references) {
  10. // if the node is a comment browser comment node
  11. if (isBrowserCommentNode(node)) {
  12. // rule following the browser comment
  13. const rule = node.next(); // browser data
  14. const browserdata = getBrowserData(node.text);
  15. if (browserdata.isNumbered) {
  16. rule.nodes.filter(isBrowserReferenceCommentNode).map(comment => {
  17. const browserdataIndex = parseFloat(comment.text) - 1;
  18. const browserslistPart = browserdata.browserslist[browserdataIndex]; // whether to remove the rule if the comment browserslist does not match the client browserslist
  19. const removeRule = !clientBrowserList.some(clientBrowser => browserslist(browserslistPart).some(commentBrowser => commentBrowser === clientBrowser)); // conditionally remove the declaration and reference comment
  20. if (removeRule) {
  21. comment.prev().remove();
  22. comment.remove();
  23. }
  24. }); // conditionally remove the empty rule and comment
  25. if (!rule.nodes.length) {
  26. rule.remove();
  27. node.remove();
  28. }
  29. } else {
  30. // whether to remove the rule if the comment browserslist does not match the client browserslist
  31. const removeRule = !clientBrowserList.some(clientBrowser => browserslist(browserdata.browserslist).some(commentBrowser => commentBrowser === clientBrowser)); // conditionally remove the rule and comment
  32. if (removeRule) {
  33. rule.remove();
  34. node.remove();
  35. }
  36. }
  37. }
  38. }
  39. }); // returns whether a node is a browser comment
  40. const isBrowserCommentNode = node => node.type === 'comment' && isBrowserCommentNodeRegExp.test(node.text) && node.next().type === 'rule';
  41. const isBrowserCommentNodeRegExp = /^\*\n * /; // returns whether a node is a browser reference comment
  42. const isBrowserReferenceCommentNode = node => node.type === 'comment' && isBrowserReferenceCommentNodeRegExp.test(node.text);
  43. const isBrowserReferenceCommentNodeRegExp = /^\d+$/; // returns browser data from comment text
  44. const getBrowserData = text => {
  45. const browserDataNumbered = text.match(browserDataMutliRegExp);
  46. const isNumbered = Boolean(browserDataNumbered);
  47. return {
  48. browserslist: isNumbered ? browserDataNumbered.map(browserslistPart => getBrowsersList(browserslistPart.replace(browserDataNumberedNewlineRegExp, '$1'))) : getBrowsersList(text.replace(browserDataNewlineRegExp, '')),
  49. isNumbered
  50. };
  51. };
  52. const browserDataMutliRegExp = /(\n \* \d+\. (?:[^\n]+|\n \* {4,})+)/g;
  53. const browserDataNewlineRegExp = /^\*\n \* ?|\n \*/g;
  54. const browserDataNumberedNewlineRegExp = /\n \* (?:( )\s*)?/g; // returns a browserlist from comment text
  55. const getBrowsersList = text => text.split(getBrowsersListInSplitRegExp).slice(1).map(part => part.split(getBrowsersListAndSplitRegExp).filter(part2 => part2)).reduce((acc, val) => acc.concat(val), []).map(part => part.replace(getBrowsersListQueryRegExp, ($0, browser, query) => browser === 'all' ? '> 0%' : `${browser}${query ? /^((?:\d*\.)?\d+)-$/.test(query) ? ` <= ${query.slice(0, -1)}` : ` ${query}` : ' > 0'}`).toLowerCase());
  56. const getBrowsersListInSplitRegExp = /\s+in\s+/;
  57. const getBrowsersListAndSplitRegExp = /(?: and|, and|,)/;
  58. const getBrowsersListQueryRegExp = /^\s*(\w+)(?: ((?:(?:\d*\.)?\d+-)?(?:\d*\.)?\d+[+-]?))?.*$/;
  59. export default index;