1
0
mirror of https://github.com/svg/svgo.git synced 2025-07-31 07:44:22 +03:00

Add descendants test and universal selector support

This commit is contained in:
Bogdan Chadkin
2021-08-23 19:24:04 +03:00
parent 5bf41deb3c
commit c9ecc384c7
2 changed files with 36 additions and 13 deletions

View File

@ -30,7 +30,7 @@ const trimQuotes = (string) => {
*/ */
const elementMatches = (csstreeNode, xastElement) => { const elementMatches = (csstreeNode, xastElement) => {
if (csstreeNode.type === 'TypeSelector') { if (csstreeNode.type === 'TypeSelector') {
return csstreeNode.name === xastElement.name; return csstreeNode.name === '*' || csstreeNode.name === xastElement.name;
} }
if (csstreeNode.type === 'IdSelector') { if (csstreeNode.type === 'IdSelector') {
return csstreeNode.name === xastElement.attributes.id; return csstreeNode.name === xastElement.attributes.id;
@ -90,7 +90,7 @@ const elementMatches = (csstreeNode, xastElement) => {
/** /**
* @type {( * @type {(
* startNode: XastParent, * startNode: XastParent,
* descendants: Array<XastElement>, * descendants: Set<XastElement>,
* parents: WeakMap<XastElement, XastParent> * parents: WeakMap<XastElement, XastParent>
* ) => void} * ) => void}
*/ */
@ -98,7 +98,7 @@ const collectDescendantElements = (startNode, descendants, parents) => {
for (const childNode of startNode.children) { for (const childNode of startNode.children) {
if (childNode.type === 'element') { if (childNode.type === 'element') {
parents.set(childNode, startNode); parents.set(childNode, startNode);
descendants.push(childNode); descendants.add(childNode);
collectDescendantElements(childNode, descendants, parents); collectDescendantElements(childNode, descendants, parents);
} }
} }
@ -107,7 +107,7 @@ const collectDescendantElements = (startNode, descendants, parents) => {
/** /**
* @type {( * @type {(
* startNodes: Array<XastElement>, * startNodes: Array<XastElement>,
* children: Array<XastElement>, * children: Set<XastElement>,
* parents: WeakMap<XastElement, XastParent> * parents: WeakMap<XastElement, XastParent>
* ) => void} * ) => void}
*/ */
@ -116,7 +116,7 @@ const collectChildrenElements = (startNodes, children, parents) => {
for (const child of startNode.children) { for (const child of startNode.children) {
if (child.type === 'element') { if (child.type === 'element') {
parents.set(child, startNode); parents.set(child, startNode);
children.push(child); children.add(child);
} }
} }
} }
@ -125,7 +125,7 @@ const collectChildrenElements = (startNodes, children, parents) => {
/** /**
* @type {( * @type {(
* startNodes: Array<XastElement>, * startNodes: Array<XastElement>,
* siblings: Array<XastElement>, * siblings: Set<XastElement>,
* parents: WeakMap<XastElement, XastParent> * parents: WeakMap<XastElement, XastParent>
* ) => void} * ) => void}
*/ */
@ -139,7 +139,7 @@ const collectAdjacentSiblings = (startNodes, siblings, parents) => {
for (const adjacentSibling of adjacentSiblings) { for (const adjacentSibling of adjacentSiblings) {
if (adjacentSibling.type === 'element') { if (adjacentSibling.type === 'element') {
parents.set(adjacentSibling, parentNode); parents.set(adjacentSibling, parentNode);
siblings.push(adjacentSibling); siblings.add(adjacentSibling);
} }
} }
} }
@ -149,7 +149,7 @@ const collectAdjacentSiblings = (startNodes, siblings, parents) => {
/** /**
* @type {( * @type {(
* startNodes: Array<XastElement>, * startNodes: Array<XastElement>,
* siblings: Array<XastElement>, * siblings: Set<XastElement>,
* parents: WeakMap<XastElement, XastParent> * parents: WeakMap<XastElement, XastParent>
* ) => void} * ) => void}
*/ */
@ -163,7 +163,7 @@ const collectGeneralSiblings = (startNodes, siblings, parents) => {
for (const generalSibling of generalSiblings) { for (const generalSibling of generalSiblings) {
if (generalSibling.type === 'element') { if (generalSibling.type === 'element') {
parents.set(generalSibling, parentNode); parents.set(generalSibling, parentNode);
siblings.push(generalSibling); siblings.add(generalSibling);
} }
} }
} }
@ -175,9 +175,9 @@ const collectGeneralSiblings = (startNodes, siblings, parents) => {
*/ */
const combination = (csstreeNodes, xastNode) => { const combination = (csstreeNodes, xastNode) => {
/** /**
* @type {Array<XastElement>} * @type {Set<XastElement>}
*/ */
let candidateNodes = []; let candidateNodes = new Set();
/** /**
* @type {WeakMap<XastElement, XastParent>} * @type {WeakMap<XastElement, XastParent>}
*/ */
@ -189,12 +189,12 @@ const combination = (csstreeNodes, xastNode) => {
let lastMatchedNodes = []; let lastMatchedNodes = [];
for (const csstreeChild of csstreeNodes) { for (const csstreeChild of csstreeNodes) {
if (csstreeChild.type === 'WhiteSpace') { if (csstreeChild.type === 'WhiteSpace') {
candidateNodes = new Set();
for (const node of lastMatchedNodes) { for (const node of lastMatchedNodes) {
candidateNodes = [];
collectDescendantElements(node, candidateNodes, candidateParents); collectDescendantElements(node, candidateNodes, candidateParents);
} }
} else if (csstreeChild.type === 'Combinator') { } else if (csstreeChild.type === 'Combinator') {
candidateNodes = []; candidateNodes = new Set();
if (csstreeChild.name === '>') { if (csstreeChild.name === '>') {
collectChildrenElements( collectChildrenElements(
lastMatchedNodes, lastMatchedNodes,

View File

@ -74,6 +74,15 @@ test('visit skips entering children if node is detached', () => {
expect(entered).toEqual(['g', 'ellipse']); expect(entered).toEqual(['g', 'ellipse']);
}); });
test('select by universal selector', () => {
const ast = root([x('g', null, [x('rect'), x('circle')])]);
expect(selectAll('*', ast)).toEqual([
x('g', null, [x('rect'), x('circle')]),
x('rect'),
x('circle'),
]);
});
test('select elements by tag', () => { test('select elements by tag', () => {
const ast = root([x('g', null, [x('rect')]), x('rect')]); const ast = root([x('g', null, [x('rect')]), x('rect')]);
expect(selectAll('rect', ast)).toEqual([x('rect'), x('rect')]); expect(selectAll('rect', ast)).toEqual([x('rect'), x('rect')]);
@ -172,6 +181,20 @@ test('select sibling and child elements', () => {
]); ]);
}); });
test('select descendants elements', () => {
const ast = root([
x('g', null, [
x('rect', { class: 'inside-g' }),
x('g', null, [x('rect', { class: 'inside-deeper-g' })]),
]),
x('rect', { class: 'inside-root' }),
]);
expect(selectAll('g rect', ast)).toEqual([
x('rect', { class: 'inside-g' }),
x('rect', { class: 'inside-deeper-g' }),
]);
});
test('select sibling and child elements', () => { test('select sibling and child elements', () => {
const ast = root([ const ast = root([
x('g', null, [ x('g', null, [