@asamuzakjp/dom-selector 8.1.1 → 8.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +14 -12
- package/src/index.js +52 -49
- package/src/js/constant.js +0 -1
- package/src/js/finder.js +1 -27
- package/src/js/selector.js +11 -14
- package/types/js/constant.d.ts +0 -1
package/package.json
CHANGED
|
@@ -58,24 +58,26 @@
|
|
|
58
58
|
"yargs": "^18.0.0"
|
|
59
59
|
},
|
|
60
60
|
"eslint": {
|
|
61
|
-
"brace-expansion": "^1.1.13"
|
|
61
|
+
"brace-expansion": "^1.1.13",
|
|
62
|
+
"js-yaml": "^4.1.2"
|
|
62
63
|
},
|
|
63
64
|
"jsdom": "$jsdom",
|
|
64
65
|
"mocha": {
|
|
65
|
-
"diff": "^8.0.3"
|
|
66
|
+
"diff": "^8.0.3",
|
|
67
|
+
"js-yaml": "^4.1.2"
|
|
66
68
|
},
|
|
67
69
|
"serialize-javascript": "^7.0.4"
|
|
68
70
|
},
|
|
69
71
|
"scripts": {
|
|
70
|
-
"bench": "node benchmark/bench.js",
|
|
71
|
-
"bench:cache": "node benchmark/bench-cache.js",
|
|
72
|
-
"bench:nwsapi": "node benchmark/bench-nwsapi.js",
|
|
73
|
-
"bench:ps-form": "node benchmark/bench-default.js && node benchmark/bench-indeterminate.js && node benchmark/bench-valid-invalid.js",
|
|
74
|
-
"bench:ps-has": "node benchmark/bench-has.js",
|
|
75
|
-
"bench:ps-nth": "node benchmark/bench-nth-child.js && node benchmark/bench-nth-of-type.js",
|
|
76
|
-
"bench:ps-traverse": "node benchmark/bench-dir.js && node benchmark/bench-lang.js",
|
|
77
|
-
"bench:ts": "node benchmark/bench-testing-library.js",
|
|
78
|
-
"bench:universal": "node benchmark/bench-universal.js",
|
|
72
|
+
"bench": "node --expose-gc benchmark/bench.js",
|
|
73
|
+
"bench:cache": "node --expose-gc benchmark/bench-cache.js",
|
|
74
|
+
"bench:nwsapi": "node --expose-gc benchmark/bench-nwsapi.js",
|
|
75
|
+
"bench:ps-form": "node --expose-gc benchmark/bench-default.js && node --expose-gc benchmark/bench-indeterminate.js && node --expose-gc benchmark/bench-valid-invalid.js",
|
|
76
|
+
"bench:ps-has": "node --expose-gc benchmark/bench-has.js",
|
|
77
|
+
"bench:ps-nth": "node --expose-gc benchmark/bench-nth-child.js && node --expose-gc benchmark/bench-nth-of-type.js",
|
|
78
|
+
"bench:ps-traverse": "node --expose-gc benchmark/bench-dir.js && node --expose-gc benchmark/bench-lang.js",
|
|
79
|
+
"bench:ts": "node --expose-gc benchmark/bench-testing-library.js",
|
|
80
|
+
"bench:universal": "node --expose-gc benchmark/bench-universal.js",
|
|
79
81
|
"build": "npm run tsc && npm run lint && npm test",
|
|
80
82
|
"lint": "eslint --fix .",
|
|
81
83
|
"test": "c8 --reporter=text mocha --exit test/**/*.test.js",
|
|
@@ -86,5 +88,5 @@
|
|
|
86
88
|
"engines": {
|
|
87
89
|
"node": "^22.13.0 || >=24.0.0"
|
|
88
90
|
},
|
|
89
|
-
"version": "8.1.
|
|
91
|
+
"version": "8.1.3"
|
|
90
92
|
}
|
package/src/index.js
CHANGED
|
@@ -101,6 +101,15 @@ export class DOMSelector {
|
|
|
101
101
|
doc.contentType === 'text/html' &&
|
|
102
102
|
doc.documentElement;
|
|
103
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Wraps the node for IDL internal implementation if idlUtils is present.
|
|
106
|
+
* @private
|
|
107
|
+
* @param {Document|DocumentFragment|Element} node - The raw node.
|
|
108
|
+
* @returns {object} The wrapped or raw node.
|
|
109
|
+
*/
|
|
110
|
+
#wrapNode = node =>
|
|
111
|
+
this.#idlUtils ? this.#idlUtils.wrapperForImpl(node) : node;
|
|
112
|
+
|
|
104
113
|
/**
|
|
105
114
|
* Clears the internal caches.
|
|
106
115
|
* @param {boolean} [clearAll] - Whether to clear all caches. If false,
|
|
@@ -205,8 +214,7 @@ export class DOMSelector {
|
|
|
205
214
|
}
|
|
206
215
|
if (filterMatches) {
|
|
207
216
|
try {
|
|
208
|
-
const
|
|
209
|
-
const match = this.#nwsapi.match(selector, n);
|
|
217
|
+
const match = this.#nwsapi.match(selector, this.#wrapNode(node));
|
|
210
218
|
let ast = null;
|
|
211
219
|
if (match) {
|
|
212
220
|
const astCacheKey = `check_ast_${selector}`;
|
|
@@ -226,16 +234,15 @@ export class DOMSelector {
|
|
|
226
234
|
}
|
|
227
235
|
}
|
|
228
236
|
}
|
|
229
|
-
if (this.#idlUtils) {
|
|
230
|
-
node = this.#idlUtils.wrapperForImpl(node);
|
|
231
|
-
}
|
|
232
237
|
const options = {
|
|
233
238
|
...opt,
|
|
234
239
|
check: true,
|
|
235
240
|
noexcept: true,
|
|
236
241
|
warn: false
|
|
237
242
|
};
|
|
238
|
-
return this.#finder
|
|
243
|
+
return this.#finder
|
|
244
|
+
.setup(selector, this.#wrapNode(node), options)
|
|
245
|
+
.find(TARGET_SELF);
|
|
239
246
|
};
|
|
240
247
|
|
|
241
248
|
/**
|
|
@@ -263,18 +270,16 @@ export class DOMSelector {
|
|
|
263
270
|
}
|
|
264
271
|
if (filterMatches) {
|
|
265
272
|
try {
|
|
266
|
-
|
|
267
|
-
return this.#nwsapi.match(selector, n);
|
|
273
|
+
return this.#nwsapi.match(selector, this.#wrapNode(node));
|
|
268
274
|
} catch (e) {
|
|
269
275
|
// fall through
|
|
270
276
|
}
|
|
271
277
|
}
|
|
272
278
|
}
|
|
273
279
|
try {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const nodes = this.#finder.setup(selector, node, opt).find(TARGET_SELF);
|
|
280
|
+
const nodes = this.#finder
|
|
281
|
+
.setup(selector, this.#wrapNode(node), opt)
|
|
282
|
+
.find(TARGET_SELF);
|
|
278
283
|
return nodes.size > 0;
|
|
279
284
|
} catch (e) {
|
|
280
285
|
this.#finder.onError(e, opt);
|
|
@@ -307,25 +312,20 @@ export class DOMSelector {
|
|
|
307
312
|
}
|
|
308
313
|
if (filterMatches) {
|
|
309
314
|
try {
|
|
310
|
-
|
|
311
|
-
return this.#nwsapi.closest(selector, n);
|
|
315
|
+
return this.#nwsapi.closest(selector, this.#wrapNode(node));
|
|
312
316
|
} catch (e) {
|
|
313
317
|
// fall through
|
|
314
318
|
}
|
|
315
319
|
}
|
|
316
320
|
}
|
|
317
|
-
let res;
|
|
318
321
|
try {
|
|
319
|
-
|
|
320
|
-
node = this.#idlUtils.wrapperForImpl(node);
|
|
321
|
-
}
|
|
322
|
+
node = this.#wrapNode(node);
|
|
322
323
|
const nodes = this.#finder.setup(selector, node, opt).find(TARGET_LINEAL);
|
|
323
324
|
if (nodes.size) {
|
|
324
325
|
let refNode = node;
|
|
325
326
|
while (refNode) {
|
|
326
327
|
if (nodes.has(refNode)) {
|
|
327
|
-
|
|
328
|
-
break;
|
|
328
|
+
return refNode;
|
|
329
329
|
}
|
|
330
330
|
refNode = refNode.parentNode;
|
|
331
331
|
}
|
|
@@ -333,7 +333,7 @@ export class DOMSelector {
|
|
|
333
333
|
} catch (e) {
|
|
334
334
|
this.#finder.onError(e, opt);
|
|
335
335
|
}
|
|
336
|
-
return
|
|
336
|
+
return null;
|
|
337
337
|
};
|
|
338
338
|
|
|
339
339
|
/**
|
|
@@ -374,19 +374,16 @@ export class DOMSelector {
|
|
|
374
374
|
}
|
|
375
375
|
}
|
|
376
376
|
*/
|
|
377
|
-
let res;
|
|
378
377
|
try {
|
|
379
|
-
|
|
380
|
-
node = this.#idlUtils.wrapperForImpl(node);
|
|
381
|
-
}
|
|
378
|
+
node = this.#wrapNode(node);
|
|
382
379
|
const nodes = this.#finder.setup(selector, node, opt).find(TARGET_FIRST);
|
|
383
380
|
if (nodes.size) {
|
|
384
|
-
|
|
381
|
+
return nodes.values().next().value;
|
|
385
382
|
}
|
|
386
383
|
} catch (e) {
|
|
387
384
|
this.#finder.onError(e, opt);
|
|
388
385
|
}
|
|
389
|
-
return
|
|
386
|
+
return null;
|
|
390
387
|
};
|
|
391
388
|
|
|
392
389
|
/**
|
|
@@ -407,37 +404,43 @@ export class DOMSelector {
|
|
|
407
404
|
if (document && REG_UNIVERSAL.test(selector)) {
|
|
408
405
|
return collectAllDescendants(node, document);
|
|
409
406
|
}
|
|
410
|
-
if (
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
407
|
+
if (this.#canUseNwsapi(document)) {
|
|
408
|
+
let routeToNwsapi = node === this.#document;
|
|
409
|
+
if (!routeToNwsapi) {
|
|
410
|
+
const testCacheKey = `testlib_${selector}`;
|
|
411
|
+
let isTestLib = this.#cache.get(testCacheKey);
|
|
412
|
+
if (isTestLib === undefined) {
|
|
413
|
+
isTestLib = REG_TEST_LIB.test(selector);
|
|
414
|
+
this.#cache.set(testCacheKey, isTestLib);
|
|
415
|
+
}
|
|
416
|
+
routeToNwsapi = isTestLib;
|
|
419
417
|
}
|
|
420
|
-
if (
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
418
|
+
if (routeToNwsapi) {
|
|
419
|
+
const cacheKey = `querySelectorAll_${selector}`;
|
|
420
|
+
let filterMatches = this.#cache.get(cacheKey);
|
|
421
|
+
if (filterMatches === undefined) {
|
|
422
|
+
filterMatches = filterSelector(selector, TARGET_ALL);
|
|
423
|
+
this.#cache.set(cacheKey, filterMatches);
|
|
424
|
+
}
|
|
425
|
+
if (filterMatches) {
|
|
426
|
+
try {
|
|
427
|
+
return this.#nwsapi.select(selector, this.#wrapNode(node));
|
|
428
|
+
} catch (e) {
|
|
429
|
+
// fall through
|
|
430
|
+
}
|
|
426
431
|
}
|
|
427
432
|
}
|
|
428
433
|
}
|
|
429
|
-
let res;
|
|
430
434
|
try {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
const nodes = this.#finder.setup(selector, node, opt).find(TARGET_ALL);
|
|
435
|
+
const nodes = this.#finder
|
|
436
|
+
.setup(selector, this.#wrapNode(node), opt)
|
|
437
|
+
.find(TARGET_ALL);
|
|
435
438
|
if (nodes.size) {
|
|
436
|
-
|
|
439
|
+
return [...nodes];
|
|
437
440
|
}
|
|
438
441
|
} catch (e) {
|
|
439
442
|
this.#finder.onError(e, opt);
|
|
440
443
|
}
|
|
441
|
-
return
|
|
444
|
+
return [];
|
|
442
445
|
};
|
|
443
446
|
}
|
package/src/js/constant.js
CHANGED
|
@@ -81,7 +81,6 @@ export const TAG_TYPE = `\\*|${TAG_TYPE_WO_UNIVERSAL}`;
|
|
|
81
81
|
export const TAG_TYPE_I = '\\*|[A-Z][\\w-]*';
|
|
82
82
|
export const COMPOUND = `(?:${TAG_TYPE}|(?:${TAG_TYPE})?(?:${SUB_TYPE})+)`;
|
|
83
83
|
export const COMPOUND_L = `(?:${TAG_TYPE}|(?:${TAG_TYPE})?(?:${SUB_TYPE}|${LOGIC_IS})+)`;
|
|
84
|
-
export const COMPOUND_L_I = `(?:(?:${TAG_TYPE_I})?(?:${SUB_TYPE}|${LOGIC_IS})+)`;
|
|
85
84
|
export const COMPOUND_I = `(?:${TAG_TYPE_I}|(?:${TAG_TYPE_I})?(?:${SUB_TYPE})+)`;
|
|
86
85
|
export const COMPOUND_WO_PSEUDO = `(?:${TAG_TYPE}|(?:${TAG_TYPE})?(?:${SUB_TYPE_WO_PSEUDO})+)`;
|
|
87
86
|
export const COMPLEX = `${COMPOUND}(?:${COMBO}${COMPOUND})*`;
|
package/src/js/finder.js
CHANGED
|
@@ -113,7 +113,6 @@ export class Finder {
|
|
|
113
113
|
#ast;
|
|
114
114
|
#astCache = new WeakMap();
|
|
115
115
|
#check;
|
|
116
|
-
#combinatorCache;
|
|
117
116
|
#descendant;
|
|
118
117
|
#document;
|
|
119
118
|
#documentCache = new WeakMap();
|
|
@@ -246,7 +245,6 @@ export class Finder {
|
|
|
246
245
|
*/
|
|
247
246
|
clearResults = (all = false) => {
|
|
248
247
|
this.#anbCache = null;
|
|
249
|
-
this.#combinatorCache = null;
|
|
250
248
|
this.#focusWithinCache = null;
|
|
251
249
|
this.#invalidateResults = null;
|
|
252
250
|
this.#nthChildCache = null;
|
|
@@ -2223,36 +2221,12 @@ export class Finder {
|
|
|
2223
2221
|
break;
|
|
2224
2222
|
}
|
|
2225
2223
|
case '~': {
|
|
2226
|
-
const parentNode = node.parentNode;
|
|
2227
|
-
if (!parentNode) {
|
|
2228
|
-
break;
|
|
2229
|
-
}
|
|
2230
|
-
if (!this.#combinatorCache) {
|
|
2231
|
-
this.#combinatorCache = new WeakMap();
|
|
2232
|
-
}
|
|
2233
|
-
let cacheMap = this.#combinatorCache.get(parentNode);
|
|
2234
|
-
if (!cacheMap) {
|
|
2235
|
-
cacheMap = new Map();
|
|
2236
|
-
this.#combinatorCache.set(parentNode, cacheMap);
|
|
2237
|
-
}
|
|
2238
|
-
let matchedSet = cacheMap.get(leaves);
|
|
2239
|
-
if (!matchedSet) {
|
|
2240
|
-
matchedSet = new Set();
|
|
2241
|
-
let child = parentNode.firstElementChild;
|
|
2242
|
-
while (child) {
|
|
2243
|
-
if (this._matchLeaves(leaves, child, opt)) {
|
|
2244
|
-
matchedSet.add(child);
|
|
2245
|
-
}
|
|
2246
|
-
child = child.nextElementSibling;
|
|
2247
|
-
}
|
|
2248
|
-
cacheMap.set(leaves, matchedSet);
|
|
2249
|
-
}
|
|
2250
2224
|
let refNode =
|
|
2251
2225
|
dir === DIR_NEXT
|
|
2252
2226
|
? node.nextElementSibling
|
|
2253
2227
|
: node.previousElementSibling;
|
|
2254
2228
|
while (refNode) {
|
|
2255
|
-
if (
|
|
2229
|
+
if (this._matchLeaves(leaves, refNode, opt)) {
|
|
2256
2230
|
matched.push(refNode);
|
|
2257
2231
|
}
|
|
2258
2232
|
refNode =
|
package/src/js/selector.js
CHANGED
|
@@ -11,8 +11,6 @@ import {
|
|
|
11
11
|
COMBINATOR,
|
|
12
12
|
COMBO,
|
|
13
13
|
COMPOUND_I,
|
|
14
|
-
COMPOUND_L_I,
|
|
15
|
-
DESCEND,
|
|
16
14
|
HAS_COMPOUND,
|
|
17
15
|
KEYS_LOGICAL,
|
|
18
16
|
KEYS_PS_CLASS_SUPPORTED,
|
|
@@ -23,6 +21,7 @@ import {
|
|
|
23
21
|
PS_CLASS_SELECTOR,
|
|
24
22
|
PS_ELEMENT_SELECTOR,
|
|
25
23
|
SELECTOR,
|
|
24
|
+
SUB_TYPE,
|
|
26
25
|
SYNTAX_ERR,
|
|
27
26
|
TARGET_ALL
|
|
28
27
|
} from './constant.js';
|
|
@@ -30,9 +29,11 @@ import {
|
|
|
30
29
|
/* regexp */
|
|
31
30
|
const REG_EXCLUDE_BASIC =
|
|
32
31
|
/[|\\]|::|[^\u0021-\u007F\s]|\[\s*[\w$*=^|~-]+(?:(?:"[\w$*=^|~\s'-]+"|'[\w$*=^|~\s"-]+')?(?:\s+[\w$*=^|~-]+)+|"[^"\]]{1,255}|'[^'\]]{1,255})\s*\]|:(?:is|where)\(\s*\)/;
|
|
32
|
+
const REG_EXCLUDE_QSA = new RegExp(
|
|
33
|
+
`(?:^(?:[A-Z]|\\.)[\\w-]*$|^(?:${SUB_TYPE}|:${N_TH})+$|${COMPOUND_I}${COMBO}${COMPOUND_I})`,
|
|
34
|
+
'i'
|
|
35
|
+
);
|
|
33
36
|
const REG_COMPLEX = new RegExp(`${COMPOUND_I}${COMBO}${COMPOUND_I}`, 'i');
|
|
34
|
-
const REG_COMPOUND = new RegExp(`^${COMPOUND_L_I}$`, 'i');
|
|
35
|
-
const REG_DESCEND = new RegExp(`${COMPOUND_I}${DESCEND}${COMPOUND_I}`, 'i');
|
|
36
37
|
const REG_LOGIC_COMPLEX = new RegExp(
|
|
37
38
|
`:(?!${PSEUDO_CLASS}|${N_TH}|${LOGIC_COMPLEX})`
|
|
38
39
|
);
|
|
@@ -264,7 +265,6 @@ export const extractSubjectsRegExp = (selector, caseSensitive) => {
|
|
|
264
265
|
* @returns {boolean} - True if the selector is valid for nwsapi.
|
|
265
266
|
*/
|
|
266
267
|
export const filterSelector = (selector, target) => {
|
|
267
|
-
const isQuerySelectorAll = target === TARGET_ALL;
|
|
268
268
|
// Basic validation and fast-fail for null/undefined/non-string values.
|
|
269
269
|
if (
|
|
270
270
|
!selector ||
|
|
@@ -277,6 +277,10 @@ export const filterSelector = (selector, target) => {
|
|
|
277
277
|
if (REG_INVALID_SYNTAX.test(selector)) {
|
|
278
278
|
return false;
|
|
279
279
|
}
|
|
280
|
+
// Target-specific early exits.
|
|
281
|
+
if (target === TARGET_ALL && REG_EXCLUDE_QSA.test(selector)) {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
280
284
|
// Exclude various complex or unsupported selectors early.
|
|
281
285
|
// i.e. non-ASCII, escaped selectors, namespaced selectors, pseudo-elements.
|
|
282
286
|
if (selector.includes('/') || REG_EXCLUDE_BASIC.test(selector)) {
|
|
@@ -289,18 +293,11 @@ export const filterSelector = (selector, target) => {
|
|
|
289
293
|
return false;
|
|
290
294
|
}
|
|
291
295
|
}
|
|
292
|
-
// Target-specific early exits.
|
|
293
|
-
if (target === TARGET_ALL && !REG_COMPOUND.test(selector)) {
|
|
294
|
-
return false;
|
|
295
|
-
}
|
|
296
296
|
// Logic for pseudo-classes.
|
|
297
297
|
if (selector.includes(':')) {
|
|
298
|
-
// Exclude descendant combinators in logical selectors for querySelectorAll.
|
|
299
|
-
if (isQuerySelectorAll && REG_DESCEND.test(selector)) {
|
|
300
|
-
return false;
|
|
301
|
-
}
|
|
302
298
|
// Determine if the selector has complex logical structures.
|
|
303
|
-
const isComplex =
|
|
299
|
+
const isComplex =
|
|
300
|
+
target === TARGET_ALL ? false : REG_COMPLEX.test(selector);
|
|
304
301
|
// Handle :has() specifically.
|
|
305
302
|
if (selector.includes(':has(')) {
|
|
306
303
|
if (!isComplex || REG_LOGIC_HAS_COMPOUND.test(selector)) {
|
package/types/js/constant.d.ts
CHANGED
|
@@ -62,7 +62,6 @@ export const TAG_TYPE: "\\*|[A-Za-z][\\w-]*";
|
|
|
62
62
|
export const TAG_TYPE_I: "\\*|[A-Z][\\w-]*";
|
|
63
63
|
export const COMPOUND: "(?:\\*|[A-Za-z][\\w-]*|(?:\\*|[A-Za-z][\\w-]*)?(?:\\[[^|\\]]+\\]|[#.:][\\w-]+)+)";
|
|
64
64
|
export const COMPOUND_L: "(?:\\*|[A-Za-z][\\w-]*|(?:\\*|[A-Za-z][\\w-]*)?(?:\\[[^|\\]]+\\]|[#.:][\\w-]+|:is\\(\\s*[^)]+\\s*\\))+)";
|
|
65
|
-
export const COMPOUND_L_I: "(?:(?:\\*|[A-Z][\\w-]*)?(?:\\[[^|\\]]+\\]|[#.:][\\w-]+|:is\\(\\s*[^)]+\\s*\\))+)";
|
|
66
65
|
export const COMPOUND_I: "(?:\\*|[A-Z][\\w-]*|(?:\\*|[A-Z][\\w-]*)?(?:\\[[^|\\]]+\\]|[#.:][\\w-]+)+)";
|
|
67
66
|
export const COMPOUND_WO_PSEUDO: "(?:\\*|[A-Za-z][\\w-]*|(?:\\*|[A-Za-z][\\w-]*)?(?:\\[[^|\\]]+\\]|[#.][\\w-]+)+)";
|
|
68
67
|
export const COMPLEX: "(?:\\*|[A-Za-z][\\w-]*|(?:\\*|[A-Za-z][\\w-]*)?(?:\\[[^|\\]]+\\]|[#.:][\\w-]+)+)(?:\\s?[\\s>~+]\\s?(?:\\*|[A-Za-z][\\w-]*|(?:\\*|[A-Za-z][\\w-]*)?(?:\\[[^|\\]]+\\]|[#.:][\\w-]+)+))*";
|