Skip to content

Commit 92ac4db

Browse files
committed
Simplify cache
1 parent 4b86654 commit 92ac4db

File tree

3 files changed

+29
-144
lines changed

3 files changed

+29
-144
lines changed

lib/parsers.js

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const {
66
} = require("@asamuzakjp/css-color");
77
const { next: syntaxes } = require("@csstools/css-syntax-patches-for-csstree");
88
const csstree = require("css-tree");
9-
const { createCacheKey, getCache, setCache } = require("./utils/cache");
9+
const { getCache, setCache } = require("./utils/cache");
1010
const { asciiLowercase } = require("./utils/strings");
1111

1212
// CSS global keywords
@@ -255,12 +255,7 @@ const parsePropertyValue = (prop, val, opt = {}) => {
255255
try {
256256
let cacheKey = "";
257257
if (inArray) {
258-
cacheKey = createCacheKey({
259-
name: "parsePropertyValue",
260-
property: prop,
261-
value: val,
262-
caseSensitive
263-
});
258+
cacheKey = `parsePropertyValue_${prop}_${val}_${caseSensitive}`;
264259
const cachedValues = getCache(cacheKey);
265260
if (Array.isArray(cachedValues)) {
266261
return cachedValues;

lib/utils/cache.js

Lines changed: 7 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,132 +1,34 @@
1-
// Forked from https://github.com/asamuzaK/cssColor/blob/main/src/js/cache.ts
2-
// cssstyle calculates values using associated options.
3-
// Even for the same property, the calculated result will differ depending on the option values.
4-
// For example, if a keyData is `{ border-color: currentColor }` and the option for that keyData
5-
// is `{ currentColor: red }`, then if the keyData is the same `{ border-color: currentColor }`
6-
// but the option is `{ currentColor: green }`, the calculated result will be different.
7-
// For this reason, we combine keyData and options to increase the uniqueness of cache keywords.
8-
91
"use strict";
102

113
const { LRUCache } = require("lru-cache");
124

13-
class CacheItem {
14-
#isNull;
15-
#item;
16-
17-
constructor(item, isNull = false) {
18-
this.#item = item;
19-
this.#isNull = Boolean(isNull);
20-
}
21-
22-
get item() {
23-
return this.#item;
24-
}
25-
26-
get isNull() {
27-
return this.#isNull;
28-
}
29-
}
30-
31-
// The lru-cache requires values to be non-null, so we provide a NullObject for null values.
32-
class NullObject extends CacheItem {
33-
constructor() {
34-
super(Symbol("null"), true);
35-
}
36-
}
37-
38-
// lru-cache instance.
5+
// The lru-cache instance.
396
const lruCache = new LRUCache({
407
max: 4096
418
});
429

10+
// The lru-cache requires non-null values, so substitute in this sentinel when we are given one.
11+
const nullSentinel = Symbol("null");
12+
4313
/**
4414
* @param key - Cache key.
4515
* @param value - Value to cache.
4616
* @returns void.
4717
*/
4818
const setCache = (key, value) => {
49-
if (key) {
50-
if (value === null) {
51-
lruCache.set(key, new NullObject());
52-
} else if (value instanceof CacheItem) {
53-
lruCache.set(key, value);
54-
} else {
55-
lruCache.set(key, new CacheItem(value));
56-
}
57-
}
19+
lruCache.set(key, value === null ? nullSentinel : value);
5820
};
5921

6022
/**
6123
* @param key - Cache key.
6224
* @returns Cached item or false otherwise.
6325
*/
6426
const getCache = (key) => {
65-
if (key && lruCache.has(key)) {
66-
const cachedItem = lruCache.get(key);
67-
if (cachedItem instanceof CacheItem) {
68-
if (cachedItem.isNull) {
69-
return null;
70-
}
71-
return cachedItem.item;
72-
}
73-
// Delete unexpected cached item.
74-
lruCache.delete(key);
75-
return false;
76-
}
77-
return false;
78-
};
79-
80-
/**
81-
* @param value - CSS value.
82-
* @returns Stringified value in JSON notation.
83-
*/
84-
const valueToJsonString = (value) => {
85-
if (typeof value === "undefined") {
86-
return "";
87-
}
88-
const res = JSON.stringify(value, (_key, val) => {
89-
let replacedValue;
90-
if (typeof val === "undefined") {
91-
replacedValue = null;
92-
} else if (typeof val === "function") {
93-
replacedValue = val.name;
94-
} else if (val instanceof Map || val instanceof Set) {
95-
replacedValue = [...val];
96-
} else if (typeof val === "bigint") {
97-
replacedValue = val.toString();
98-
} else {
99-
replacedValue = val;
100-
}
101-
return replacedValue;
102-
});
103-
return res;
104-
};
105-
106-
/**
107-
* Create stringified cacheKey from keyData and options.
108-
* Note that if the options include a function, it will not be cached.
109-
* @param keyData - Key data object.
110-
* @param [opt] - Options.
111-
* @returns Cache key.
112-
*/
113-
const createCacheKey = (keyData, opt = {}) => {
114-
const { customProperty = {}, dimension = {} } = opt;
115-
let cacheKey = "";
116-
if (
117-
keyData &&
118-
Object.keys(keyData).length &&
119-
typeof customProperty.callback !== "function" &&
120-
typeof dimension.callback !== "function"
121-
) {
122-
keyData.opt = valueToJsonString(opt);
123-
cacheKey = valueToJsonString(keyData);
124-
}
125-
return cacheKey;
27+
const value = lruCache.get(key);
28+
return value === nullSentinel ? null : value;
12629
};
12730

12831
module.exports = {
129-
createCacheKey,
13032
getCache,
13133
setCache
13234
};

test/CSSStyleDeclaration.test.js

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,14 @@ const assert = require("node:assert/strict");
55
const { CSSStyleDeclaration } = require("../lib/CSSStyleDeclaration");
66

77
describe("CSSStyleDeclaration", () => {
8+
const window = {
9+
getComputedStyle: () => {},
10+
DOMException: globalThis.DOMException,
11+
TypeError: globalThis.TypeError
12+
};
13+
814
it("has methods", () => {
9-
const style = new CSSStyleDeclaration();
15+
const style = new CSSStyleDeclaration(window);
1016
assert.strictEqual(typeof style.item, "function");
1117
assert.strictEqual(typeof style.getPropertyValue, "function");
1218
assert.strictEqual(typeof style.setProperty, "function");
@@ -15,7 +21,7 @@ describe("CSSStyleDeclaration", () => {
1521
});
1622

1723
it("has attributes", () => {
18-
const style = new CSSStyleDeclaration();
24+
const style = new CSSStyleDeclaration(window);
1925
assert.ok(style.__lookupGetter__("cssText"));
2026
assert.ok(style.__lookupSetter__("cssText"));
2127
assert.ok(style.__lookupGetter__("length"));
@@ -24,9 +30,6 @@ describe("CSSStyleDeclaration", () => {
2430
});
2531

2632
it("sets internals for Element", () => {
27-
const window = {
28-
DOMException: globalThis.DOMException
29-
};
3033
const node = {
3134
nodeType: 1,
3235
style: {},
@@ -47,9 +50,6 @@ describe("CSSStyleDeclaration", () => {
4750
});
4851

4952
it("sets internals for CSSRule", () => {
50-
const window = {
51-
DOMException: globalThis.DOMException
52-
};
5353
const rule = {
5454
parentRule: {},
5555
parentStyleSheet: {
@@ -67,7 +67,7 @@ describe("CSSStyleDeclaration", () => {
6767
});
6868

6969
it("has format in internal options", () => {
70-
const style = new CSSStyleDeclaration(null, {
70+
const style = new CSSStyleDeclaration(window, {
7171
foo: "bar"
7272
});
7373
assert.deepEqual(style._options, {
@@ -77,7 +77,7 @@ describe("CSSStyleDeclaration", () => {
7777
});
7878

7979
it("should not override format if exists", () => {
80-
const style = new CSSStyleDeclaration(null, {
80+
const style = new CSSStyleDeclaration(window, {
8181
format: "computedValue"
8282
});
8383
assert.deepEqual(style._options, {
@@ -86,10 +86,6 @@ describe("CSSStyleDeclaration", () => {
8686
});
8787

8888
it("getting cssText returns empty string if computed flag is set", () => {
89-
const window = {
90-
getComputedStyle: () => {},
91-
DOMException: globalThis.DOMException
92-
};
9389
const style = new CSSStyleDeclaration(window, {
9490
format: "computedValue"
9591
});
@@ -98,79 +94,75 @@ describe("CSSStyleDeclaration", () => {
9894
});
9995

10096
it("setting improper css to cssText should not throw", () => {
101-
const style = new CSSStyleDeclaration();
97+
const style = new CSSStyleDeclaration(window);
10298
style.cssText = "color: ";
10399
assert.strictEqual(style.cssText, "");
104100
style.cssText = "color: red!";
105101
assert.strictEqual(style.cssText, "");
106102
});
107103

108104
it("item() throws if argument is not given", () => {
109-
const style = new CSSStyleDeclaration();
105+
const style = new CSSStyleDeclaration(window);
110106
assert.throws(
111107
() => {
112108
style.item();
113109
},
114110
(e) => {
115-
assert.strictEqual(e instanceof globalThis.TypeError, true);
111+
assert.strictEqual(e instanceof window.TypeError, true);
116112
assert.strictEqual(e.message, "1 argument required, but only 0 present.");
117113
return true;
118114
}
119115
);
120116
});
121117

122118
it("camelcase properties are not assigned with setproperty()", () => {
123-
const style = new CSSStyleDeclaration();
119+
const style = new CSSStyleDeclaration(window);
124120
style.setProperty("fontSize", "12px");
125121
assert.strictEqual(style.cssText, "");
126122
});
127123

128124
it("custom properties are case-sensitive", () => {
129-
const style = new CSSStyleDeclaration();
125+
const style = new CSSStyleDeclaration(window);
130126
style.cssText = "--fOo: purple";
131127

132128
assert.strictEqual(style.getPropertyValue("--foo"), "");
133129
assert.strictEqual(style.getPropertyValue("--fOo"), "purple");
134130
});
135131

136132
it("getPropertyValue for custom properties in cssText", () => {
137-
const style = new CSSStyleDeclaration();
133+
const style = new CSSStyleDeclaration(window);
138134
style.cssText = "--foo: red";
139135

140136
assert.strictEqual(style.getPropertyValue("--foo"), "red");
141137
});
142138

143139
it("getPropertyValue for custom properties with setProperty", () => {
144-
const style = new CSSStyleDeclaration();
140+
const style = new CSSStyleDeclaration(window);
145141
style.setProperty("--bar", "blue");
146142

147143
assert.strictEqual(style.getPropertyValue("--bar"), "blue");
148144
});
149145

150146
it("getPropertyValue for custom properties with object setter", () => {
151-
const style = new CSSStyleDeclaration();
147+
const style = new CSSStyleDeclaration(window);
152148
style["--baz"] = "yellow";
153149

154150
assert.strictEqual(style.getPropertyValue("--baz"), "");
155151
});
156152

157153
it("getPropertyPriority for property", () => {
158-
const style = new CSSStyleDeclaration();
154+
const style = new CSSStyleDeclaration(window);
159155
style.setProperty("color", "green", "important");
160156
assert.strictEqual(style.getPropertyPriority("color"), "important");
161157
});
162158

163159
it("getPropertyPriority for custom property", () => {
164-
const style = new CSSStyleDeclaration();
160+
const style = new CSSStyleDeclaration(window);
165161
style.setProperty("--foo", "green", "important");
166162
assert.strictEqual(style.getPropertyPriority("--foo"), "important");
167163
});
168164

169165
it("setProperty throws if read-only flag is set", () => {
170-
const window = {
171-
getComputedStyle: () => {},
172-
DOMException: globalThis.DOMException
173-
};
174166
const style = new CSSStyleDeclaration(window);
175167
style.setProperty("--foo", "green");
176168
style.setOptions({
@@ -190,10 +182,6 @@ describe("CSSStyleDeclaration", () => {
190182
});
191183

192184
it("removeProperty throws if read-only flag is set", () => {
193-
const window = {
194-
getComputedStyle: () => {},
195-
DOMException: globalThis.DOMException
196-
};
197185
const style = new CSSStyleDeclaration(window);
198186
style.setProperty("--foo", "green");
199187
style.setProperty("--bar", "red");

0 commit comments

Comments
 (0)