Skip to content

Commit a287a2a

Browse files
authored
refactor(mergePaths): improve performance (#1904)
1 parent 52961ba commit a287a2a

File tree

1 file changed

+40
-24
lines changed

1 file changed

+40
-24
lines changed

plugins/mergePaths.js

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
'use strict';
22

3-
const { detachNodeFromParent } = require('../lib/xast.js');
3+
/**
4+
* @typedef {import('../lib/types').XastChild} XastChild
5+
*/
6+
47
const { collectStylesheet, computeStyle } = require('../lib/style.js');
58
const { path2js, js2path, intersects } = require('./_path.js');
69

@@ -25,12 +28,18 @@ exports.fn = (root, params) => {
2528
return {
2629
element: {
2730
enter: (node) => {
28-
let prevChild = null;
31+
if (node.children.length <= 1) {
32+
return;
33+
}
34+
35+
/** @type {XastChild[]} */
36+
const elementsToRemove = [];
37+
let prevChild = node.children[0];
38+
39+
for (let i = 1; i < node.children.length; i++) {
40+
const child = node.children[i];
2941

30-
for (const child of node.children) {
31-
// skip if previous element is not path or contains animation elements
3242
if (
33-
prevChild == null ||
3443
prevChild.type !== 'element' ||
3544
prevChild.name !== 'path' ||
3645
prevChild.children.length !== 0 ||
@@ -40,7 +49,6 @@ exports.fn = (root, params) => {
4049
continue;
4150
}
4251

43-
// skip if element is not path or contains animation elements
4452
if (
4553
child.type !== 'element' ||
4654
child.name !== 'path' ||
@@ -51,7 +59,6 @@ exports.fn = (root, params) => {
5159
continue;
5260
}
5361

54-
// preserve paths with markers
5562
const computedStyle = computeStyle(stylesheet, child);
5663
if (
5764
computedStyle['marker-start'] ||
@@ -62,36 +69,45 @@ exports.fn = (root, params) => {
6269
continue;
6370
}
6471

65-
const prevChildAttrs = Object.keys(prevChild.attributes);
6672
const childAttrs = Object.keys(child.attributes);
67-
let attributesAreEqual = prevChildAttrs.length === childAttrs.length;
68-
for (const name of childAttrs) {
69-
if (name !== 'd') {
70-
if (
71-
prevChild.attributes[name] == null ||
72-
prevChild.attributes[name] !== child.attributes[name]
73-
) {
74-
attributesAreEqual = false;
75-
}
76-
}
73+
if (childAttrs.length !== Object.keys(prevChild.attributes).length) {
74+
prevChild = child;
75+
continue;
76+
}
77+
78+
const areAttrsEqual = childAttrs.some((attr) => {
79+
return (
80+
attr !== 'd' &&
81+
prevChild.type === 'element' &&
82+
prevChild.attributes[attr] !== child.attributes[attr]
83+
);
84+
});
85+
86+
if (areAttrsEqual) {
87+
prevChild = child;
88+
continue;
7789
}
90+
7891
const prevPathJS = path2js(prevChild);
7992
const curPathJS = path2js(child);
8093

81-
if (
82-
attributesAreEqual &&
83-
(force || !intersects(prevPathJS, curPathJS))
84-
) {
85-
js2path(prevChild, prevPathJS.concat(curPathJS), {
94+
if (force || !intersects(prevPathJS, curPathJS)) {
95+
prevPathJS.push(...curPathJS);
96+
js2path(prevChild, prevPathJS, {
8697
floatPrecision,
8798
noSpaceAfterFlags,
8899
});
89-
detachNodeFromParent(child, node);
100+
101+
elementsToRemove.push(child);
90102
continue;
91103
}
92104

93105
prevChild = child;
94106
}
107+
108+
node.children = node.children.filter(
109+
(child) => !elementsToRemove.includes(child),
110+
);
95111
},
96112
},
97113
};

0 commit comments

Comments
 (0)