sortDefsChildren.js 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. 'use strict';
  2. exports.type = 'visitor';
  3. exports.name = 'sortDefsChildren';
  4. exports.active = true;
  5. exports.description = 'Sorts children of <defs> to improve compression';
  6. /**
  7. * Sorts children of defs in order to improve compression.
  8. * Sorted first by frequency then by element name length then by element name (to ensure grouping).
  9. *
  10. * @author David Leston
  11. *
  12. * @type {import('../lib/types').Plugin<void>}
  13. */
  14. exports.fn = () => {
  15. return {
  16. element: {
  17. enter: (node) => {
  18. if (node.name === 'defs') {
  19. /**
  20. * @type {Map<string, number>}
  21. */
  22. const frequencies = new Map();
  23. for (const child of node.children) {
  24. if (child.type === 'element') {
  25. const frequency = frequencies.get(child.name);
  26. if (frequency == null) {
  27. frequencies.set(child.name, 1);
  28. } else {
  29. frequencies.set(child.name, frequency + 1);
  30. }
  31. }
  32. }
  33. node.children.sort((a, b) => {
  34. if (a.type !== 'element' || b.type !== 'element') {
  35. return 0;
  36. }
  37. const aFrequency = frequencies.get(a.name);
  38. const bFrequency = frequencies.get(b.name);
  39. if (aFrequency != null && bFrequency != null) {
  40. const frequencyComparison = bFrequency - aFrequency;
  41. if (frequencyComparison !== 0) {
  42. return frequencyComparison;
  43. }
  44. }
  45. const lengthComparison = b.name.length - a.name.length;
  46. if (lengthComparison !== 0) {
  47. return lengthComparison;
  48. }
  49. if (a.name !== b.name) {
  50. return a.name > b.name ? -1 : 1;
  51. }
  52. return 0;
  53. });
  54. }
  55. },
  56. },
  57. };
  58. };