css-medias-alt-minifier.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. /*
  2. * Copyright 2010-2019 Gildas Lormeau
  3. * contact : gildas.lormeau <at> gmail.com
  4. *
  5. * This file is part of SingleFile.
  6. *
  7. * The code in this file is free software: you can redistribute it and/or
  8. * modify it under the terms of the GNU Affero General Public License
  9. * (GNU AGPL) as published by the Free Software Foundation, either version 3
  10. * of the License, or (at your option) any later version.
  11. *
  12. * The code in this file is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
  15. * General Public License for more details.
  16. *
  17. * As additional permission under GNU AGPL version 3 section 7, you may
  18. * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
  19. * AGPL normally required by section 4, provided you include this license
  20. * notice and a URL through which recipients can access the Corresponding
  21. * Source.
  22. */
  23. this.mediasAltMinifier = this.mediasAltMinifier || (() => {
  24. const MEDIA_ALL = "all";
  25. const MEDIA_SCREEN = "screen";
  26. let cssTree, mediaQueryParser;
  27. return {
  28. getInstance(...args) {
  29. [cssTree, mediaQueryParser] = args;
  30. return {
  31. process: stylesheets => {
  32. const stats = { processed: 0, discarded: 0 };
  33. stylesheets.forEach((stylesheetInfo, element) => {
  34. if (matchesMediaType(stylesheetInfo.mediaText || MEDIA_ALL, MEDIA_SCREEN) && stylesheetInfo.stylesheet.children) {
  35. const removedRules = processRules(stylesheetInfo.stylesheet.children, stats);
  36. removedRules.forEach(({ cssRules, cssRule }) => cssRules.remove(cssRule));
  37. } else {
  38. stylesheets.delete(element);
  39. }
  40. });
  41. return stats;
  42. }
  43. };
  44. }
  45. };
  46. function processRules(cssRules, stats, removedRules = []) {
  47. for (let cssRule = cssRules.head; cssRule; cssRule = cssRule.next) {
  48. const ruleData = cssRule.data;
  49. if (ruleData.type == "Atrule" && ruleData.name == "media" && ruleData.block && ruleData.block.children && ruleData.prelude && ruleData.prelude.children) {
  50. stats.processed++;
  51. if (matchesMediaType(cssTree.generate(ruleData.prelude), MEDIA_SCREEN)) {
  52. processRules(ruleData.block.children, stats, removedRules);
  53. } else {
  54. removedRules.push({ cssRules, cssRule });
  55. stats.discarded++;
  56. }
  57. }
  58. }
  59. return removedRules;
  60. }
  61. function flatten(array) {
  62. return array.reduce((a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []);
  63. }
  64. function matchesMediaType(mediaText, mediaType) {
  65. const foundMediaTypes = flatten(mediaQueryParser.parseMediaList(mediaText).map(node => getMediaTypes(node, mediaType)));
  66. return foundMediaTypes.find(mediaTypeInfo => (!mediaTypeInfo.not && (mediaTypeInfo.value == mediaType || mediaTypeInfo.value == MEDIA_ALL))
  67. || (mediaTypeInfo.not && (mediaTypeInfo.value == MEDIA_ALL || mediaTypeInfo.value != mediaType)));
  68. }
  69. function getMediaTypes(parentNode, mediaType, mediaTypes = []) {
  70. parentNode.nodes.map((node, indexNode) => {
  71. if (node.type == "media-query") {
  72. return getMediaTypes(node, mediaType, mediaTypes);
  73. } else {
  74. if (node.type == "media-type") {
  75. const nodeMediaType = { not: Boolean(indexNode && parentNode.nodes[0].type == "keyword" && parentNode.nodes[0].value == "not"), value: node.value };
  76. if (!mediaTypes.find(mediaType => nodeMediaType.not == mediaType.not && nodeMediaType.value == mediaType.value)) {
  77. mediaTypes.push(nodeMediaType);
  78. }
  79. }
  80. }
  81. });
  82. if (!mediaTypes.length) {
  83. mediaTypes.push({ not: false, value: MEDIA_ALL });
  84. }
  85. return mediaTypes;
  86. }
  87. })();