match - Add css match functions with basic test cases

This commit is contained in:
2019-09-20 11:27:55 +05:30
parent 051bb1f543
commit c47a4dddbd
3 changed files with 180 additions and 15 deletions
+4
View File
@@ -4,6 +4,7 @@ import {
findNodeByComponentRef,
findNodeByComponentName
} from "./findNode";
import { matchGenerator, matchAll, matchFirst } from "./match";
import * as Utils from "./utils";
export {
@@ -12,5 +13,8 @@ export {
findNodeByComponent,
findNodeByComponentRef,
findNodeByComponentName,
matchGenerator,
matchAll,
matchFirst,
Utils
};
+78 -15
View File
@@ -1,20 +1,22 @@
import CSSwhat from "css-what";
import * as CSSWhat from "css-what";
import { traverseGenerator } from "./traverse";
import { FiberNode } from "./mocked-types";
import { isNodeNotHtmlLike } from "./utils";
function* matchGenerator(
node: FiberNode,
match: string | CSSwhat.Selector[][]
match: string | CSSWhat.Selector[][]
): IterableIterator<FiberNode> {
// Either parse match string or allow parsed match object as it is
let matchParsed: CSSwhat.Selector[][];
let matchParsed: CSSWhat.Selector[][];
if (typeof match === "string") {
matchParsed = CSSwhat.parse(match);
matchParsed = CSSWhat.parse(match, {
lowerCaseTags: false,
lowerCaseAttributeNames: false
});
} else {
matchParsed = match;
}
// If selector is a combination of multiple basic selectors (a, b),
// pass them separately to matchGenerator and combine their results one after another
const selectorsCount = matchParsed.length;
@@ -35,20 +37,46 @@ function* matchGenerator(
let currentMatchingNodes: FiberNode[] = [node];
let currentMatchingSelectorPartIndex = 0;
let currentMatchingSelectorPart: CSSwhat.Selector =
parsedSelector[currentMatchingSelectorPartIndex];
let lastRelationshipSelectorPart: CSSwhat.Selector | undefined = undefined;
let lastRelationshipSelectorPart: CSSWhat.Selector | undefined = undefined;
while (currentMatchingSelectorPartIndex < parsedSelector.length) {
const nextMatchingNodes: FiberNode[] = [];
for (const currentNode of currentMatchingNodes) {
const currentMatchingSelectorPart: CSSWhat.Selector =
parsedSelector[currentMatchingSelectorPartIndex];
if (["tag"].includes(currentMatchingSelectorPart.type)) {
const traverseIterator = traverseGenerator(currentNode);
const startParams = {
skipSelfForStartNode: true,
skipSiblingForStartNode: true
};
let nextParams: {
skipChild?: boolean;
skipSibling?: boolean;
} = {};
if (
lastRelationshipSelectorPart === undefined ||
lastRelationshipSelectorPart.type === "descendant"
) {
nextParams = { skipChild: false, skipSibling: false };
} else if (lastRelationshipSelectorPart.type === "child") {
// visit only first level of child
nextParams = { skipChild: true };
} else if (lastRelationshipSelectorPart.type === "sibling") {
// visit only siblings of start node
nextParams = { skipChild: true, skipSibling: true };
startParams.skipSiblingForStartNode = false;
}
const traverseIterator = traverseGenerator(currentNode, startParams);
// Handle supported non-traversal parts here
if (currentMatchingSelectorPart.type == "tag") {
for (const tmpNode of traverseIterator) {
let tmpNode: FiberNode;
while (
!({ value: tmpNode } = traverseIterator.next(nextParams)).done
) {
if (
isNodeNotHtmlLike(tmpNode) &&
tmpNode.type.name === currentMatchingSelectorPart.name
@@ -57,11 +85,15 @@ function* matchGenerator(
}
}
}
traverseIterator.throw &&
traverseIterator.throw(new Error("cleanup"));
} else if (["descendant"].includes(currentMatchingSelectorPart.type)) {
// Handle traversal parts here
// traverseIterator.throw &&
// traverseIterator.throw(new Error("cleanup"));
} else if (
["descendant", "child"].includes(currentMatchingSelectorPart.type)
) {
// Handle traversal parts here - Save for look back in next part
lastRelationshipSelectorPart = currentMatchingSelectorPart;
// Preserve currentMatchingNodes
nextMatchingNodes.push(...currentMatchingNodes);
} else {
// For unhandled parts
lastRelationshipSelectorPart = undefined;
@@ -69,10 +101,41 @@ function* matchGenerator(
}
currentMatchingNodes = nextMatchingNodes;
currentMatchingSelectorPartIndex += 1;
}
for (const tmpNode of currentMatchingNodes) {
yield tmpNode;
}
return;
}
}
export { matchGenerator };
function matchAll(
node: FiberNode,
match: string | CSSWhat.Selector[][]
): Array<FiberNode> {
return [...matchGenerator(node, match)];
}
function matchFirst(
node: FiberNode,
match: string | CSSWhat.Selector[][]
): FiberNode | null {
const matchIterator = matchGenerator(node, match);
const firstResult = matchIterator.next();
// Cancel generator
matchIterator.throw && matchIterator.throw(new Error("Cleanup"));
// If match found, return that
if (!firstResult.done) {
return firstResult.value;
}
// Else return null
return null;
}
export { matchGenerator, matchAll, matchFirst };