@asamuzakjp/dom-selector 5.2.2 → 5.3.1

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.
@@ -3,28 +3,23 @@
3
3
  */
4
4
 
5
5
  /* string */
6
- export const AN_PLUS_B = 'AnPlusB';
6
+ export const ATTR_SELECTOR = 'AttributeSelector';
7
+ export const CLASS_SELECTOR = 'ClassSelector';
7
8
  export const COMBINATOR = 'Combinator';
8
9
  export const EMPTY = '__EMPTY__';
9
- export const IDENTIFIER = 'Identifier';
10
+ export const IDENT = 'Identifier';
11
+ export const ID_SELECTOR = 'IdSelector';
10
12
  export const NOT_SUPPORTED_ERR = 'NotSupportedError';
11
13
  export const NTH = 'Nth';
12
- export const RAW = 'Raw';
14
+ export const PS_CLASS_SELECTOR = 'PseudoClassSelector';
15
+ export const PS_ELEMENT_SELECTOR = 'PseudoElementSelector';
13
16
  export const SELECTOR = 'Selector';
14
- export const SELECTOR_ATTR = 'AttributeSelector';
15
- export const SELECTOR_CLASS = 'ClassSelector';
16
- export const SELECTOR_ID = 'IdSelector';
17
- export const SELECTOR_LIST = 'SelectorList';
18
- export const SELECTOR_PSEUDO_CLASS = 'PseudoClassSelector';
19
- export const SELECTOR_PSEUDO_ELEMENT = 'PseudoElementSelector';
20
- export const SELECTOR_TYPE = 'TypeSelector';
21
- export const STRING = 'String';
22
17
  export const SYNTAX_ERR = 'SyntaxError';
23
18
  export const TARGET_ALL = 'all';
24
19
  export const TARGET_FIRST = 'first';
25
20
  export const TARGET_LINEAL = 'lineal';
26
21
  export const TARGET_SELF = 'self';
27
- export const U_FFFD = '\uFFFD';
22
+ export const TYPE_SELECTOR = 'TypeSelector';
28
23
 
29
24
  /* numeric */
30
25
  export const BIT_01 = 1;
@@ -34,9 +29,9 @@ export const BIT_08 = 8;
34
29
  export const BIT_16 = 0x10;
35
30
  export const BIT_32 = 0x20;
36
31
  export const BIT_FFFF = 0xFFFF;
37
- export const BIT_HYPHEN = 0x2D;
38
32
  export const DUO = 2;
39
33
  export const HEX = 16;
34
+ export const HYPHEN = 0x2D;
40
35
  export const TYPE_FROM = 8;
41
36
  export const TYPE_TO = -1;
42
37
 
@@ -61,7 +56,7 @@ export const ALPHA_NUM = '[A-Z\\d]+';
61
56
  export const CHILD_IDX = '(?:first|last|only)-(?:child|of-type)';
62
57
  export const DIGIT = '(?:0|[1-9]\\d*)';
63
58
  export const LANG_PART = `(?:-${ALPHA_NUM})*`;
64
- export const PSEUDO_CLASSES =
59
+ export const PSEUDO_CLASS =
65
60
  `(?:any-)?link|${CHILD_IDX}|checked|empty|indeterminate|root|target|visited`;
66
61
  export const ANB =
67
62
  `[+-]?(?:${DIGIT}n?|n)|(?:[+-]?${DIGIT})?n\\s*[+-]\\s*${DIGIT}`;
@@ -95,35 +90,6 @@ export const LOGICAL_COMPOUND =
95
90
  `${KEY_IS_NOT}\\(\\s*${COMPOUND_A}(?:\\s*,\\s*${COMPOUND_A})*\\s*\\)`;
96
91
 
97
92
  /* regexp */
98
- export const REG_ANCHOR = /^a(?:rea)?$/;
99
- export const REG_COMPLEX = new RegExp(`${COMBO}${COMPOUND_I}`, 'i');
100
- export const REG_DESCEND = new RegExp(`${DESCEND}${COMPOUND_I}`, 'i');
101
- export const REG_DIR = /^(?:ltr|rtl)$/;
102
- export const REG_FILTER_COMPLEX =
103
- new RegExp(`:(?!${PSEUDO_CLASSES}|${N_TH}|${LOGICAL_COMPLEX})`);
104
- export const REG_FILTER_COMPOUND =
105
- new RegExp(`:(?!${PSEUDO_CLASSES}|${N_TH}|${LOGICAL_COMPOUND})`);
106
- export const REG_FILTER_SIMPLE = new RegExp(`:(?!${PSEUDO_CLASSES}|${N_TH})`);
107
- export const REG_FORM = /^(?:button|fieldset|form|input|select|textarea)$/;
108
- export const REG_FORM_CTRL =
109
- /^(?:button|fieldset|input|optgroup|option|select|textarea)$/;
110
- export const REG_FORM_VALID = /^(?:button|form|input|select|textarea)$/;
111
- export const REG_HEX = /^([\da-f]{1,6}\s?)/i;
112
- export const REG_INTERACT = /^(?:details|dialog)$/;
113
- export const REG_INVALID_SELECTOR = /^$|^\s*>|,\s*$/;
114
- export const REG_LANG = new RegExp(`^(?:\\*-)?${ALPHA_NUM}${LANG_PART}$`, 'i');
115
- export const REG_LANG_QUOTED = /(:lang\(\s*("[A-Za-z\d\-*]*")\s*\))/;
116
- export const REG_LOGICAL_EMPTY = /(:(is|where)\(\s*\))/;
117
- export const REG_LOGICAL_PSEUDO = /^(?:has|is|not|where)$/;
118
- export const REG_SHADOW_HOST = /^host(?:-context)?$/;
119
- export const REG_SHADOW_MODE = /^(?:close|open)$/;
120
- export const REG_SHADOW_PSEUDO = /^part|slotted$/;
121
- export const REG_TAG_NAME = /[A-Z][\\w-]*/i;
122
- export const REG_TYPE_CHECK = /^(?:checkbox|radio)$/;
123
- export const REG_TYPE_INPUT =
93
+ export const REG_LOGICAL = /^(?:has|is|not|where)$/;
94
+ export const REG_INPUT_TYPE =
124
95
  /^(?:date(?:time-local)?|email|month|number|password|search|tel|text|time|url|week)$/;
125
- export const REG_TYPE_RANGE =
126
- /^(?:date(?:time-local)?|month|number|range|time|week)$/;
127
- export const REG_TYPE_RESET = /^(?:button|reset)$/;
128
- export const REG_TYPE_SUBMIT = /^(?:image|submit)$/;
129
- export const REG_TYPE_TEXT = /^(?:email|number|password|search|tel|text|url)$/;
package/src/js/finder.js CHANGED
@@ -14,18 +14,26 @@ import {
14
14
 
15
15
  /* constants */
16
16
  import {
17
- BIT_01, COMBINATOR, DOCUMENT_FRAGMENT_NODE, DOCUMENT_NODE, ELEMENT_NODE,
18
- EMPTY, NOT_SUPPORTED_ERR, REG_ANCHOR, REG_FORM, REG_FORM_CTRL,
19
- REG_FORM_VALID, REG_INTERACT, REG_LOGICAL_PSEUDO, REG_SHADOW_HOST,
20
- REG_TYPE_CHECK, REG_TYPE_INPUT, REG_TYPE_RANGE, REG_TYPE_RESET,
21
- REG_TYPE_SUBMIT, REG_TYPE_TEXT, SELECTOR_ATTR, SELECTOR_CLASS, SELECTOR_ID,
22
- SELECTOR_PSEUDO_CLASS, SELECTOR_PSEUDO_ELEMENT, SELECTOR_TYPE, SHOW_ALL,
23
- SYNTAX_ERR, TARGET_ALL, TARGET_FIRST, TARGET_LINEAL, TARGET_SELF, TEXT_NODE,
24
- WALKER_FILTER
17
+ ATTR_SELECTOR, BIT_01, CLASS_SELECTOR, COMBINATOR, DOCUMENT_FRAGMENT_NODE,
18
+ DOCUMENT_NODE, ELEMENT_NODE, EMPTY, ID_SELECTOR, NOT_SUPPORTED_ERR,
19
+ PS_CLASS_SELECTOR, PS_ELEMENT_SELECTOR, REG_INPUT_TYPE, REG_LOGICAL,
20
+ SHOW_ALL, SYNTAX_ERR, TARGET_ALL, TARGET_FIRST, TARGET_LINEAL, TARGET_SELF,
21
+ TEXT_NODE, TYPE_SELECTOR, WALKER_FILTER
25
22
  } from './constant.js';
26
23
  const DIR_NEXT = 'next';
27
24
  const DIR_PREV = 'prev';
28
25
  const KEY_TAB = 'Tab';
26
+ const REG_ANCHOR = /^a(?:rea)?$/;
27
+ const REG_FORM_CTRL =
28
+ /^(?:button|fieldset|input|optgroup|option|select|textarea)$/;
29
+ const REG_FORM_VALID = /^(?:button|form|input|select|textarea)$/;
30
+ const REG_INTERACT = /^(?:details|dialog)$/;
31
+ const REG_SHADOW_HOST = /^host(?:-context)?$/;
32
+ const REG_TYPE_CHECK = /^(?:checkbox|radio)$/;
33
+ const REG_TYPE_RANGE = /^(?:date(?:time-local)?|month|number|range|time|week)$/;
34
+ const REG_TYPE_RESET = /^(?:button|reset)$/;
35
+ const REG_TYPE_SUBMIT = /^(?:image|submit)$/;
36
+ const REG_TYPE_TEXT = /^(?:email|number|password|search|tel|text|url)$/;
29
37
 
30
38
  /**
31
39
  * Finder
@@ -110,6 +118,8 @@ export class Finder {
110
118
  } else {
111
119
  throw new this.#window.DOMException(e.message, e.name);
112
120
  }
121
+ } else if (e.name in this.#window) {
122
+ throw new this.#window[e.name](e.message);
113
123
  } else {
114
124
  throw e;
115
125
  }
@@ -222,8 +232,10 @@ export class Finder {
222
232
  } catch (e) {
223
233
  this.onError(e);
224
234
  }
225
- const { branches, info: { hasHasPseudoFunc } } = walkAST(cssAst);
226
- let invalidate = !!hasHasPseudoFunc;
235
+ const {
236
+ branches, info: { hasHasPseudoFunc, hasNthChildOfSelector }
237
+ } = walkAST(cssAst);
238
+ let invalidate = !!(hasHasPseudoFunc || hasNthChildOfSelector);
227
239
  let descendant = false;
228
240
  let i = 0;
229
241
  ast = [];
@@ -354,12 +366,9 @@ export class Finder {
354
366
  if (this.#astCache.has(selector)) {
355
367
  selectorBranches = this.#astCache.get(selector);
356
368
  } else {
357
- const { branches, info } = walkAST(selector);
369
+ const { branches } = walkAST(selector);
358
370
  selectorBranches = branches;
359
371
  this.#astCache.set(selector, selectorBranches);
360
- if (info.hasLogicalPseudoFunc) {
361
- this.#invalidate = true;
362
- }
363
372
  }
364
373
  }
365
374
  if (parentNode) {
@@ -828,7 +837,7 @@ export class Finder {
828
837
  } = opt;
829
838
  const matched = new Set();
830
839
  // :has(), :is(), :not(), :where()
831
- if (REG_LOGICAL_PSEUDO.test(astName)) {
840
+ if (REG_LOGICAL.test(astName)) {
832
841
  let astData;
833
842
  if (this.#astCache.has(ast)) {
834
843
  astData = this.#astCache.get(ast);
@@ -1194,7 +1203,7 @@ export class Finder {
1194
1203
  break;
1195
1204
  }
1196
1205
  case 'input': {
1197
- if ((!node.type || REG_TYPE_INPUT.test(node.type)) &&
1206
+ if ((!node.type || REG_INPUT_TYPE.test(node.type)) &&
1198
1207
  (node.readonly || node.hasAttribute('readonly') ||
1199
1208
  node.disabled || node.hasAttribute('disabled'))) {
1200
1209
  matched.add(node);
@@ -1219,7 +1228,7 @@ export class Finder {
1219
1228
  break;
1220
1229
  }
1221
1230
  case 'input': {
1222
- if ((!node.type || REG_TYPE_INPUT.test(node.type)) &&
1231
+ if ((!node.type || REG_INPUT_TYPE.test(node.type)) &&
1223
1232
  !(node.readonly || node.hasAttribute('readonly') ||
1224
1233
  node.disabled || node.hasAttribute('disabled'))) {
1225
1234
  matched.add(node);
@@ -1476,7 +1485,7 @@ export class Finder {
1476
1485
  if (node.hasAttribute('type')) {
1477
1486
  const inputType = node.getAttribute('type');
1478
1487
  if (inputType === 'file' || REG_TYPE_CHECK.test(inputType) ||
1479
- REG_TYPE_INPUT.test(inputType)) {
1488
+ REG_INPUT_TYPE.test(inputType)) {
1480
1489
  targetNode = node;
1481
1490
  }
1482
1491
  } else {
@@ -1497,7 +1506,7 @@ export class Finder {
1497
1506
  if (node.hasAttribute('type')) {
1498
1507
  const inputType = node.getAttribute('type');
1499
1508
  if (inputType === 'file' || REG_TYPE_CHECK.test(inputType) ||
1500
- REG_TYPE_INPUT.test(inputType)) {
1509
+ REG_INPUT_TYPE.test(inputType)) {
1501
1510
  targetNode = node;
1502
1511
  }
1503
1512
  } else {
@@ -1767,23 +1776,23 @@ export class Finder {
1767
1776
  }
1768
1777
  if (node.nodeType === ELEMENT_NODE) {
1769
1778
  switch (astType) {
1770
- case SELECTOR_PSEUDO_ELEMENT: {
1779
+ case PS_ELEMENT_SELECTOR: {
1771
1780
  this.#matcher.matchPseudoElementSelector(astName, opt);
1772
1781
  break;
1773
1782
  }
1774
- case SELECTOR_ID: {
1783
+ case ID_SELECTOR: {
1775
1784
  if (node.id === astName) {
1776
1785
  matched.add(node);
1777
1786
  }
1778
1787
  break;
1779
1788
  }
1780
- case SELECTOR_CLASS: {
1789
+ case CLASS_SELECTOR: {
1781
1790
  if (node.classList.contains(astName)) {
1782
1791
  matched.add(node);
1783
1792
  }
1784
1793
  break;
1785
1794
  }
1786
- case SELECTOR_PSEUDO_CLASS: {
1795
+ case PS_CLASS_SELECTOR: {
1787
1796
  const nodes = this._matchPseudoClassSelector(ast, node, opt);
1788
1797
  return nodes;
1789
1798
  }
@@ -1794,9 +1803,9 @@ export class Finder {
1794
1803
  }
1795
1804
  }
1796
1805
  }
1797
- } else if (this.#shadow && astType === SELECTOR_PSEUDO_CLASS &&
1806
+ } else if (this.#shadow && astType === PS_CLASS_SELECTOR &&
1798
1807
  node.nodeType === DOCUMENT_FRAGMENT_NODE) {
1799
- if (astName !== 'has' && REG_LOGICAL_PSEUDO.test(astName)) {
1808
+ if (astName !== 'has' && REG_LOGICAL.test(astName)) {
1800
1809
  const nodes = this._matchPseudoClassSelector(ast, node, opt);
1801
1810
  return nodes;
1802
1811
  } else if (REG_SHADOW_HOST.test(astName)) {
@@ -1831,17 +1840,18 @@ export class Finder {
1831
1840
  }
1832
1841
  if (typeof bool !== 'boolean') {
1833
1842
  let cacheable = true;
1834
- if (node.nodeType === ELEMENT_NODE && REG_FORM.test(node.localName)) {
1843
+ if (node.nodeType === ELEMENT_NODE &&
1844
+ /^(?:button|fieldset|form|input|select|textarea)$/.test(node.localName)) {
1835
1845
  cacheable = false;
1836
1846
  }
1837
1847
  for (const leaf of leaves) {
1838
1848
  switch (leaf.type) {
1839
- case SELECTOR_ATTR:
1840
- case SELECTOR_ID: {
1849
+ case ATTR_SELECTOR:
1850
+ case ID_SELECTOR: {
1841
1851
  cacheable = false;
1842
1852
  break;
1843
1853
  }
1844
- case SELECTOR_PSEUDO_CLASS: {
1854
+ case PS_CLASS_SELECTOR: {
1845
1855
  if (/^(?:(?:any-)?link|defined|dir)$/.test(leaf.name)) {
1846
1856
  cacheable = false;
1847
1857
  }
@@ -1921,11 +1931,11 @@ export class Finder {
1921
1931
  pending = true;
1922
1932
  } else {
1923
1933
  switch (leafType) {
1924
- case SELECTOR_PSEUDO_ELEMENT: {
1934
+ case PS_ELEMENT_SELECTOR: {
1925
1935
  this.#matcher.matchPseudoElementSelector(leafName, opt);
1926
1936
  break;
1927
1937
  }
1928
- case SELECTOR_ID: {
1938
+ case ID_SELECTOR: {
1929
1939
  if (this.#root.nodeType === ELEMENT_NODE) {
1930
1940
  pending = true;
1931
1941
  } else {
@@ -1943,7 +1953,7 @@ export class Finder {
1943
1953
  }
1944
1954
  break;
1945
1955
  }
1946
- case SELECTOR_CLASS: {
1956
+ case CLASS_SELECTOR: {
1947
1957
  const items = baseNode.getElementsByClassName(leafName);
1948
1958
  nodes = this._matchHTMLCollection(items, {
1949
1959
  compound,
@@ -1951,7 +1961,7 @@ export class Finder {
1951
1961
  });
1952
1962
  break;
1953
1963
  }
1954
- case SELECTOR_TYPE: {
1964
+ case TYPE_SELECTOR: {
1955
1965
  if (this.#document.contentType === 'text/html' &&
1956
1966
  !/[*|]/.test(leafName)) {
1957
1967
  const items = baseNode.getElementsByTagName(leafName);
@@ -2322,13 +2332,13 @@ export class Finder {
2322
2332
  let filtered = false;
2323
2333
  let pending = false;
2324
2334
  switch (leafType) {
2325
- case SELECTOR_PSEUDO_ELEMENT: {
2335
+ case PS_ELEMENT_SELECTOR: {
2326
2336
  this.#matcher.matchPseudoElementSelector(leafName, {
2327
2337
  warn: this.#warn
2328
2338
  });
2329
2339
  break;
2330
2340
  }
2331
- case SELECTOR_ID: {
2341
+ case ID_SELECTOR: {
2332
2342
  if (targetType === TARGET_SELF) {
2333
2343
  [nodes, filtered] = this._matchSelf(leaves);
2334
2344
  } else if (targetType === TARGET_LINEAL) {
@@ -2359,7 +2369,7 @@ export class Finder {
2359
2369
  }
2360
2370
  break;
2361
2371
  }
2362
- case SELECTOR_CLASS: {
2372
+ case CLASS_SELECTOR: {
2363
2373
  if (targetType === TARGET_SELF) {
2364
2374
  [nodes, filtered] = this._matchSelf(leaves);
2365
2375
  } else if (targetType === TARGET_LINEAL) {
@@ -2383,7 +2393,7 @@ export class Finder {
2383
2393
  }
2384
2394
  break;
2385
2395
  }
2386
- case SELECTOR_TYPE: {
2396
+ case TYPE_SELECTOR: {
2387
2397
  if (targetType === TARGET_SELF) {
2388
2398
  [nodes, filtered] = this._matchSelf(leaves);
2389
2399
  } else if (targetType === TARGET_LINEAL) {
@@ -2473,19 +2483,19 @@ export class Finder {
2473
2483
  type: lastType
2474
2484
  }]
2475
2485
  } = lastTwig;
2476
- if (lastType === SELECTOR_PSEUDO_ELEMENT ||
2477
- lastType === SELECTOR_ID) {
2486
+ if (lastType === PS_ELEMENT_SELECTOR ||
2487
+ lastType === ID_SELECTOR) {
2478
2488
  dir = DIR_PREV;
2479
2489
  twig = lastTwig;
2480
- } else if (firstType === SELECTOR_PSEUDO_ELEMENT ||
2481
- firstType === SELECTOR_ID) {
2490
+ } else if (firstType === PS_ELEMENT_SELECTOR ||
2491
+ firstType === ID_SELECTOR) {
2482
2492
  dir = DIR_NEXT;
2483
2493
  twig = firstTwig;
2484
2494
  } else if (targetType === TARGET_ALL) {
2485
- if (firstName === '*' && firstType === SELECTOR_TYPE) {
2495
+ if (firstName === '*' && firstType === TYPE_SELECTOR) {
2486
2496
  dir = DIR_PREV;
2487
2497
  twig = lastTwig;
2488
- } else if (lastName === '*' && lastType === SELECTOR_TYPE) {
2498
+ } else if (lastName === '*' && lastType === TYPE_SELECTOR) {
2489
2499
  dir = DIR_NEXT;
2490
2500
  twig = firstTwig;
2491
2501
  } else if (branchLen === 2) {
@@ -2501,17 +2511,17 @@ export class Finder {
2501
2511
  dir = DIR_NEXT;
2502
2512
  twig = firstTwig;
2503
2513
  }
2504
- } else if (lastName === '*' && lastType === SELECTOR_TYPE) {
2514
+ } else if (lastName === '*' && lastType === TYPE_SELECTOR) {
2505
2515
  dir = DIR_NEXT;
2506
2516
  twig = firstTwig;
2507
- } else if (firstName === '*' && firstType === SELECTOR_TYPE) {
2517
+ } else if (firstName === '*' && firstType === TYPE_SELECTOR) {
2508
2518
  dir = DIR_PREV;
2509
2519
  twig = lastTwig;
2510
2520
  } else {
2511
2521
  let bool;
2512
2522
  for (const { combo, leaves: [leaf] } of branch) {
2513
2523
  const { name: leafName, type: leafType } = leaf;
2514
- if (leafType === SELECTOR_PSEUDO_CLASS && leafName === 'dir') {
2524
+ if (leafType === PS_CLASS_SELECTOR && leafName === 'dir') {
2515
2525
  bool = false;
2516
2526
  break;
2517
2527
  }
@@ -2874,6 +2884,9 @@ export class Finder {
2874
2884
  nodes = new Set(sortNodes(nodes));
2875
2885
  }
2876
2886
  }
2887
+ if (this.#invalidate) {
2888
+ this.#invalidateResults = new WeakMap();
2889
+ }
2877
2890
  return nodes;
2878
2891
  }
2879
2892
  };
package/src/js/matcher.js CHANGED
@@ -8,8 +8,8 @@ import { getDirectionality, getType, isNamespaceDeclared } from './utility.js';
8
8
 
9
9
  /* constants */
10
10
  import {
11
- ALPHA_NUM, ELEMENT_NODE, EMPTY, LANG_PART, NOT_SUPPORTED_ERR, REG_LANG,
12
- REG_TAG_NAME, SELECTOR_ATTR, SELECTOR_TYPE, SYNTAX_ERR
11
+ ALPHA_NUM, ATTR_SELECTOR, ELEMENT_NODE, EMPTY, LANG_PART, NOT_SUPPORTED_ERR,
12
+ SYNTAX_ERR, TYPE_SELECTOR
13
13
  } from './constant.js';
14
14
 
15
15
  /* Matcher */
@@ -294,7 +294,7 @@ export class Matcher {
294
294
  prefix: astPrefix, localName: astLocalName
295
295
  } = parseAstName(astName, node);
296
296
  if (node.ownerDocument.contentType === 'text/html' &&
297
- REG_TAG_NAME.test(localName)) {
297
+ /[A-Z][\\w-]*/i.test(localName)) {
298
298
  astPrefix = astPrefix.toLowerCase();
299
299
  astLocalName = astLocalName.toLowerCase();
300
300
  }
@@ -393,7 +393,8 @@ export class Matcher {
393
393
  }
394
394
  }
395
395
  } else if (astName) {
396
- if (REG_LANG.test(astName)) {
396
+ const reg = new RegExp(`^(?:\\*-)?${ALPHA_NUM}${LANG_PART}$`, 'i');
397
+ if (reg.test(astName)) {
397
398
  let regExtendedLang;
398
399
  if (astName.indexOf('-') > -1) {
399
400
  const [langMain, langSub, ...langRest] = astName.split('-');
@@ -462,11 +463,11 @@ export class Matcher {
462
463
  }
463
464
  let matched;
464
465
  switch (ast.type) {
465
- case SELECTOR_ATTR: {
466
+ case ATTR_SELECTOR: {
466
467
  matched = this._matchAttributeSelector(ast, node);
467
468
  break;
468
469
  }
469
- case SELECTOR_TYPE: {
470
+ case TYPE_SELECTOR: {
470
471
  matched = this._matchTypeSelector(ast, node, opt ?? {});
471
472
  break;
472
473
  }
package/src/js/parser.js CHANGED
@@ -8,12 +8,14 @@ import { getType } from './utility.js';
8
8
 
9
9
  /* constants */
10
10
  import {
11
- BIT_01, BIT_02, BIT_04, BIT_08, BIT_16, BIT_32, BIT_FFFF, BIT_HYPHEN,
12
- DUO, EMPTY, HEX, NTH, REG_HEX, REG_INVALID_SELECTOR, REG_LANG_QUOTED,
13
- REG_LOGICAL_EMPTY, REG_LOGICAL_PSEUDO, REG_SHADOW_PSEUDO, SELECTOR,
14
- SELECTOR_ATTR, SELECTOR_CLASS, SELECTOR_ID, SELECTOR_PSEUDO_CLASS,
15
- SELECTOR_PSEUDO_ELEMENT, SELECTOR_TYPE, SYNTAX_ERR, U_FFFD
11
+ ATTR_SELECTOR, BIT_01, BIT_02, BIT_04, BIT_08, BIT_16, BIT_32, BIT_FFFF,
12
+ CLASS_SELECTOR, DUO, EMPTY, HEX, HYPHEN, ID_SELECTOR, NTH, PS_CLASS_SELECTOR,
13
+ PS_ELEMENT_SELECTOR, REG_LOGICAL, SELECTOR, SYNTAX_ERR, TYPE_SELECTOR
16
14
  } from './constant.js';
15
+ const REG_LANG_QUOTED = /(:lang\(\s*("[A-Za-z\d\-*]*")\s*\))/;
16
+ const REG_LOGICAL_EMPTY = /(:(is|where)\(\s*\))/;
17
+ const REG_SHADOW_PSEUDO = /^part|slotted$/;
18
+ const U_FFFD = '\uFFFD';
17
19
 
18
20
  /**
19
21
  * unescape selector
@@ -29,7 +31,7 @@ export const unescapeSelector = (selector = '') => {
29
31
  if (item === '' && i === l - 1) {
30
32
  item = U_FFFD;
31
33
  } else {
32
- const hexExists = REG_HEX.exec(item);
34
+ const hexExists = /^([\da-f]{1,6}\s?)/i.exec(item);
33
35
  if (hexExists) {
34
36
  const [, hex] = hexExists;
35
37
  let str;
@@ -88,7 +90,7 @@ export const preprocess = (...args) => {
88
90
  throw new DOMException(`Invalid selector ${selector}`, SYNTAX_ERR);
89
91
  }
90
92
  const codePoint = postHash.codePointAt(0);
91
- if (codePoint === BIT_HYPHEN) {
93
+ if (codePoint === HYPHEN) {
92
94
  if (/^\d$/.test(postHash.substring(1, 2))) {
93
95
  throw new DOMException(`Invalid selector ${selector}`, SYNTAX_ERR);
94
96
  }
@@ -126,7 +128,7 @@ export const preprocess = (...args) => {
126
128
  export const parseSelector = selector => {
127
129
  selector = preprocess(selector);
128
130
  // invalid selectors
129
- if (REG_INVALID_SELECTOR.test(selector)) {
131
+ if (/^$|^\s*>|,\s*$/.test(selector)) {
130
132
  throw new DOMException(`Invalid selector ${selector}`, SYNTAX_ERR);
131
133
  }
132
134
  let res;
@@ -191,8 +193,8 @@ export const walkAST = (ast = {}) => {
191
193
  branches.add(node.children);
192
194
  break;
193
195
  }
194
- case SELECTOR_PSEUDO_CLASS: {
195
- if (REG_LOGICAL_PSEUDO.test(node.name)) {
196
+ case PS_CLASS_SELECTOR: {
197
+ if (REG_LOGICAL.test(node.name)) {
196
198
  info.set('hasNestedSelector', true);
197
199
  info.set('hasLogicalPseudoFunc', true);
198
200
  if (node.name === 'has') {
@@ -201,7 +203,7 @@ export const walkAST = (ast = {}) => {
201
203
  }
202
204
  break;
203
205
  }
204
- case SELECTOR_PSEUDO_ELEMENT: {
206
+ case PS_ELEMENT_SELECTOR: {
205
207
  if (REG_SHADOW_PSEUDO.test(node.name)) {
206
208
  info.set('hasNestedSelector', true);
207
209
  }
@@ -210,6 +212,7 @@ export const walkAST = (ast = {}) => {
210
212
  case NTH: {
211
213
  if (node.selector) {
212
214
  info.set('hasNestedSelector', true);
215
+ info.set('hasNthChildOfSelector', true);
213
216
  }
214
217
  break;
215
218
  }
@@ -221,12 +224,11 @@ export const walkAST = (ast = {}) => {
221
224
  if (info.get('hasNestedSelector')) {
222
225
  findAll(ast, (node, item, list) => {
223
226
  if (list) {
224
- if (node.type === SELECTOR_PSEUDO_CLASS &&
225
- REG_LOGICAL_PSEUDO.test(node.name)) {
227
+ if (node.type === PS_CLASS_SELECTOR && REG_LOGICAL.test(node.name)) {
226
228
  const itemList = list.filter(i => {
227
229
  const { name, type } = i;
228
230
  const res =
229
- type === SELECTOR_PSEUDO_CLASS && REG_LOGICAL_PSEUDO.test(name);
231
+ type === PS_CLASS_SELECTOR && REG_LOGICAL.test(name);
230
232
  return res;
231
233
  });
232
234
  for (const { children } of itemList) {
@@ -240,12 +242,12 @@ export const walkAST = (ast = {}) => {
240
242
  }
241
243
  }
242
244
  }
243
- } else if (node.type === SELECTOR_PSEUDO_ELEMENT &&
245
+ } else if (node.type === PS_ELEMENT_SELECTOR &&
244
246
  REG_SHADOW_PSEUDO.test(node.name)) {
245
247
  const itemList = list.filter(i => {
246
248
  const { name, type } = i;
247
249
  const res =
248
- type === SELECTOR_PSEUDO_ELEMENT && REG_SHADOW_PSEUDO.test(name);
250
+ type === PS_ELEMENT_SELECTOR && REG_SHADOW_PSEUDO.test(name);
249
251
  return res;
250
252
  });
251
253
  for (const { children } of itemList) {
@@ -290,12 +292,12 @@ export const sortAST = asts => {
290
292
  const arr = [...asts];
291
293
  if (arr.length > 1) {
292
294
  const order = new Map([
293
- [SELECTOR_PSEUDO_ELEMENT, BIT_01],
294
- [SELECTOR_ID, BIT_02],
295
- [SELECTOR_CLASS, BIT_04],
296
- [SELECTOR_TYPE, BIT_08],
297
- [SELECTOR_ATTR, BIT_16],
298
- [SELECTOR_PSEUDO_CLASS, BIT_32]
295
+ [PS_ELEMENT_SELECTOR, BIT_01],
296
+ [ID_SELECTOR, BIT_02],
297
+ [CLASS_SELECTOR, BIT_04],
298
+ [TYPE_SELECTOR, BIT_08],
299
+ [ATTR_SELECTOR, BIT_16],
300
+ [PS_CLASS_SELECTOR, BIT_32]
299
301
  ]);
300
302
  arr.sort((a, b) => {
301
303
  const { type: typeA } = a;
package/src/js/utility.js CHANGED
@@ -10,10 +10,15 @@ import isCustomElementName from 'is-potential-custom-element-name';
10
10
  /* constants */
11
11
  import {
12
12
  DOCUMENT_FRAGMENT_NODE, DOCUMENT_NODE, DOCUMENT_POSITION_CONTAINS,
13
- DOCUMENT_POSITION_PRECEDING, ELEMENT_NODE, REG_DIR, REG_FILTER_COMPLEX,
14
- REG_FILTER_COMPOUND, REG_FILTER_SIMPLE, REG_SHADOW_MODE, REG_TYPE_INPUT,
15
- TEXT_NODE, TYPE_FROM, TYPE_TO, WALKER_FILTER
13
+ DOCUMENT_POSITION_PRECEDING, ELEMENT_NODE, N_TH, LOGICAL_COMPLEX,
14
+ LOGICAL_COMPOUND, PSEUDO_CLASS, REG_INPUT_TYPE, TEXT_NODE, TYPE_FROM,
15
+ TYPE_TO, WALKER_FILTER
16
16
  } from './constant.js';
17
+ const REG_LOGICAL_COMPLEX =
18
+ new RegExp(`:(?!${PSEUDO_CLASS}|${N_TH}|${LOGICAL_COMPLEX})`);
19
+ const REG_LOGICAL_COMPOUND =
20
+ new RegExp(`:(?!${PSEUDO_CLASS}|${N_TH}|${LOGICAL_COMPOUND})`);
21
+ const REG_WO_LOGICAL = new RegExp(`:(?!${PSEUDO_CLASS}|${N_TH})`);
17
22
 
18
23
  /**
19
24
  * get type
@@ -171,7 +176,7 @@ export const isInShadowTree = node => {
171
176
  while (refNode) {
172
177
  const { host, mode, nodeType, parentNode } = refNode;
173
178
  if (host && mode && nodeType === DOCUMENT_FRAGMENT_NODE &&
174
- REG_SHADOW_MODE.test(mode)) {
179
+ /^(?:close|open)$/.test(mode)) {
175
180
  bool = true;
176
181
  break;
177
182
  }
@@ -221,7 +226,7 @@ export const getDirectionality = node => {
221
226
  if (node.nodeType === ELEMENT_NODE) {
222
227
  const { dir: nodeDir, localName, parentNode } = node;
223
228
  const { getEmbeddingLevels } = bidiFactory();
224
- if (REG_DIR.test(nodeDir)) {
229
+ if (/^(?:ltr|rtl)$/.test(nodeDir)) {
225
230
  res = nodeDir;
226
231
  } else if (nodeDir === 'auto') {
227
232
  let text;
@@ -253,7 +258,7 @@ export const getDirectionality = node => {
253
258
  text = itemTextContent.trim();
254
259
  } else if (itemNodeType === ELEMENT_NODE) {
255
260
  if (!/^(?:bdi|script|style|textarea)$/.test(itemLocalName) &&
256
- (!itemDir || !REG_DIR.test(itemDir))) {
261
+ (!itemDir || !/^(?:ltr|rtl)$/.test(itemDir))) {
257
262
  if (itemLocalName === 'slot') {
258
263
  text = getSlottedTextContent(item);
259
264
  } else {
@@ -394,7 +399,7 @@ export const isFocusVisible = node => {
394
399
  const { localName, type } = node;
395
400
  switch (localName) {
396
401
  case 'input': {
397
- if (!type || REG_TYPE_INPUT.test(type)) {
402
+ if (!type || REG_INPUT_TYPE.test(type)) {
398
403
  res = true;
399
404
  }
400
405
  break;
@@ -596,21 +601,16 @@ export const filterSelector = (selector, opt = {}) => {
596
601
  // filter pseudo-classes
597
602
  if (selector.includes(':')) {
598
603
  const { complex, descend } = opt;
599
- let reg;
600
604
  if (/:(?:is|not)\(/.test(selector)) {
601
605
  if (complex) {
602
- reg = REG_FILTER_COMPLEX;
606
+ return !REG_LOGICAL_COMPLEX.test(selector);
603
607
  } else {
604
- reg = REG_FILTER_COMPOUND;
608
+ return !REG_LOGICAL_COMPOUND.test(selector);
605
609
  }
606
- } else {
607
- if (descend) {
608
- return false;
609
- }
610
- reg = REG_FILTER_SIMPLE;
611
- }
612
- if (reg.test(selector)) {
610
+ } else if (descend) {
613
611
  return false;
612
+ } else {
613
+ return !REG_WO_LOGICAL.test(selector);
614
614
  }
615
615
  }
616
616
  return true;