Skip to content

Commit

Permalink
fix: update memo interface
Browse files Browse the repository at this point in the history
  • Loading branch information
fczbkk committed Nov 6, 2022
1 parent e516ccc commit f518f7f
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 56 deletions.
37 changes: 23 additions & 14 deletions src/memo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,43 @@ import {
CssSelectorType,
CssSelectorTypes,
} from "./types";
import { SELECTOR_TYPE_GETTERS } from "./utilities-selectors.js";
import {
sanitizeSelectorNeedle,
SELECTOR_TYPE_GETTERS,
} from "./utilities-selectors.js";
import { getIntersection } from "./utilities-data.js";

export type MemoSelectorData = Map<CssSelectorType, CssSelectors>;
export type MemoElementData = Map<Element[], MemoSelectorData>;
export type MemoElementData = Map<Element, MemoSelectorData>;
export type MemoizedSelectorGetter = (
elements: Element[],
needle: Element | Element[],
selectorTypes: CssSelectorTypes
) => CssSelectorsByType;

/**
* Returns memoized data for element, creates new record in memo if necessary.
*/
function getElementData(
elements: Element[],
element: Element,
memo: MemoElementData = new Map()
): MemoSelectorData {
if (!memo.get(elements)) {
memo.set(elements, new Map());
if (!memo.get(element)) {
memo.set(element, new Map());
}
return memo.get(elements);
return memo.get(element);
}

/**
* Returns selector data of given type for element. Generates selector data if necessary.
*/
function getSelectorData(
elements: Element[],
element: Element,
selectorType: CssSelectorType,
memo: MemoElementData = new Map()
) {
const elementData = getElementData(elements, memo);
const elementData = getElementData(element, memo);
if (!elementData.get(selectorType)) {
elementData.set(selectorType, getSelectors(elements, selectorType));
elementData.set(selectorType, getSelectors(element, selectorType));
}
return elementData.get(selectorType);
}
Expand All @@ -45,10 +49,10 @@ function getSelectorData(
* Returns selector data of given type for element.
*/
function getSelectors(
elements: Element[],
element: Element,
selectorType: CssSelectorType
): CssSelectors {
return SELECTOR_TYPE_GETTERS[selectorType](elements);
return SELECTOR_TYPE_GETTERS[selectorType]([element]);
}

/**
Expand All @@ -58,12 +62,17 @@ export function createMemo(
memo: MemoElementData = new Map()
): MemoizedSelectorGetter {
return function (
elements: Element[],
needle: Element | Element[],
selectors: CssSelectorTypes
): CssSelectorsByType {
const result = {} as CssSelectorsByType;
const sanitizedNeedle = sanitizeSelectorNeedle(needle);
selectors.forEach((selectorType) => {
result[selectorType] = getSelectorData(elements, selectorType, memo);
result[selectorType] = getIntersection(
sanitizedNeedle.map((element) =>
getSelectorData(element, selectorType, memo)
)
);
});
return result;
};
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export enum CssSelectorType {

export type CssSelectorTypes = Array<CssSelectorType>;

export type CssSelectorsByType = Record<CssSelectorType, CssSelectors>;
export type CssSelectorsByType = Partial<Record<CssSelectorType, CssSelectors>>;

export type CssSelectorData = {
[key in CssSelectorType]?: Array<string> | Array<Array<string>>;
Expand Down
41 changes: 0 additions & 41 deletions test/memo.spec.js

This file was deleted.

61 changes: 61 additions & 0 deletions test/memo.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { assert } from "chai";
import { createMemo } from "../src/memo.js";
import { CssSelectorType } from "../src/types.js";

describe("Memo", () => {
let root: HTMLElement;

/**
* Simple way to retrieve target element for test.
* @returns {Element}
*/
function getTargetElement() {
return root.querySelector("[data-target]");
}

/**
* Simple way to retrieve multiple target elements for test.
* @returns {Element[]}
*/
function getTargetElements() {
return root.querySelectorAll("[data-target]");
}

beforeEach(() => {
root = document.body.appendChild(document.createElement("div"));
});

afterEach(() => {
root.parentNode.removeChild(root);
});

it("should retrieve selectors for an element", () => {
root.innerHTML = "<div data-target></div>";
const element = getTargetElement();
const getElementSelectors = createMemo();
const result = getElementSelectors(element, [CssSelectorType.tag]);
assert.deepEqual(result, { [CssSelectorType.tag]: ["div"] });
});

it("should retrieve common selectors for multiple elements", () => {
root.innerHTML = `
<div data-target class='aaa bbb'></div>
<div data-target class='aaa ccc'></div>
`;
const elements = [...getTargetElements()];
const getElementSelectors = createMemo();
const result = getElementSelectors(elements, [CssSelectorType.class]);
assert.deepEqual(result, { [CssSelectorType.class]: [".aaa"] });
});

it("should use remembered value", () => {
root.innerHTML = "<div data-target></div>";
const element = getTargetElement();
const memoData = new Map([
[element, new Map([[CssSelectorType.tag, ["mock_tag_selector"]]])],
]);
const getElementSelectors = createMemo(memoData);
const result = getElementSelectors(element, [CssSelectorType.tag]);
assert.deepEqual(result, { [CssSelectorType.tag]: ["mock_tag_selector"] });
});
});

0 comments on commit f518f7f

Please sign in to comment.