From c9ecc384c72e1f1bea1fef461d1d16b2d1f206f8 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Mon, 23 Aug 2021 19:24:04 +0300 Subject: [PATCH] Add descendants test and universal selector support --- lib/xast.js | 26 +++++++++++++------------- lib/xast.test.js | 23 +++++++++++++++++++++++ 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/lib/xast.js b/lib/xast.js index 8b6f3edd..adf09894 100644 --- a/lib/xast.js +++ b/lib/xast.js @@ -30,7 +30,7 @@ const trimQuotes = (string) => { */ const elementMatches = (csstreeNode, xastElement) => { if (csstreeNode.type === 'TypeSelector') { - return csstreeNode.name === xastElement.name; + return csstreeNode.name === '*' || csstreeNode.name === xastElement.name; } if (csstreeNode.type === 'IdSelector') { return csstreeNode.name === xastElement.attributes.id; @@ -90,7 +90,7 @@ const elementMatches = (csstreeNode, xastElement) => { /** * @type {( * startNode: XastParent, - * descendants: Array, + * descendants: Set, * parents: WeakMap * ) => void} */ @@ -98,7 +98,7 @@ const collectDescendantElements = (startNode, descendants, parents) => { for (const childNode of startNode.children) { if (childNode.type === 'element') { parents.set(childNode, startNode); - descendants.push(childNode); + descendants.add(childNode); collectDescendantElements(childNode, descendants, parents); } } @@ -107,7 +107,7 @@ const collectDescendantElements = (startNode, descendants, parents) => { /** * @type {( * startNodes: Array, - * children: Array, + * children: Set, * parents: WeakMap * ) => void} */ @@ -116,7 +116,7 @@ const collectChildrenElements = (startNodes, children, parents) => { for (const child of startNode.children) { if (child.type === 'element') { parents.set(child, startNode); - children.push(child); + children.add(child); } } } @@ -125,7 +125,7 @@ const collectChildrenElements = (startNodes, children, parents) => { /** * @type {( * startNodes: Array, - * siblings: Array, + * siblings: Set, * parents: WeakMap * ) => void} */ @@ -139,7 +139,7 @@ const collectAdjacentSiblings = (startNodes, siblings, parents) => { for (const adjacentSibling of adjacentSiblings) { if (adjacentSibling.type === 'element') { parents.set(adjacentSibling, parentNode); - siblings.push(adjacentSibling); + siblings.add(adjacentSibling); } } } @@ -149,7 +149,7 @@ const collectAdjacentSiblings = (startNodes, siblings, parents) => { /** * @type {( * startNodes: Array, - * siblings: Array, + * siblings: Set, * parents: WeakMap * ) => void} */ @@ -163,7 +163,7 @@ const collectGeneralSiblings = (startNodes, siblings, parents) => { for (const generalSibling of generalSiblings) { if (generalSibling.type === 'element') { parents.set(generalSibling, parentNode); - siblings.push(generalSibling); + siblings.add(generalSibling); } } } @@ -175,9 +175,9 @@ const collectGeneralSiblings = (startNodes, siblings, parents) => { */ const combination = (csstreeNodes, xastNode) => { /** - * @type {Array} + * @type {Set} */ - let candidateNodes = []; + let candidateNodes = new Set(); /** * @type {WeakMap} */ @@ -189,12 +189,12 @@ const combination = (csstreeNodes, xastNode) => { let lastMatchedNodes = []; for (const csstreeChild of csstreeNodes) { if (csstreeChild.type === 'WhiteSpace') { + candidateNodes = new Set(); for (const node of lastMatchedNodes) { - candidateNodes = []; collectDescendantElements(node, candidateNodes, candidateParents); } } else if (csstreeChild.type === 'Combinator') { - candidateNodes = []; + candidateNodes = new Set(); if (csstreeChild.name === '>') { collectChildrenElements( lastMatchedNodes, diff --git a/lib/xast.test.js b/lib/xast.test.js index 89be9b8a..62d8b669 100644 --- a/lib/xast.test.js +++ b/lib/xast.test.js @@ -74,6 +74,15 @@ test('visit skips entering children if node is detached', () => { 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', () => { const ast = root([x('g', null, [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', () => { const ast = root([ x('g', null, [