You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

93 lines
2.3 KiB

3 months ago
  1. 'use strict';
  2. /**
  3. * @typedef {import('../lib/types').XastElement} XastElement
  4. */
  5. const { visitSkip, detachNodeFromParent } = require('../lib/xast.js');
  6. const JSAPI = require('../lib/svgo/jsAPI.js');
  7. exports.name = 'mergeStyles';
  8. exports.type = 'visitor';
  9. exports.active = true;
  10. exports.description = 'merge multiple style elements into one';
  11. /**
  12. * Merge multiple style elements into one.
  13. *
  14. * @author strarsis <strarsis@gmail.com>
  15. *
  16. * @type {import('../lib/types').Plugin<void>}
  17. */
  18. exports.fn = () => {
  19. /**
  20. * @type {null | XastElement}
  21. */
  22. let firstStyleElement = null;
  23. let collectedStyles = '';
  24. let styleContentType = 'text';
  25. return {
  26. element: {
  27. enter: (node, parentNode) => {
  28. // skip <foreignObject> content
  29. if (node.name === 'foreignObject') {
  30. return visitSkip;
  31. }
  32. // collect style elements
  33. if (node.name !== 'style') {
  34. return;
  35. }
  36. // skip <style> with invalid type attribute
  37. if (
  38. node.attributes.type != null &&
  39. node.attributes.type !== '' &&
  40. node.attributes.type !== 'text/css'
  41. ) {
  42. return;
  43. }
  44. // extract style element content
  45. let css = '';
  46. for (const child of node.children) {
  47. if (child.type === 'text') {
  48. css += child.value;
  49. }
  50. if (child.type === 'cdata') {
  51. styleContentType = 'cdata';
  52. css += child.value;
  53. }
  54. }
  55. // remove empty style elements
  56. if (css.trim().length === 0) {
  57. detachNodeFromParent(node, parentNode);
  58. return;
  59. }
  60. // collect css and wrap with media query if present in attribute
  61. if (node.attributes.media == null) {
  62. collectedStyles += css;
  63. } else {
  64. collectedStyles += `@media ${node.attributes.media}{${css}}`;
  65. delete node.attributes.media;
  66. }
  67. // combine collected styles in the first style element
  68. if (firstStyleElement == null) {
  69. firstStyleElement = node;
  70. } else {
  71. detachNodeFromParent(node, parentNode);
  72. firstStyleElement.children = [
  73. new JSAPI(
  74. { type: styleContentType, value: collectedStyles },
  75. firstStyleElement
  76. ),
  77. ];
  78. }
  79. },
  80. },
  81. };
  82. };