@remote-dom/polyfill 1.5.1 → 1.6.0

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.
Files changed (304) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/README.md +51 -3
  3. package/build/cjs/Attr.cjs +33 -4
  4. package/build/cjs/CharacterData.cjs +10 -0
  5. package/build/cjs/ChildNode.cjs +79 -13
  6. package/build/cjs/Comment.cjs +1 -1
  7. package/build/cjs/CustomElementRegistry.cjs +27 -2
  8. package/build/cjs/CustomEvent.cjs +2 -2
  9. package/build/cjs/Document.cjs +56 -32
  10. package/build/cjs/DocumentFragment.cjs +6 -2
  11. package/build/cjs/Element.cjs +68 -8
  12. package/build/cjs/Event.cjs +29 -27
  13. package/build/cjs/EventTarget.cjs +128 -58
  14. package/build/cjs/HTMLTemplateElement.cjs +1 -0
  15. package/build/cjs/MutationObserver.cjs +167 -1
  16. package/build/cjs/NamedNodeMap.cjs +82 -45
  17. package/build/cjs/Node.cjs +47 -11
  18. package/build/cjs/NodeList.cjs +9 -1
  19. package/build/cjs/ParentNode.cjs +319 -77
  20. package/build/cjs/SVGElement.cjs +1 -1
  21. package/build/cjs/Text.cjs +1 -1
  22. package/build/cjs/Window.cjs +131 -38
  23. package/build/cjs/attribute-reactions.cjs +20 -0
  24. package/build/cjs/constants.cjs +48 -24
  25. package/build/cjs/custom-element-reactions.cjs +52 -0
  26. package/build/cjs/dom-exception.cjs +15 -0
  27. package/build/cjs/hook-effects.cjs +51 -0
  28. package/build/cjs/names.cjs +61 -0
  29. package/build/cjs/selectors.cjs +213 -122
  30. package/build/cjs/serialization.cjs +168 -65
  31. package/build/cjs/shared.cjs +177 -28
  32. package/build/esm/Attr.mjs +34 -5
  33. package/build/esm/CharacterData.mjs +10 -0
  34. package/build/esm/ChildNode.mjs +76 -15
  35. package/build/esm/Comment.mjs +2 -2
  36. package/build/esm/CustomElementRegistry.mjs +27 -2
  37. package/build/esm/CustomEvent.mjs +2 -2
  38. package/build/esm/Document.mjs +60 -35
  39. package/build/esm/DocumentFragment.mjs +7 -3
  40. package/build/esm/Element.mjs +69 -9
  41. package/build/esm/Event.mjs +27 -27
  42. package/build/esm/EventTarget.mjs +130 -60
  43. package/build/esm/HTMLTemplateElement.mjs +2 -1
  44. package/build/esm/MutationObserver.mjs +166 -2
  45. package/build/esm/NamedNodeMap.mjs +83 -46
  46. package/build/esm/Node.mjs +48 -12
  47. package/build/esm/NodeList.mjs +9 -1
  48. package/build/esm/ParentNode.mjs +322 -81
  49. package/build/esm/SVGElement.mjs +2 -2
  50. package/build/esm/Text.mjs +2 -2
  51. package/build/esm/Window.mjs +132 -39
  52. package/build/esm/attribute-reactions.mjs +16 -0
  53. package/build/esm/constants.mjs +28 -23
  54. package/build/esm/custom-element-reactions.mjs +47 -0
  55. package/build/esm/dom-exception.mjs +11 -0
  56. package/build/esm/hook-effects.mjs +46 -0
  57. package/build/esm/names.mjs +54 -0
  58. package/build/esm/selectors.mjs +202 -124
  59. package/build/esm/serialization.mjs +169 -66
  60. package/build/esm/shared.mjs +173 -30
  61. package/build/esnext/Attr.esnext +45 -5
  62. package/build/esnext/CharacterData.esnext +10 -0
  63. package/build/esnext/ChildNode.esnext +84 -15
  64. package/build/esnext/Comment.esnext +2 -2
  65. package/build/esnext/CustomElementRegistry.esnext +42 -0
  66. package/build/esnext/CustomEvent.esnext +2 -2
  67. package/build/esnext/Document.esnext +69 -32
  68. package/build/esnext/DocumentFragment.esnext +6 -2
  69. package/build/esnext/Element.esnext +78 -9
  70. package/build/esnext/Event.esnext +36 -37
  71. package/build/esnext/EventTarget.esnext +133 -61
  72. package/build/esnext/HTMLTemplateElement.esnext +3 -1
  73. package/build/esnext/MutationObserver.esnext +163 -2
  74. package/build/esnext/NamedNodeMap.esnext +110 -42
  75. package/build/esnext/Node.esnext +50 -12
  76. package/build/esnext/NodeList.esnext +1 -1
  77. package/build/esnext/ParentNode.esnext +352 -79
  78. package/build/esnext/SVGElement.esnext +2 -2
  79. package/build/esnext/Text.esnext +2 -2
  80. package/build/esnext/Window.esnext +120 -16
  81. package/build/esnext/attribute-reactions.esnext +21 -0
  82. package/build/esnext/constants.esnext +34 -21
  83. package/build/esnext/custom-element-reactions.esnext +49 -0
  84. package/build/esnext/dom-exception.esnext +11 -0
  85. package/build/esnext/hook-effects.esnext +46 -0
  86. package/build/esnext/names.esnext +61 -0
  87. package/build/esnext/selectors.esnext +202 -100
  88. package/build/esnext/serialization.esnext +171 -48
  89. package/build/esnext/shared.esnext +181 -34
  90. package/build/tsconfig.tsbuildinfo +1 -1
  91. package/build/typescript/Attr.d.ts +6 -4
  92. package/build/typescript/Attr.d.ts.map +1 -1
  93. package/build/typescript/CharacterData.d.ts.map +1 -1
  94. package/build/typescript/ChildNode.d.ts +5 -0
  95. package/build/typescript/ChildNode.d.ts.map +1 -1
  96. package/build/typescript/ClipboardEvent.d.ts.map +1 -1
  97. package/build/typescript/Comment.d.ts +2 -2
  98. package/build/typescript/Comment.d.ts.map +1 -1
  99. package/build/typescript/CustomElementRegistry.d.ts +1 -0
  100. package/build/typescript/CustomElementRegistry.d.ts.map +1 -1
  101. package/build/typescript/CustomEvent.d.ts.map +1 -1
  102. package/build/typescript/Document.d.ts +10 -5
  103. package/build/typescript/Document.d.ts.map +1 -1
  104. package/build/typescript/DocumentFragment.d.ts +2 -1
  105. package/build/typescript/DocumentFragment.d.ts.map +1 -1
  106. package/build/typescript/Element.d.ts +16 -7
  107. package/build/typescript/Element.d.ts.map +1 -1
  108. package/build/typescript/ErrorEvent.d.ts.map +1 -1
  109. package/build/typescript/Event.d.ts +9 -10
  110. package/build/typescript/Event.d.ts.map +1 -1
  111. package/build/typescript/EventTarget.d.ts +20 -3
  112. package/build/typescript/EventTarget.d.ts.map +1 -1
  113. package/build/typescript/FocusEvent.d.ts.map +1 -1
  114. package/build/typescript/HTMLTemplateElement.d.ts.map +1 -1
  115. package/build/typescript/MutationObserver.d.ts +42 -0
  116. package/build/typescript/MutationObserver.d.ts.map +1 -1
  117. package/build/typescript/NamedNodeMap.d.ts +5 -3
  118. package/build/typescript/NamedNodeMap.d.ts.map +1 -1
  119. package/build/typescript/Node.d.ts +4 -3
  120. package/build/typescript/Node.d.ts.map +1 -1
  121. package/build/typescript/NodeList.d.ts +3 -2
  122. package/build/typescript/NodeList.d.ts.map +1 -1
  123. package/build/typescript/ParentNode.d.ts +29 -10
  124. package/build/typescript/ParentNode.d.ts.map +1 -1
  125. package/build/typescript/PromiseRejectionEvent.d.ts.map +1 -1
  126. package/build/typescript/SVGElement.d.ts +1 -1
  127. package/build/typescript/SVGElement.d.ts.map +1 -1
  128. package/build/typescript/Text.d.ts +1 -1
  129. package/build/typescript/Text.d.ts.map +1 -1
  130. package/build/typescript/ToggleEvent.d.ts.map +1 -1
  131. package/build/typescript/Window.d.ts +5 -1
  132. package/build/typescript/Window.d.ts.map +1 -1
  133. package/build/typescript/attribute-reactions.d.ts +4 -0
  134. package/build/typescript/attribute-reactions.d.ts.map +1 -0
  135. package/build/typescript/constants.d.ts +27 -18
  136. package/build/typescript/constants.d.ts.map +1 -1
  137. package/build/typescript/custom-element-reactions.d.ts +6 -0
  138. package/build/typescript/custom-element-reactions.d.ts.map +1 -0
  139. package/build/typescript/dom-exception.d.ts +2 -0
  140. package/build/typescript/dom-exception.d.ts.map +1 -0
  141. package/build/typescript/extensions.d.ts +4 -0
  142. package/build/typescript/extensions.d.ts.map +1 -0
  143. package/build/typescript/hook-effects.d.ts +6 -0
  144. package/build/typescript/hook-effects.d.ts.map +1 -0
  145. package/build/typescript/hooks.d.ts +3 -3
  146. package/build/typescript/hooks.d.ts.map +1 -1
  147. package/build/typescript/index.d.ts +1 -0
  148. package/build/typescript/index.d.ts.map +1 -1
  149. package/build/typescript/names.d.ts +11 -0
  150. package/build/typescript/names.d.ts.map +1 -0
  151. package/build/typescript/selectors.d.ts +21 -22
  152. package/build/typescript/selectors.d.ts.map +1 -1
  153. package/build/typescript/serialization.d.ts.map +1 -1
  154. package/build/typescript/shared.d.ts +10 -0
  155. package/build/typescript/shared.d.ts.map +1 -1
  156. package/build/typescript/tests/ChildNode.test.d.ts +2 -0
  157. package/build/typescript/tests/ChildNode.test.d.ts.map +1 -0
  158. package/build/typescript/tests/MutationObserver.test.d.ts +2 -0
  159. package/build/typescript/tests/MutationObserver.test.d.ts.map +1 -0
  160. package/build/typescript/tests/adopt-inserted-subtrees.test.d.ts +2 -0
  161. package/build/typescript/tests/adopt-inserted-subtrees.test.d.ts.map +1 -0
  162. package/build/typescript/tests/attr-mutation.test.d.ts +2 -0
  163. package/build/typescript/tests/attr-mutation.test.d.ts.map +1 -0
  164. package/build/typescript/tests/child-node-before-after.test.d.ts +2 -0
  165. package/build/typescript/tests/child-node-before-after.test.d.ts.map +1 -0
  166. package/build/typescript/tests/child-node-replace-with.test.d.ts +2 -0
  167. package/build/typescript/tests/child-node-replace-with.test.d.ts.map +1 -0
  168. package/build/typescript/tests/clone-import-namespaces.test.d.ts +2 -0
  169. package/build/typescript/tests/clone-import-namespaces.test.d.ts.map +1 -0
  170. package/build/typescript/tests/constants.test.d.ts +2 -0
  171. package/build/typescript/tests/constants.test.d.ts.map +1 -0
  172. package/build/typescript/tests/create-element-namespaces.test.d.ts +2 -0
  173. package/build/typescript/tests/create-element-namespaces.test.d.ts.map +1 -0
  174. package/build/typescript/tests/create-text-node.test.d.ts +2 -0
  175. package/build/typescript/tests/create-text-node.test.d.ts.map +1 -0
  176. package/build/typescript/tests/custom-element-reactions.test.d.ts +2 -0
  177. package/build/typescript/tests/custom-element-reactions.test.d.ts.map +1 -0
  178. package/build/typescript/tests/custom-element-registry.test.d.ts +2 -0
  179. package/build/typescript/tests/custom-element-registry.test.d.ts.map +1 -0
  180. package/build/typescript/tests/custom-event.test.d.ts +2 -0
  181. package/build/typescript/tests/custom-event.test.d.ts.map +1 -0
  182. package/build/typescript/tests/document-clone-import-contract.test.d.ts +2 -0
  183. package/build/typescript/tests/document-clone-import-contract.test.d.ts.map +1 -0
  184. package/build/typescript/tests/document.test.d.ts +2 -0
  185. package/build/typescript/tests/document.test.d.ts.map +1 -0
  186. package/build/typescript/tests/dom-mutation-selector-errors.test.d.ts +2 -0
  187. package/build/typescript/tests/dom-mutation-selector-errors.test.d.ts.map +1 -0
  188. package/build/typescript/tests/event-dispatch.test.d.ts +2 -0
  189. package/build/typescript/tests/event-dispatch.test.d.ts.map +1 -0
  190. package/build/typescript/tests/event-target.test.d.ts +2 -0
  191. package/build/typescript/tests/event-target.test.d.ts.map +1 -0
  192. package/build/typescript/tests/event.test.d.ts +2 -0
  193. package/build/typescript/tests/event.test.d.ts.map +1 -0
  194. package/build/typescript/tests/extensions.test.d.ts +2 -0
  195. package/build/typescript/tests/extensions.test.d.ts.map +1 -0
  196. package/build/typescript/tests/get-elements-by-tag-name.test.d.ts +2 -0
  197. package/build/typescript/tests/get-elements-by-tag-name.test.d.ts.map +1 -0
  198. package/build/typescript/tests/getElementsByClassName.test.d.ts +2 -0
  199. package/build/typescript/tests/getElementsByClassName.test.d.ts.map +1 -0
  200. package/build/typescript/tests/hooks.test.d.ts +2 -0
  201. package/build/typescript/tests/hooks.test.d.ts.map +1 -0
  202. package/build/typescript/tests/inner-html-parsing.test.d.ts +2 -0
  203. package/build/typescript/tests/inner-html-parsing.test.d.ts.map +1 -0
  204. package/build/typescript/tests/is-default-namespace.test.d.ts +2 -0
  205. package/build/typescript/tests/is-default-namespace.test.d.ts.map +1 -0
  206. package/build/typescript/tests/name-and-namespace-normalization.test.d.ts +2 -0
  207. package/build/typescript/tests/name-and-namespace-normalization.test.d.ts.map +1 -0
  208. package/build/typescript/tests/named-node-map.test.d.ts +2 -0
  209. package/build/typescript/tests/named-node-map.test.d.ts.map +1 -0
  210. package/build/typescript/tests/namespaced-attribute-reactions.test.d.ts +2 -0
  211. package/build/typescript/tests/namespaced-attribute-reactions.test.d.ts.map +1 -0
  212. package/build/typescript/tests/node.test.d.ts +2 -0
  213. package/build/typescript/tests/node.test.d.ts.map +1 -0
  214. package/build/typescript/tests/parent-node-mutation-return-values.test.d.ts +2 -0
  215. package/build/typescript/tests/parent-node-mutation-return-values.test.d.ts.map +1 -0
  216. package/build/typescript/tests/parent-node-mutation-validation.test.d.ts +2 -0
  217. package/build/typescript/tests/parent-node-mutation-validation.test.d.ts.map +1 -0
  218. package/build/typescript/tests/parent-node.test.d.ts +2 -0
  219. package/build/typescript/tests/parent-node.test.d.ts.map +1 -0
  220. package/build/typescript/tests/serialization-template.test.d.ts +2 -0
  221. package/build/typescript/tests/serialization-template.test.d.ts.map +1 -0
  222. package/build/typescript/tests/slot-attribute.test.d.ts +2 -0
  223. package/build/typescript/tests/slot-attribute.test.d.ts.map +1 -0
  224. package/build/typescript/tests/text-content.test.d.ts +2 -0
  225. package/build/typescript/tests/text-content.test.d.ts.map +1 -0
  226. package/build/typescript/tests/toggle-attribute.test.d.ts +2 -0
  227. package/build/typescript/tests/toggle-attribute.test.d.ts.map +1 -0
  228. package/build/typescript/tests/wide-tree-traversal.test.d.ts +2 -0
  229. package/build/typescript/tests/wide-tree-traversal.test.d.ts.map +1 -0
  230. package/build/typescript/tests/window-set-global.test.d.ts +2 -0
  231. package/build/typescript/tests/window-set-global.test.d.ts.map +1 -0
  232. package/package.json +1 -1
  233. package/source/Attr.ts +60 -8
  234. package/source/CharacterData.ts +13 -0
  235. package/source/ChildNode.ts +107 -14
  236. package/source/Comment.ts +2 -2
  237. package/source/CustomElementRegistry.ts +56 -0
  238. package/source/CustomEvent.ts +2 -2
  239. package/source/Document.ts +104 -38
  240. package/source/DocumentFragment.ts +12 -2
  241. package/source/Element.ts +129 -14
  242. package/source/Event.ts +34 -32
  243. package/source/EventTarget.ts +203 -79
  244. package/source/HTMLTemplateElement.ts +2 -1
  245. package/source/MutationObserver.ts +279 -1
  246. package/source/NamedNodeMap.ts +141 -60
  247. package/source/Node.ts +70 -14
  248. package/source/NodeList.ts +13 -3
  249. package/source/ParentNode.ts +500 -88
  250. package/source/SVGElement.ts +2 -2
  251. package/source/Text.ts +2 -2
  252. package/source/Window.ts +156 -17
  253. package/source/attribute-reactions.ts +32 -0
  254. package/source/constants.ts +40 -17
  255. package/source/custom-element-reactions.ts +64 -0
  256. package/source/dom-exception.ts +14 -0
  257. package/source/extensions.ts +4 -0
  258. package/source/hook-effects.ts +56 -0
  259. package/source/hooks.ts +11 -3
  260. package/source/index.ts +1 -0
  261. package/source/names.ts +94 -0
  262. package/source/selectors.ts +301 -138
  263. package/source/serialization.ts +224 -66
  264. package/source/shared.ts +269 -40
  265. package/source/tests/ChildNode.test.ts +178 -0
  266. package/source/tests/MutationObserver.test.ts +303 -0
  267. package/source/tests/adopt-inserted-subtrees.test.ts +584 -0
  268. package/source/tests/attr-mutation.test.ts +112 -0
  269. package/source/tests/child-node-before-after.test.ts +421 -0
  270. package/source/tests/child-node-replace-with.test.ts +251 -0
  271. package/source/tests/clone-import-namespaces.test.ts +365 -0
  272. package/source/tests/constants.test.ts +46 -0
  273. package/source/tests/create-element-namespaces.test.ts +142 -0
  274. package/source/tests/create-text-node.test.ts +38 -0
  275. package/source/tests/custom-element-reactions.test.ts +600 -0
  276. package/source/tests/custom-element-registry.test.ts +137 -0
  277. package/source/tests/custom-event.test.ts +26 -0
  278. package/source/tests/document-clone-import-contract.test.ts +77 -0
  279. package/source/tests/document.test.ts +122 -0
  280. package/source/tests/dom-mutation-selector-errors.test.ts +484 -0
  281. package/source/tests/event-dispatch.test.ts +270 -0
  282. package/source/tests/event-target.test.ts +208 -0
  283. package/source/tests/event.test.ts +98 -0
  284. package/source/tests/extensions.test.ts +159 -0
  285. package/source/tests/get-elements-by-tag-name.test.ts +79 -0
  286. package/source/tests/getElementsByClassName.test.ts +109 -0
  287. package/source/tests/hooks.test.ts +43 -0
  288. package/source/tests/inner-html-parsing.test.ts +461 -0
  289. package/source/tests/is-default-namespace.test.ts +111 -0
  290. package/source/tests/name-and-namespace-normalization.test.ts +319 -0
  291. package/source/tests/named-node-map.test.ts +122 -0
  292. package/source/tests/namespaced-attribute-reactions.test.ts +141 -0
  293. package/source/tests/node.test.ts +56 -0
  294. package/source/tests/parent-node-mutation-return-values.test.ts +63 -0
  295. package/source/tests/parent-node-mutation-validation.test.ts +362 -0
  296. package/source/tests/parent-node.test.ts +169 -0
  297. package/source/tests/selectors.test.ts +533 -22
  298. package/source/tests/serialization-template.test.ts +232 -0
  299. package/source/tests/slot-attribute.test.ts +133 -0
  300. package/source/tests/text-content.test.ts +440 -0
  301. package/source/tests/toggle-attribute.test.ts +113 -0
  302. package/source/tests/wide-tree-traversal.test.ts +699 -0
  303. package/source/tests/window-set-global.test.ts +321 -0
  304. package/tsconfig.json +1 -1
@@ -1,43 +1,112 @@
1
- import {CHILD, NEXT, PARENT, PREV} from './constants.ts';
1
+ import {
2
+ CHILD,
3
+ NEXT,
4
+ PARENT,
5
+ PREV,
6
+ HTML_NAMESPACE,
7
+ asciiLowercase,
8
+ splitOnASCIIWhitespace,
9
+ } from './constants.ts';
2
10
  import {isElementNode} from './shared.ts';
11
+ import {NodeList} from './NodeList.ts';
12
+ import {createDOMException} from './dom-exception.ts';
3
13
 
4
14
  import type {Node} from './Node.ts';
5
15
  import type {Element} from './Element.ts';
6
16
  import type {ParentNode} from './ParentNode.ts';
7
17
 
8
- const enum Combinator {
9
- Descendant,
10
- Child,
11
- Sibling,
12
- Adjacent,
13
- Inner,
14
- }
18
+ export const COMBINATOR_DESCENDANT = 0;
19
+ export const COMBINATOR_CHILD = 1;
20
+ export const COMBINATOR_SIBLING = 2;
21
+ export const COMBINATOR_ADJACENT = 3;
22
+ export const COMBINATOR_INNER = 4;
15
23
 
16
- const enum MatcherType {
17
- Unknown,
18
- Element,
19
- Id,
20
- Class,
21
- Attribute,
22
- Pseudo,
23
- Function,
24
- }
24
+ export type Combinator =
25
+ | typeof COMBINATOR_DESCENDANT
26
+ | typeof COMBINATOR_CHILD
27
+ | typeof COMBINATOR_SIBLING
28
+ | typeof COMBINATOR_ADJACENT
29
+ | typeof COMBINATOR_INNER;
30
+
31
+ export const MATCHER_UNKNOWN = 0;
32
+ export const MATCHER_ELEMENT = 1;
33
+ export const MATCHER_ID = 2;
34
+ export const MATCHER_CLASS = 3;
35
+ export const MATCHER_ATTRIBUTE = 4;
36
+ export const MATCHER_PSEUDO = 5;
37
+ export const MATCHER_FUNCTION = 6;
38
+ export const MATCHER_SCOPE = 7;
25
39
 
26
- interface Part {
40
+ export type MatcherType =
41
+ | typeof MATCHER_UNKNOWN
42
+ | typeof MATCHER_ELEMENT
43
+ | typeof MATCHER_ID
44
+ | typeof MATCHER_CLASS
45
+ | typeof MATCHER_ATTRIBUTE
46
+ | typeof MATCHER_PSEUDO
47
+ | typeof MATCHER_FUNCTION
48
+ | typeof MATCHER_SCOPE;
49
+
50
+ export interface Part {
27
51
  combinator: Combinator;
28
52
  matchers: Matcher[];
29
53
  }
30
54
 
31
- interface Matcher {
55
+ export interface Matcher {
32
56
  type: MatcherType;
33
57
  name: string;
34
58
  value?: string;
35
59
  }
36
60
 
37
- const ELEMENT_SELECTOR_TEST = /[a-z]/;
61
+ const SUPPORTED_IDENTIFIER_TEST =
62
+ /^(?:--|-?[A-Za-z_\u0080-\u{10FFFF}])[A-Za-z0-9_\u0080-\u{10FFFF}-]*$/u;
63
+
64
+ function readFunctionArgument(
65
+ selector: string,
66
+ start: number,
67
+ ): [string, number] {
68
+ let depth = 1;
69
+ let quote: string | null = null;
70
+
71
+ for (let index = start; index < selector.length; index++) {
72
+ const character = selector[index]!;
73
+
74
+ if (quote) {
75
+ if (character === '\\') {
76
+ index++;
77
+ } else if (character === quote) {
78
+ quote = null;
79
+ }
80
+ continue;
81
+ }
82
+
83
+ if (character === '"' || character === "'") {
84
+ quote = character;
85
+ } else if (character === '(') {
86
+ depth++;
87
+ } else if (character === ')' && --depth === 0) {
88
+ return [selector.slice(start, index), index + 1];
89
+ }
90
+ }
91
+
92
+ return [selector.slice(start), selector.length];
93
+ }
94
+
95
+ function throwSelectorSyntaxError(selector: string): never {
96
+ throw createDOMException(
97
+ `Invalid or unsupported selector: "${selector}"`,
98
+ 'SyntaxError',
99
+ );
100
+ }
38
101
 
39
- export function querySelector(within: ParentNode, selector: string) {
40
- const parts = parseSelector(selector);
102
+ export function querySelector(
103
+ within: ParentNode,
104
+ selector: string | Matcher[],
105
+ ): Element | null {
106
+ const parts: Part[] =
107
+ typeof selector === 'string'
108
+ ? parseSelector(selector)
109
+ : [{combinator: COMBINATOR_INNER, matchers: selector}];
41
110
  let result: Element | null = null;
42
111
 
43
112
  const child = within[CHILD];
@@ -50,9 +119,15 @@ export function querySelector(within: ParentNode, selector: string) {
50
119
  return result;
51
120
  }
52
121
 
53
- export function querySelectorAll(within: ParentNode, selector: string) {
54
- const parts = parseSelector(selector);
55
- const results: Element[] = [];
122
+ export function querySelectorAll(
123
+ within: ParentNode,
124
+ selector: string | Matcher[],
125
+ ): NodeList<Element> {
126
+ const parts: Part[] =
127
+ typeof selector === 'string'
128
+ ? parseSelector(selector)
129
+ : [{combinator: COMBINATOR_INNER, matchers: selector}];
130
+ const results = new NodeList<Element>();
56
131
 
57
132
  const child = within[CHILD];
58
133
  if (child && parts[0]!.matchers.length) {
@@ -63,108 +138,221 @@ export function querySelectorAll(within: ParentNode, selector: string) {
63
138
  return results;
64
139
  }
65
140
 
66
- export function parseSelector(selector: string) {
67
- let part: Part = {combinator: Combinator.Inner, matchers: []};
141
+ export function parseSelector(
142
+ selector: string,
143
+ insideHas = false,
144
+ allowLeadingCombinator = false,
145
+ ) {
146
+ let part: Part = {combinator: COMBINATOR_INNER, matchers: []};
68
147
  const parts = [part];
69
148
  const tokenizer =
70
- /\s*?([>\s+~]?)\s*?(?:(?:\[\s*([^\]=]+)(?:=(['"])(.*?)\3)?\s*\])|([#.]?)([^\s#.[>:+~]+)|:(\w+)(?:\((.*?)\))?)/gi;
149
+ /[\t\n\f\r ]*?([>\t\n\f\r +~]?)[\t\n\f\r ]*?(?:(?:\[[\t\n\f\r ]*([^\]=\t\n\f\r ]+)[\t\n\f\r ]*(?:=[\t\n\f\r ]*(?:(['"])(.*?)\3|([^\]\t\n\f\r ]+)))?[\t\n\f\r ]*(?:\]|$))|([#.]?)([^\t\n\f\r #.[>:+~()]+)|:(\w+)(\()?)/gi;
150
+ const normalizedSelector = selector.replace(
151
+ /^[\t\n\f\r ]+|[\t\n\f\r ]+$/g,
152
+ '',
153
+ );
154
+ if (normalizedSelector === '') throwSelectorSyntaxError(selector);
155
+
156
+ let consumed = 0;
71
157
  let token;
72
- while ((token = tokenizer.exec(selector))) {
73
- // [1]: ancestor/parent/sibling/adjacent
158
+ while ((token = tokenizer.exec(normalizedSelector))) {
159
+ if (token.index !== consumed) throwSelectorSyntaxError(selector);
160
+
161
+ // [1]: ancestor/ parent/ sibling/ adjacent
74
162
  // [2]: attribute name
75
- // [4]: attribute value
76
- // [5]: id/class sigil
77
- // [6]: id/class name
78
- // [7]: :pseudo/:function() name
79
- // [8]: :function(argument) value
163
+ // [3]/[4]: quoted attribute value
164
+ // [5]: unquoted attribute value
165
+ // [6]: id/class sigil
166
+ // [7]: id/class/type name
167
+ // [8]: :pseudo/:function() name
168
+ // [9]: :function opening parenthesis
80
169
  if (token[1]) {
81
- // Update the combinator on the (now parent) Part:
82
- if (token[1] === '>') part.combinator = Combinator.Child;
83
- else if (token[1] === '+') part.combinator = Combinator.Adjacent;
84
- else if (token[1] === '~') part.combinator = Combinator.Sibling;
85
- else part.combinator = Combinator.Descendant;
86
- // Add a new Part for the next selector parts:
87
- part = {combinator: Combinator.Inner, matchers: []};
170
+ if (
171
+ part.matchers.length === 0 &&
172
+ !(allowLeadingCombinator && parts.length === 1)
173
+ ) {
174
+ throwSelectorSyntaxError(selector);
175
+ }
176
+
177
+ if (token[1] === '>') part.combinator = COMBINATOR_CHILD;
178
+ else if (token[1] === '+') part.combinator = COMBINATOR_ADJACENT;
179
+ else if (token[1] === '~') part.combinator = COMBINATOR_SIBLING;
180
+ else part.combinator = COMBINATOR_DESCENDANT;
181
+ part = {combinator: COMBINATOR_INNER, matchers: []};
88
182
  parts.push(part);
89
183
  }
90
184
 
91
- let type = MatcherType.Unknown;
185
+ const name = token[8] ? asciiLowercase(token[8]) : (token[2] || token[7])!;
186
+ const isTypeSelector = token[7] != null && !token[6];
187
+ if (isTypeSelector && part.matchers.length > 0) {
188
+ throwSelectorSyntaxError(selector);
189
+ }
190
+
191
+ let type: MatcherType;
192
+ let value = token[4] ?? token[5] ?? token[7];
92
193
  if (token[2]) {
93
- type = MatcherType.Attribute;
94
- } else if (token[5]) {
95
- type = token[5] === '#' ? MatcherType.Id : MatcherType.Class;
96
- } else if (token[7]) {
97
- type = token[8] == null ? MatcherType.Pseudo : MatcherType.Function;
194
+ if (!SUPPORTED_IDENTIFIER_TEST.test(name)) {
195
+ throwSelectorSyntaxError(selector);
196
+ }
197
+
198
+ const unquotedValue = token[5];
199
+ const openingQuote = unquotedValue?.[0];
200
+ if (token[3] == null && (openingQuote === '"' || openingQuote === "'")) {
201
+ const valueOffset = token[0].lastIndexOf(unquotedValue!);
202
+ value = normalizedSelector.slice(token.index + valueOffset + 1);
203
+ tokenizer.lastIndex = normalizedSelector.length;
204
+ } else if (
205
+ unquotedValue != null &&
206
+ !SUPPORTED_IDENTIFIER_TEST.test(unquotedValue)
207
+ ) {
208
+ throwSelectorSyntaxError(selector);
209
+ }
210
+ type = MATCHER_ATTRIBUTE;
98
211
  } else if (token[6]) {
99
- if (token[6] === '*') {
100
- type = MatcherType.Unknown; // Universal selector matches all
101
- } else if (ELEMENT_SELECTOR_TEST.test(token[6])) {
102
- type = MatcherType.Element;
212
+ if (!SUPPORTED_IDENTIFIER_TEST.test(name)) {
213
+ throwSelectorSyntaxError(selector);
103
214
  }
215
+ type = token[6] === '#' ? MATCHER_ID : MATCHER_CLASS;
216
+ } else if (token[8]) {
217
+ type = token[9] == null ? MATCHER_PSEUDO : MATCHER_FUNCTION;
218
+ } else if (token[7] === '*') {
219
+ type = MATCHER_UNKNOWN;
220
+ } else if (token[7] && SUPPORTED_IDENTIFIER_TEST.test(token[7])) {
221
+ type = MATCHER_ELEMENT;
222
+ } else {
223
+ throwSelectorSyntaxError(selector);
104
224
  }
105
- part.matchers.push({
106
- type,
107
- name: (token[2] || token[6] || token[7])!,
108
- value: token[4] ?? token[6] ?? token[8],
109
- });
225
+
226
+ if (token[9]) {
227
+ [value, tokenizer.lastIndex] = readFunctionArgument(
228
+ normalizedSelector,
229
+ tokenizer.lastIndex,
230
+ );
231
+
232
+ if (name !== 'has' && name !== 'not') {
233
+ throwSelectorSyntaxError(selector);
234
+ }
235
+ if (name === 'has' && insideHas) throwSelectorSyntaxError(selector);
236
+ parseSelector(value, insideHas || name === 'has', name === 'has');
237
+ } else if (type === MATCHER_PSEUDO) {
238
+ throwSelectorSyntaxError(selector);
239
+ }
240
+
241
+ part.matchers.push({type, name, value});
242
+ consumed = tokenizer.lastIndex;
110
243
  }
244
+
245
+ if (consumed !== normalizedSelector.length || part.matchers.length === 0) {
246
+ throwSelectorSyntaxError(selector);
247
+ }
248
+
111
249
  return parts;
112
250
  }
113
251
 
114
252
  function matchesSelector(element: Element, selector: string) {
115
253
  const parsed = parseSelector(selector);
116
- let part: Part | undefined;
117
- while ((part = parsed.pop())) {
118
- if (!matchesSelectorPart(element, part)) return false;
254
+ return parsed[0]?.matchers.length
255
+ ? matchesSelectorRecursive(element, parsed)
256
+ : false;
257
+ }
258
+
259
+ function matchesRelativeSelector(scope: Element, selector: string) {
260
+ const parts = parseSelector(selector, true, true);
261
+ const first = parts[0]!;
262
+ if (parts.length === 1 && first.matchers.length === 0) return false;
263
+
264
+ let leadingCombinator = COMBINATOR_DESCENDANT;
265
+ const scopeMatcher: Matcher = {type: MATCHER_SCOPE, name: ':scope'};
266
+ if (first.matchers.length === 0) {
267
+ leadingCombinator = first.combinator;
268
+ first.matchers.push(scopeMatcher);
269
+ } else {
270
+ parts.unshift({
271
+ combinator: COMBINATOR_DESCENDANT,
272
+ matchers: [scopeMatcher],
273
+ });
119
274
  }
120
- return true;
275
+
276
+ if (parts.some(({matchers}) => matchers.length === 0)) return false;
277
+
278
+ const root =
279
+ leadingCombinator === COMBINATOR_ADJACENT ||
280
+ leadingCombinator === COMBINATOR_SIBLING
281
+ ? scope[NEXT]
282
+ : scope[CHILD];
283
+ if (!root) return false;
284
+
285
+ let matched = false;
286
+ walkNodesForSelector(
287
+ root,
288
+ parts,
289
+ () => {
290
+ matched = true;
291
+ return false;
292
+ },
293
+ scope,
294
+ );
295
+ return matched;
121
296
  }
122
297
 
123
298
  function walkNodesForSelector(
124
299
  node: Node,
125
300
  parts: Part[],
126
301
  callback: (node: Element) => boolean | void,
302
+ scope?: Element,
127
303
  ) {
128
- if (isElementNode(node)) {
129
- if (matchesSelectorRecursive(node, parts)) {
130
- if (callback(node) === false) return false;
131
- }
132
- const child = node[CHILD];
133
- if (child && walkNodesForSelector(child, parts, callback) === false) {
134
- return false;
304
+ const pendingSiblings: Node[] = [];
305
+ let current: Node | null = node;
306
+
307
+ while (current) {
308
+ if (isElementNode(current)) {
309
+ if (matchesSelectorRecursive(current, parts, scope)) {
310
+ if (callback(current) === false) return false;
311
+ }
312
+
313
+ const child: Node | null = current[CHILD];
314
+ if (child) {
315
+ const sibling = current[NEXT];
316
+ if (sibling) pendingSiblings.push(sibling);
317
+ current = child;
318
+ continue;
319
+ }
135
320
  }
321
+
322
+ current = current[NEXT] ?? pendingSiblings.pop() ?? null;
136
323
  }
137
- const next = node[NEXT];
138
- if (next && walkNodesForSelector(next, parts, callback) === false) {
139
- return false;
140
- }
324
+
141
325
  return true;
142
326
  }
143
327
 
144
- function matchesSelectorRecursive(element: Element, parts: Part[]): boolean {
328
+ function matchesSelectorRecursive(
329
+ element: Element,
330
+ parts: Part[],
331
+ scope?: Element,
332
+ ): boolean {
145
333
  const {combinator, matchers} = parts[parts.length - 1]!;
146
- if (combinator === Combinator.Inner) {
147
- if (!matchesSelectorMatcher(element, matchers)) return false;
334
+ if (combinator === COMBINATOR_INNER) {
335
+ if (!matchesSelectorMatcher(element, matchers, scope)) return false;
148
336
  const pp = parts.slice(0, -1);
149
- return pp.length === 0 || matchesSelectorRecursive(element, pp);
337
+ return pp.length === 0 || matchesSelectorRecursive(element, pp, scope);
150
338
  }
151
339
  const link =
152
- combinator === Combinator.Child || combinator === Combinator.Descendant
340
+ combinator === COMBINATOR_CHILD || combinator === COMBINATOR_DESCENDANT
153
341
  ? PARENT
154
342
  : PREV;
155
343
  let ref = element[link];
156
344
  if (!ref) return false;
157
345
 
158
346
  if (
159
- combinator === Combinator.Descendant ||
160
- combinator === Combinator.Sibling
347
+ combinator === COMBINATOR_DESCENDANT ||
348
+ combinator === COMBINATOR_SIBLING
161
349
  ) {
162
350
  // For descendant/sibling combinators, search through all ancestors/siblings
163
351
  while (ref) {
164
- if (isElementNode(ref) && matchesSelectorMatcher(ref, matchers)) {
352
+ if (isElementNode(ref) && matchesSelectorMatcher(ref, matchers, scope)) {
165
353
  const pp = parts.slice(0, -1);
166
354
  if (pp.length === 0) return true;
167
- if (matchesSelectorRecursive(element, pp)) return true;
355
+ if (matchesSelectorRecursive(ref, pp, scope)) return true;
168
356
  }
169
357
  ref = ref[link];
170
358
  }
@@ -172,7 +360,7 @@ function matchesSelectorRecursive(element: Element, parts: Part[]): boolean {
172
360
  } else {
173
361
  // For child/adjacent combinators, check only the immediate parent/sibling
174
362
  // For sibling combinators, skip non-element siblings
175
- if (combinator === Combinator.Adjacent && !isElementNode(ref)) {
363
+ if (combinator === COMBINATOR_ADJACENT && !isElementNode(ref)) {
176
364
  // Skip to next element sibling
177
365
  while (ref && !isElementNode(ref)) {
178
366
  ref = ref[link];
@@ -180,89 +368,64 @@ function matchesSelectorRecursive(element: Element, parts: Part[]): boolean {
180
368
  if (!ref) return false;
181
369
  }
182
370
 
183
- if (!isElementNode(ref) || !matchesSelectorMatcher(ref, matchers)) {
371
+ if (!isElementNode(ref) || !matchesSelectorMatcher(ref, matchers, scope)) {
184
372
  return false;
185
373
  }
186
374
  const pp = parts.slice(0, -1);
187
- return pp.length === 0 || matchesSelectorRecursive(element, pp);
375
+ return pp.length === 0 || matchesSelectorRecursive(ref, pp, scope);
188
376
  }
189
377
  }
190
378
 
191
- function matchesSelectorPart(element: Element, {combinator, matchers}: Part) {
192
- if (combinator === Combinator.Inner) {
193
- return matchesSelectorMatcher(element, matchers);
194
- }
195
- const link =
196
- combinator === Combinator.Child || combinator === Combinator.Descendant
197
- ? PARENT
198
- : PREV;
199
- let ref = element[link];
200
- if (!ref) return false;
201
-
202
- // For sibling combinators, skip non-element siblings
203
- if (combinator === Combinator.Adjacent && !isElementNode(ref)) {
204
- while (ref && !isElementNode(ref)) {
205
- ref = ref[link];
206
- }
207
- if (!ref) return false;
208
- }
209
-
210
- if (!isElementNode(ref) || !matchesSelectorMatcher(ref, matchers)) {
211
- return false;
212
- }
213
-
214
- if (
215
- combinator === Combinator.Descendant ||
216
- combinator === Combinator.Sibling
217
- ) {
218
- while ((ref = ref[link])) {
219
- if (isElementNode(ref) && matchesSelectorMatcher(ref, matchers))
220
- return true;
221
- }
222
- }
223
- return true;
379
+ function getSelectorAttribute(element: Element, name: string) {
380
+ return element.getAttributeNS(
381
+ null,
382
+ element.namespaceURI === HTML_NAMESPACE ? asciiLowercase(name) : name,
383
+ );
224
384
  }
225
385
 
226
386
  function matchesSelectorMatcher(
227
387
  element: Element | null,
228
388
  matcher: Matcher | Matcher[],
389
+ scope?: Element,
229
390
  ) {
230
391
  if (!element) return false;
231
392
  if (Array.isArray(matcher)) {
232
393
  for (const single of matcher) {
233
- if (matchesSelectorMatcher(element, single) === false) return false;
394
+ if (matchesSelectorMatcher(element, single, scope) === false) {
395
+ return false;
396
+ }
234
397
  }
235
398
  return true;
236
399
  }
237
400
  const {type, name, value} = matcher;
238
401
  switch (type) {
239
- case MatcherType.Unknown:
402
+ case MATCHER_UNKNOWN:
240
403
  return name === '*'; // Universal selector
241
- case MatcherType.Element:
242
- return element.localName === name;
243
- case MatcherType.Id:
244
- return element.getAttribute('id') === name;
245
- case MatcherType.Class:
246
- const classAttr = element.getAttribute('class');
404
+ case MATCHER_ELEMENT:
405
+ return element.namespaceURI === HTML_NAMESPACE
406
+ ? element.localName === asciiLowercase(name)
407
+ : element.localName === name;
408
+ case MATCHER_ID:
409
+ return getSelectorAttribute(element, 'id') === name;
410
+ case MATCHER_CLASS:
411
+ const classAttr = getSelectorAttribute(element, 'class');
247
412
  if (!classAttr) return false;
248
- return classAttr.split(/\s+/).includes(name);
249
- case MatcherType.Attribute:
250
- return value == null
251
- ? element.hasAttribute(name)
252
- : element.getAttribute(name) === value;
253
- case MatcherType.Pseudo:
254
- switch (name) {
255
- default:
256
- throw Error(`Pseudo :${name} not implemented`);
257
- }
258
- case MatcherType.Function:
413
+ return splitOnASCIIWhitespace(classAttr).includes(name);
414
+ case MATCHER_ATTRIBUTE:
415
+ const attribute = getSelectorAttribute(element, name);
416
+ return value == null ? attribute != null : attribute === value;
417
+ case MATCHER_SCOPE:
418
+ return element === scope;
419
+ case MATCHER_PSEUDO:
420
+ throwSelectorSyntaxError(`:${name}`);
421
+ case MATCHER_FUNCTION:
259
422
  switch (name) {
260
423
  case 'has':
261
- return matchesSelector(element, value || '');
424
+ return matchesRelativeSelector(element, value || '');
262
425
  case 'not':
263
426
  return !matchesSelector(element, value || '');
264
427
  default:
265
- throw Error(`Function :${name}(${value}) not implemented`);
428
+ throwSelectorSyntaxError(`:${name}(${value})`);
266
429
  }
267
430
  }
268
431
  return false;