Commit 13d99e49 authored by David Sveningsson's avatar David Sveningsson Committed by David Sveningsson

feat(dom): new api for caching data on `DOMNode`

parent 3d60ed2b
/* eslint-disable-next-line @typescript-eslint/no-empty-interface */
export interface DOMNodeCache {}
......@@ -162,4 +162,73 @@ describe("DOMNode", () => {
expect(node.generateSelector()).toBeNull();
});
});
describe("cache", () => {
it("cacheGet() should return undefined when no value is cached", () => {
expect.assertions(1);
const a = new DOMNode(NodeType.ELEMENT_NODE, "a");
expect(a.cacheGet("foo")).toBeUndefined();
});
it("cacheGet() should get value set with cacheSet()", () => {
expect.assertions(1);
const a = new DOMNode(NodeType.ELEMENT_NODE, "a");
a.cacheSet("foo", 1);
expect(a.cacheGet("foo")).toEqual(1);
});
it("cacheSet() should return value", () => {
expect.assertions(1);
const a = new DOMNode(NodeType.ELEMENT_NODE, "a");
expect(a.cacheSet("foo", 1)).toEqual(1);
});
it("cacheSet() should overwrite previous value", () => {
expect.assertions(1);
const a = new DOMNode(NodeType.ELEMENT_NODE, "a");
a.cacheSet("foo", 1);
a.cacheSet("foo", 2);
expect(a.cacheGet("foo")).toEqual(2);
});
it("should cache values per instance", () => {
expect.assertions(4);
const a = new DOMNode(NodeType.ELEMENT_NODE, "a");
const b = new DOMNode(NodeType.ELEMENT_NODE, "a");
expect(a.cacheGet("foo")).toBeUndefined();
expect(b.cacheGet("foo")).toBeUndefined();
a.cacheSet("foo", 1);
b.cacheSet("foo", 2);
expect(a.cacheGet("foo")).toEqual(1);
expect(b.cacheGet("foo")).toEqual(2);
});
it("cacheRemove() should remove value from cache", () => {
expect.assertions(4);
const a = new DOMNode(NodeType.ELEMENT_NODE, "a");
a.cacheSet("foo", 1);
expect(a.cacheExists("foo")).toBeTruthy();
expect(a.cacheGet("foo")).toEqual(1);
a.cacheRemove("foo");
expect(a.cacheExists("foo")).toBeFalsy();
expect(a.cacheGet("foo")).toBeUndefined();
});
it("cacheRemove() should return true if value existed", () => {
expect.assertions(3);
const a = new DOMNode(NodeType.ELEMENT_NODE, "a");
a.cacheSet("foo", 1);
expect(a.cacheRemove("foo")).toBeTruthy();
expect(a.cacheRemove("foo")).toBeFalsy();
expect(a.cacheRemove("bar")).toBeFalsy();
});
it("cacheExists() should return true if value is cached", () => {
expect.assertions(2);
const a = new DOMNode(NodeType.ELEMENT_NODE, "a");
a.cacheSet("foo", 1);
expect(a.cacheExists("foo")).toBeTruthy();
expect(a.cacheExists("bar")).toBeFalsy();
});
});
});
import { Location } from "../context";
import { NodeType } from "./nodetype";
import { DOMNodeCache } from "./cache";
export type DOMInternalID = number;
const DOCUMENT_NODE_NAME = "#document";
const TEXT_CONTENT = Symbol("textContent");
declare module "./cache" {
export interface DOMNodeCache {
[TEXT_CONTENT]: string;
}
}
let counter = 0;
......@@ -20,6 +28,8 @@ export class DOMNode {
public readonly location: Location;
public readonly unique: DOMInternalID;
private readonly cache: Map<string | number | symbol, any>;
/**
* Set of disabled rules for this node.
*
......@@ -42,13 +52,61 @@ export class DOMNode {
this.disabledRules = new Set();
this.childNodes = [];
this.unique = counter++;
this.cache = new Map();
}
/**
* Fetch cached value from this DOM node.
*
* @returns value or `undefined` if the value doesn't exist.
*/
public cacheGet<K extends keyof DOMNodeCache>(key: K): DOMNodeCache[K] | undefined;
public cacheGet(key: string | number | symbol): any | undefined;
public cacheGet(key: string | number | symbol): any | undefined {
return this.cache.get(key);
}
/**
* Store a value in cache.
*
* @returns the value itself is returned.
*/
public cacheSet<K extends keyof DOMNodeCache>(key: K, value: DOMNodeCache[K]): DOMNodeCache[K];
public cacheSet<T>(key: string | number | symbol, value: T): T;
public cacheSet<T>(key: string | number | symbol, value: T): T {
this.cache.set(key, value);
return value;
}
/**
* Remove a value by key from cache.
*/
public cacheRemove<K extends keyof DOMNodeCache>(key: K): boolean;
public cacheRemove(key: string | number | symbol): boolean;
public cacheRemove(key: string | number | symbol): boolean {
return this.cache.delete(key);
}
/**
* Check if key exists in cache.
*/
public cacheExists<K extends keyof DOMNodeCache>(key: K): boolean;
public cacheExists(key: string | number | symbol): boolean;
public cacheExists(key: string | number | symbol): boolean {
return this.cache.has(key);
}
/**
* Get the text (recursive) from all child nodes.
*/
public get textContent(): string {
return this.childNodes.map((node) => node.textContent).join("");
const cached = this.cacheGet(TEXT_CONTENT);
if (cached) {
return cached;
}
const text = this.childNodes.map((node) => node.textContent).join("");
this.cacheSet(TEXT_CONTENT, text);
return text;
}
public append(node: DOMNode): void {
......
......@@ -6,3 +6,4 @@ export { DOMTree } from "./domtree";
export { DynamicValue } from "./dynamic-value";
export { NodeType } from "./nodetype";
export { TextNode } from "./text";
export { DOMNodeCache } from "./cache";
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment