@remote-dom/polyfill 1.5.0 → 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 +94 -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 +7 -3
  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 +15 -3
  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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,99 @@
1
1
  # @remote-dom/polyfill
2
2
 
3
+ ## 1.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#620](https://github.com/Shopify/remote-dom/pull/620) [`4be18ef`](https://github.com/Shopify/remote-dom/commit/4be18ef20017587835e7275e3901cd5fcdcdc50e) Thanks [@andrewiggins](https://github.com/andrewiggins)! - Add `getElementById()` to `Document` and `DocumentFragment`, with reflected `Element.id` properties. Add `getElementsByTagName()` to `Document` and `Element`, supporting HTML, non-HTML, and wildcard descendant searches. `querySelector` and `querySelectorAll` accept a pre-parsed `Matcher[]` in addition to string selectors, with `MatcherType`, `Combinator`, `Matcher`, and `Part` exported from `selectors.ts`; `getElementById` and `getElementsByTagName` delegate to this shared selector engine instead of independent tree-walk implementations.
8
+
9
+ Fixed `insertBefore()` leaving the previous sibling pointing at the reference node when inserting before a middle child, causing `NEXT` traversals (including `getElementById`) to skip the inserted subtree even though `childNodes` contained it, and return the inserted child as required by the DOM specification. Fixed `appendChild()` to return the appended child and `NodeList.item()` to return `null` for out-of-range indexes. Fixed case-insensitive HTML tag-name matching in the selector engine so `querySelector('DIV')` now matches `<div>` per the CSS spec. Fixed CSS-escaping issues so `getElementById` matches ids containing special characters (`.`, `:`, `#`, etc.) literally instead of treating them as selector syntax.
10
+
11
+ - [#625](https://github.com/Shopify/remote-dom/pull/625) [`ab6c549`](https://github.com/Shopify/remote-dom/commit/ab6c5494908a5efb53fc8e5534b2b2967b9cbe41) Thanks [@airhorns](https://github.com/airhorns)! - Add `getElementsByClassName()` to polyfilled documents and elements.
12
+
13
+ - [#624](https://github.com/Shopify/remote-dom/pull/624) [`e2a9eef`](https://github.com/Shopify/remote-dom/commit/e2a9eef700edd7f46a0cafb3e3a63d757ccfbc2e) Thanks [@airhorns](https://github.com/airhorns)! - Implement the standard `MutationObserver` methods for child, attribute, and character-data changes in the polyfilled DOM.
14
+
15
+ - [#652](https://github.com/Shopify/remote-dom/pull/652) [`0789d12`](https://github.com/Shopify/remote-dom/commit/0789d12f61ae3a93bd659543b4607eb496efa090) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Add composable `Window` extensions for installing DOM APIs and subscribing to DOM operations.
16
+
17
+ ### Patch Changes
18
+
19
+ - [#681](https://github.com/Shopify/remote-dom/pull/681) [`fc245ba`](https://github.com/Shopify/remote-dom/commit/fc245ba6edd7622c4cccbd98e744d6934f04ab46) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Adopt complete subtrees, including initialized template content, during cross-document insertion so descendant and attribute mutations use the destination document.
20
+
21
+ - [#669](https://github.com/Shopify/remote-dom/pull/669) [`dff700b`](https://github.com/Shopify/remote-dom/commit/dff700bcc9610903ad4f9e6d8b23e6017f113af8) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Dispatch attribute hooks when mutating an attached attribute's value or nodeValue.
22
+
23
+ - [#679](https://github.com/Shopify/remote-dom/pull/679) [`da43e02`](https://github.com/Shopify/remote-dom/commit/da43e02ef1662763d242ed3b386b5b57130aaef3) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Correct chained combinators, whitespace, exact attribute equality, and scoped relative `:has()` selectors, including leading combinators, nested functional pseudo-classes, and ASCII-case-insensitive pseudo-class names.
24
+
25
+ - [#642](https://github.com/Shopify/remote-dom/pull/642) [`85cefcd`](https://github.com/Shopify/remote-dom/commit/85cefcd63efe62de20aadb2fa75d08485b4f1d96) Thanks [@andrewiggins](https://github.com/andrewiggins)! - Add the missing `CustomElementRegistry.initialize()` compatibility method so TypeScript 7 type checks cleanly.
26
+
27
+ - [#653](https://github.com/Shopify/remote-dom/pull/653) [`a9aee3e`](https://github.com/Shopify/remote-dom/commit/a9aee3e3834b621e0c9a4fd432a9eb674447403a) Thanks [@andrewiggins](https://github.com/andrewiggins)! - Make the Polyfill source compatible with type stripping by replacing TypeScript enums and a parameter property with erasable syntax.
28
+
29
+ - [#668](https://github.com/Shopify/remote-dom/pull/668) [`f4af17f`](https://github.com/Shopify/remote-dom/commit/f4af17fb2739cd6cbbf3adf52c1af1fb695c749b) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Preserve attribute ownership and linked-list integrity when attributes are reinstalled, replaced, removed, or reused.
30
+
31
+ - [#683](https://github.com/Shopify/remote-dom/pull/683) [`c13d18b`](https://github.com/Shopify/remote-dom/commit/c13d18b5469de4e87900d7c8123ad18b653f6d4e) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Throw a named `NotSupportedError` when cloning a document or importing one with `Document.importNode()`.
32
+
33
+ - [#681](https://github.com/Shopify/remote-dom/pull/681) [`9904cdd`](https://github.com/Shopify/remote-dom/commit/9904cdd79af5589c3b43e884e27fa2ce10721b58) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Make `ChildNode.replaceWith()`, `before()`, and `after()` validate all arguments before changing existing trees, preserve sibling argument order, and commit each operation before custom-element reactions run.
34
+
35
+ - [#664](https://github.com/Shopify/remote-dom/pull/664) [`8de0600`](https://github.com/Shopify/remote-dom/commit/8de06005c7e4d843182fbd403699dc2df67bee33) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Correct event dispatch lifecycle state, isolate composed paths, and defer listener registrations added during dispatch.
36
+
37
+ - [#692](https://github.com/Shopify/remote-dom/pull/692) [`ee48d52`](https://github.com/Shopify/remote-dom/commit/ee48d5290023a22de7632d7e652438514a1c1cfb) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Throw named DOM errors for invalid tree mutations and selector syntax.
38
+
39
+ - [#669](https://github.com/Shopify/remote-dom/pull/669) [`7eb8a18`](https://github.com/Shopify/remote-dom/commit/7eb8a18aa6753ac34e58d51cfda0533a99e88523) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Return `null` from `Document.textContent` and ignore assignments to preserve the initialized document structure.
40
+
41
+ - [#669](https://github.com/Shopify/remote-dom/pull/669) [`6ddc71f`](https://github.com/Shopify/remote-dom/commit/6ddc71f50e2c492d71c8b506eebc08cf40559199) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Clear parent children without creating an empty text node when assigning an empty `textContent` value, and finish the complete replacement before running custom-element reactions.
42
+
43
+ - [#662](https://github.com/Shopify/remote-dom/pull/662) [`e249e3e`](https://github.com/Shopify/remote-dom/commit/e249e3e8055b7878442ad72fdd2b151cdc3c0171) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Correct event listener identity and AbortSignal handling in the EventTarget polyfill.
44
+
45
+ - [#663](https://github.com/Shopify/remote-dom/pull/663) [`44e3b36`](https://github.com/Shopify/remote-dom/commit/44e3b3677920a43a98a92b8179470d7ede0dac96) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Fix event cancellation semantics for `preventDefault()`, `returnValue`, and `dispatchEvent()`.
46
+
47
+ - [#666](https://github.com/Shopify/remote-dom/pull/666) [`072b7e8`](https://github.com/Shopify/remote-dom/commit/072b7e8a68855144cc359d11107874007c043f2b) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Install missing event methods and error handlers consistently without replacing native global event delivery.
48
+
49
+ - [#685](https://github.com/Shopify/remote-dom/pull/685) [`fd4de61`](https://github.com/Shopify/remote-dom/commit/fd4de61b0be8eac8e056941f288f0592404b57dc) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Preserve character references and literal ampersands when parsing `innerHTML`, decode attribute references without double-escaping, handle nested `innerHTML` parsing from synchronous callbacks, and handle HTML void elements without nesting following content or serializing closing tags.
50
+
51
+ - [#669](https://github.com/Shopify/remote-dom/pull/669) [`fd8e186`](https://github.com/Shopify/remote-dom/commit/fd8e186d49bc1b3fa68ec0a26eb1de1a59c218c5) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Remove `slot` attributes without recreating them as empty attributes, keeping local and host state synchronized.
52
+
53
+ - [#688](https://github.com/Shopify/remote-dom/pull/688) [`5f0b228`](https://github.com/Shopify/remote-dom/commit/5f0b2281bddfc7ac37c64503f40f38f4f9b98243) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Default omitted CustomEvent detail values to null.
54
+
55
+ - [#688](https://github.com/Shopify/remote-dom/pull/688) [`08e3223`](https://github.com/Shopify/remote-dom/commit/08e3223a5fb340e80693168e04ed03a01764ef2a) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Include `Comment` nodes in the polyfill hook type contract.
56
+
57
+ - [#677](https://github.com/Shopify/remote-dom/pull/677) [`a629e25`](https://github.com/Shopify/remote-dom/commit/a629e25eb4eba6aae495257a40f30883b6dce53a) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Normalize and validate DOM element and attribute names across HTML and namespaced APIs, including `toggleAttribute()` and custom-element attribute reactions.
58
+
59
+ - [#688](https://github.com/Shopify/remote-dom/pull/688) [`e1acf97`](https://github.com/Shopify/remote-dom/commit/e1acf97baa3786d3284b6a0164ccc7206e3ec956) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Normalize `createTextNode()` hook data to match the created text node.
60
+
61
+ - [#685](https://github.com/Shopify/remote-dom/pull/685) [`0a39389`](https://github.com/Shopify/remote-dom/commit/0a3938987f46fd962ac84ed00808680852263df9) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Parse template descendants into template content and serialize that content with stack-safe HTML output.
62
+
63
+ - [#623](https://github.com/Shopify/remote-dom/pull/623) [`c3918c6`](https://github.com/Shopify/remote-dom/commit/c3918c61c3ee6b3aa6ec7fbf4d53aa76532ec29d) Thanks [@airhorns](https://github.com/airhorns)! - Fix `ChildNode.replaceWith()` throwing instead of replacing the node
64
+
65
+ `replaceWith()` passed its arguments to `replaceChild()` in the wrong order — `replaceChild(newChild, oldChild)` was called as `parent.replaceChild(this, node)`, naming the incoming node as the child to replace. Since that node is usually fresh and has no parent, the reference check rejected it and every call threw `reference node is not a child of this parent`. It also read the following sibling off the incoming node rather than off `this`, so the remaining arguments had no correct insertion point to anchor to.
66
+
67
+ The method now removes `this` and inserts the given nodes at its position, in argument order, anchored on the first following sibling that is not itself being moved. Strings become text nodes, calling it with no arguments removes the node (matching `remove()`), and a node with no parent is still left alone.
68
+
69
+ - [#714](https://github.com/Shopify/remote-dom/pull/714) [`516fcfa`](https://github.com/Shopify/remote-dom/commit/516fcfa4c4a04e8ff5a0e7c6b7da0d38d2dd1f57) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Resolve `Node.isDefaultNamespace()` with DOM locate-a-namespace semantics.
70
+
71
+ - [#669](https://github.com/Shopify/remote-dom/pull/669) [`23ceb4d`](https://github.com/Shopify/remote-dom/commit/23ceb4d22c62096ffdb00d4d107ab2cacdc54935) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Return removed and replaced nodes from `removeChild` and `replaceChild`.
72
+
73
+ - [#679](https://github.com/Shopify/remote-dom/pull/679) [`5466415`](https://github.com/Shopify/remote-dom/commit/54664151820316d9f655e9964de232854fdec5c4) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Return a NodeList-compatible collection from `querySelectorAll()`.
74
+
75
+ - [#669](https://github.com/Shopify/remote-dom/pull/669) [`d916fab`](https://github.com/Shopify/remote-dom/commit/d916fabc327a9352c1f3e8b2d37f939527d2027a) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Queue custom-element reactions so compound tree and attribute mutations commit their local state and Remote DOM hooks before callbacks run. Drain nested reactions in FIFO order, and finish the queue before rethrowing the first callback error.
76
+
77
+ - [#669](https://github.com/Shopify/remote-dom/pull/669) [`4b8bae9`](https://github.com/Shopify/remote-dom/commit/4b8bae9a507cf80492fb3b03975b90872b76465c) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Traverse wide and deep trees without overflowing the call stack during text collection, selector queries, and subtree connectivity updates. Prepare insertion snapshots transactionally before committing links, connectivity, hooks, and reactions so traversal failures preserve local and remote tree state. Capture lifecycle reactions in mutation order before emitting reentrant tree-mutation hook effects through a FIFO queue.
78
+
79
+ - [#676](https://github.com/Shopify/remote-dom/pull/676) [`6b42b09`](https://github.com/Shopify/remote-dom/commit/6b42b0980b3d2b01a1029ae1cab52345e0fb021a) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Make `CustomElementRegistry.define()` reject invalid custom element names and duplicate name or constructor registrations, matching browser behavior.
80
+
81
+ - [#660](https://github.com/Shopify/remote-dom/pull/660) [`685dff1`](https://github.com/Shopify/remote-dom/commit/685dff1a617c97afc5079edb742940bb6ff92022) Thanks [@andrewiggins](https://github.com/andrewiggins)! - Correct class selector whitespace parsing and return polyfilled `NodeList` collections from selector queries.
82
+
83
+ - [#683](https://github.com/Shopify/remote-dom/pull/683) [`7181475`](https://github.com/Shopify/remote-dom/commit/71814754bcd9a06155e9a284867c93031c133007) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Preserve established element and attribute names, namespaces, and both direct and template-content trees when cloning or importing nodes without recursive traversal.
84
+
85
+ - [#688](https://github.com/Shopify/remote-dom/pull/688) [`3c57e5b`](https://github.com/Shopify/remote-dom/commit/3c57e5b6202f0779eb7ee99ad3298f6d17032c51) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Fix `Node.contains()` for nested descendants and nodes outside the current subtree.
86
+
87
+ - [#667](https://github.com/Shopify/remote-dom/pull/667) [`cdfd5dd`](https://github.com/Shopify/remote-dom/commit/cdfd5dd2bc3fecbea7799d3921bb8d93bd6103dd) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Validate tree insertions and replacements before changing node links so invalid ancestor, template-content cycle, and reference-node mutations preserve the existing trees. Treat inserting a node before itself as a no-op and safely replace a child with its next sibling.
88
+
89
+ - [#687](https://github.com/Shopify/remote-dom/pull/687) [`db32f49`](https://github.com/Shopify/remote-dom/commit/db32f49fbbe0bdfd1e14450008b5b8634d2362d7) Thanks [@olavoasantos](https://github.com/olavoasantos)! - Ignore unmatched closing tags, preserve SVG and HTML namespace boundaries, and retain literal parser names while parsing `innerHTML`.
90
+
91
+ ## 1.5.1
92
+
93
+ ### Patch Changes
94
+
95
+ - [#600](https://github.com/Shopify/remote-dom/pull/600) [`d9b4dab`](https://github.com/Shopify/remote-dom/commit/d9b4dab1ce2f76751fafab04e7a6ede8a3aa8045) Thanks [@henrytao-me](https://github.com/henrytao-me)! - Fix document fragment owner document
96
+
3
97
  ## 1.5.0
4
98
 
5
99
  ### Minor Changes
package/README.md CHANGED
@@ -34,12 +34,60 @@ This process will install polyfilled versions of the following globals:
34
34
  - [`location`](https://developer.mozilla.org/en-US/docs/Web/API/Window/location) and [`navigator`](https://developer.mozilla.org/en-US/docs/Web/API/Window/navigator), though these are just set to `globalThis.location` and `globalThis.navigator`.
35
35
  - The [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event), [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget), [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent), [`Node`](https://developer.mozilla.org/en-US/docs/Web/API/Node), [`ParentNode`](https://developer.mozilla.org/en-US/docs/Web/API/ParentNode), [`ChildNode`](https://developer.mozilla.org/en-US/docs/Web/API/ChildNode), [`Document`](https://developer.mozilla.org/en-US/docs/Web/API/Document), [`DocumentFragment`](https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment), [`CharacterData`](https://developer.mozilla.org/en-US/docs/Web/API/CharacterData), [`Comment`](https://developer.mozilla.org/en-US/docs/Web/API/Comment), [`Text`](https://developer.mozilla.org/en-US/docs/Web/API/Text), [`Element`](https://developer.mozilla.org/en-US/docs/Web/API/Element), [`HTMLElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement), [`SVGElement`](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement), [`HTMLTemplateElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTemplateElement), and [`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) constructors.
36
36
 
37
- This polyfill lets you hook into many of the operations that happen in the DOM, like creating elements, updating attributes, and adding event listeners. You define these hooks by overwriting any of the properties on the `hooks` export of this library.
37
+ ## Extensions
38
+
39
+ Use `Window.with()` to create a reusable `Window` subclass with additional DOM
40
+ APIs or behavior. An extension is a function with two distinct parts:
41
+
42
+ - **The function body runs once per window, during construction.** It receives
43
+ the new `Window` instance and can install or replace APIs on it, like
44
+ `window.MutationObserver`. Use it for setup, not for reacting to DOM changes.
45
+ - **The returned hooks run for the lifetime of the window.** They subscribe to
46
+ DOM operations on that window, such as creating elements, setting attributes,
47
+ and adding event listeners, and are called each time one of those operations
48
+ happens. Returning hooks is optional.
49
+
50
+ ```ts
51
+ import {Window, type WindowExtension} from '@remote-dom/polyfill';
52
+
53
+ class CustomMutationObserver {}
54
+
55
+ const mutationObserverExtension: WindowExtension = (window) => {
56
+ // Construction time: extend this window instance.
57
+ window.MutationObserver = CustomMutationObserver;
58
+
59
+ // Runtime: subscribe to DOM operations on this window.
60
+ return {
61
+ setAttribute(element, name, value) {
62
+ // Notify observers associated with this window.
63
+ },
64
+ };
65
+ };
66
+
67
+ const ExtendedWindow = Window.with(mutationObserverExtension);
68
+ const window = new ExtendedWindow();
69
+ ```
70
+
71
+ Extensions are installed in the order passed to `Window.with()` and across
72
+ chained `.with()` calls. The two parts compose differently:
73
+
74
+ - **Window APIs override.** Assignments like `window.MutationObserver = …` are
75
+ plain property writes, so when multiple extensions set the same API, the last
76
+ extension installed wins.
77
+ - **Hooks are additive.** Every installed extension’s hooks are called in
78
+ installation order for each DOM operation.
79
+
80
+ Extensions are installed after the base window and its initial document have
81
+ been constructed, so their hooks do not observe the document’s bootstrap.
82
+
83
+ Assigning hooks directly through the exported `HOOKS` symbol is the legacy
84
+ integration API. New integrations should use extensions instead. Extension
85
+ hooks run first, followed by the legacy hook assigned through `window[HOOKS]`:
38
86
 
39
87
  ```ts
40
- import {hooks} from '@remote-dom/polyfill';
88
+ import {HOOKS} from '@remote-dom/polyfill';
41
89
 
42
- hooks.createElement = (element) => {
90
+ window[HOOKS].createElement = (element) => {
43
91
  console.log('Creating element:', element);
44
92
  };
45
93
  ```
@@ -4,9 +4,12 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
5
  const constants = require('./constants.cjs');
6
6
  const Node = require('./Node.cjs');
7
+ const MutationObserver = require('./MutationObserver.cjs');
8
+ const attributeReactions = require('./attribute-reactions.cjs');
9
+ const customElementReactions = require('./custom-element-reactions.cjs');
7
10
 
8
11
  class Attr extends Node.Node {
9
- nodeType = (() => constants.NodeType.ATTRIBUTE_NODE)();
12
+ nodeType = constants.NODE_TYPE_ATTRIBUTE;
10
13
  [constants.NS] = null;
11
14
  [constants.NEXT] = null;
12
15
  [constants.OWNER_ELEMENT] = null;
@@ -24,15 +27,41 @@ class Attr extends Node.Node {
24
27
  return this[constants.NAME];
25
28
  }
26
29
  set name(_readonly) {}
30
+ get localName() {
31
+ if (this[constants.NS] == null) return this[constants.NAME];
32
+ const separator = this[constants.NAME].indexOf(':');
33
+ return separator < 0 ? this[constants.NAME] : this[constants.NAME].slice(separator + 1);
34
+ }
35
+ get prefix() {
36
+ if (this[constants.NS] == null) return null;
37
+ const separator = this[constants.NAME].indexOf(':');
38
+ return separator < 0 ? null : this[constants.NAME].slice(0, separator);
39
+ }
27
40
  get value() {
28
41
  return this[constants.VALUE];
29
42
  }
30
43
  set value(value) {
31
44
  const str = String(value);
32
- this[constants.VALUE] = str;
33
45
  const ownerElement = this[constants.OWNER_ELEMENT];
34
- if (!ownerElement) return;
35
- this[constants.HOOKS].setAttribute?.(ownerElement, this[constants.NAME], str, this[constants.NS]);
46
+ if (!ownerElement) {
47
+ this[constants.VALUE] = str;
48
+ return;
49
+ }
50
+ customElementReactions.performWithCustomElementReactions(() => {
51
+ const oldValue = this[constants.VALUE];
52
+ this[constants.VALUE] = str;
53
+ if (MutationObserver.attributeObserversActive && oldValue !== str) {
54
+ MutationObserver.queueMutationRecord({
55
+ type: 'attributes',
56
+ target: ownerElement,
57
+ attributeName: this[constants.NAME],
58
+ attributeNamespace: this[constants.NS],
59
+ oldValue
60
+ });
61
+ }
62
+ this[constants.HOOKS].setAttribute?.(ownerElement, this[constants.NAME], str, this[constants.NS]);
63
+ attributeReactions.enqueueAttributeReaction(ownerElement, this.localName, oldValue, str, this[constants.NS]);
64
+ });
36
65
  }
37
66
  get nodeValue() {
38
67
  return this.value;
@@ -4,6 +4,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
5
  const constants = require('./constants.cjs');
6
6
  const ChildNode = require('./ChildNode.cjs');
7
+ const MutationObserver = require('./MutationObserver.cjs');
7
8
 
8
9
  class CharacterData extends ChildNode.ChildNode {
9
10
  [constants.DATA] = '';
@@ -12,11 +13,20 @@ class CharacterData extends ChildNode.ChildNode {
12
13
  this[constants.DATA] = data == null ? '' : String(data);
13
14
  }
14
15
  setData(data) {
16
+ const shouldQueueMutation = MutationObserver.characterDataObserversActive;
17
+ const oldValue = shouldQueueMutation ? this[constants.DATA] : null;
15
18
  let str = '';
16
19
  if (data != null) {
17
20
  str = typeof data === 'string' ? data : String(data);
18
21
  }
19
22
  this[constants.DATA] = str;
23
+ if (shouldQueueMutation && oldValue !== str) {
24
+ MutationObserver.queueMutationRecord({
25
+ type: 'characterData',
26
+ target: this,
27
+ oldValue
28
+ });
29
+ }
20
30
  this[constants.HOOKS].setText?.(this, str);
21
31
  }
22
32
  get data() {
@@ -3,8 +3,14 @@
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
5
  const constants = require('./constants.cjs');
6
+ const customElementReactions = require('./custom-element-reactions.cjs');
7
+ const hookEffects = require('./hook-effects.cjs');
8
+ const domException = require('./dom-exception.cjs');
6
9
  const Node = require('./Node.cjs');
7
10
 
11
+ const INSERT_NODE = Symbol('insertNode');
12
+ const PREFLIGHT_INSERTIONS = Symbol('preflightInsertions');
13
+ const REPLACE_NODE = Symbol('replaceNode');
8
14
  class ChildNode extends Node.Node {
9
15
  remove() {
10
16
  const parent = this.parentNode;
@@ -12,36 +18,96 @@ class ChildNode extends Node.Node {
12
18
  parent.removeChild(this);
13
19
  }
14
20
  replaceWith(...nodes) {
21
+ const staged = stageNodes(nodes);
15
22
  const parent = this.parentNode;
16
23
  if (!parent) return;
17
- const node = toNode(parent, nodes[0]);
18
- const next = node[constants.NEXT];
19
- parent.replaceChild(this, node);
20
- for (let i = 1; i < nodes.length; i++) {
21
- parent.insertBefore(toNode(parent, nodes[i]), next);
22
- }
24
+ return performChildNodeMutation(hookEffects => {
25
+ validateInsertionNodes(parent, staged);
26
+ let next = this[constants.NEXT];
27
+ while (next && staged.includes(next)) next = next[constants.NEXT];
28
+ const replacement = convertNodesIntoNode(parent, staged, hookEffects);
29
+ if (this.parentNode === parent) {
30
+ parent[REPLACE_NODE](replacement, this, hookEffects);
31
+ } else {
32
+ parent[INSERT_NODE](replacement, next, hookEffects);
33
+ }
34
+ });
23
35
  }
24
36
  before(...nodes) {
37
+ const staged = stageNodes(nodes);
25
38
  const parent = this.parentNode;
26
39
  if (!parent) return;
27
- for (const node of nodes) {
28
- parent.insertBefore(toNode(parent, node), this);
29
- }
40
+ return performChildNodeMutation(hookEffects => {
41
+ validateInsertionNodes(parent, staged);
42
+ let previous = this[constants.PREV];
43
+ while (previous && staged.includes(previous)) previous = previous[constants.PREV];
44
+ const node = convertNodesIntoNode(parent, staged, hookEffects);
45
+ parent[INSERT_NODE](node, previous ? previous[constants.NEXT] : parent.firstChild, hookEffects);
46
+ });
30
47
  }
31
48
  after(...nodes) {
49
+ const staged = stageNodes(nodes);
32
50
  const parent = this.parentNode;
33
51
  if (!parent) return;
34
- const next = this[constants.NEXT];
35
- for (const node of nodes) {
36
- parent.insertBefore(toNode(parent, node), next);
37
- }
52
+ return performChildNodeMutation(hookEffects => {
53
+ validateInsertionNodes(parent, staged);
54
+ let next = this[constants.NEXT];
55
+ while (next && staged.includes(next)) next = next[constants.NEXT];
56
+ const node = convertNodesIntoNode(parent, staged, hookEffects);
57
+ parent[INSERT_NODE](node, next, hookEffects);
58
+ });
38
59
  }
39
60
  }
61
+ function performChildNodeMutation(mutation) {
62
+ return customElementReactions.performWithCustomElementReactions(() => {
63
+ const hookEffects$1 = [];
64
+ try {
65
+ mutation(hookEffects$1);
66
+ } catch (error) {
67
+ try {
68
+ hookEffects.performHookEffects(hookEffects$1);
69
+ } catch {}
70
+ throw error;
71
+ }
72
+ hookEffects.performHookEffects(hookEffects$1);
73
+ });
74
+ }
75
+ function stageNodes(nodes) {
76
+ return nodes.map(node => node instanceof Node.Node ? node : String(node));
77
+ }
40
78
  function toNode(parent, node) {
41
79
  if (node instanceof Node.Node) return node;
42
80
  const ownerDocument = parent.ownerDocument;
43
81
  return ownerDocument.createTextNode(String(node));
44
82
  }
83
+ function validateInsertionNodes(parent, nodes) {
84
+ for (const node of nodes) {
85
+ if (!(node instanceof Node.Node)) continue;
86
+ let ancestor = parent;
87
+ while (ancestor) {
88
+ if (ancestor === node) {
89
+ throw domException.createDOMException('Cannot insert a node into itself or one of its descendants', 'HierarchyRequestError');
90
+ }
91
+ ancestor = ancestor[constants.PARENT] ?? ancestor[constants.HOST];
92
+ }
93
+ }
94
+ }
95
+ function convertNodesIntoNode(parent, nodes, hookEffects) {
96
+ const convertedNodes = [];
97
+ for (const node of nodes) convertedNodes.push(toNode(parent, node));
98
+ if (convertedNodes.length === 1) return convertedNodes[0];
99
+ const fragment = parent.ownerDocument.createDocumentFragment();
100
+ fragment[PREFLIGHT_INSERTIONS](convertedNodes);
101
+ for (const node of convertedNodes) {
102
+ fragment[INSERT_NODE](node, null, hookEffects);
103
+ }
104
+ return fragment;
105
+ }
45
106
 
46
107
  exports.ChildNode = ChildNode;
108
+ exports.INSERT_NODE = INSERT_NODE;
109
+ exports.PREFLIGHT_INSERTIONS = PREFLIGHT_INSERTIONS;
110
+ exports.REPLACE_NODE = REPLACE_NODE;
111
+ exports.stageNodes = stageNodes;
47
112
  exports.toNode = toNode;
113
+ exports.validateInsertionNodes = validateInsertionNodes;
@@ -6,7 +6,7 @@ const constants = require('./constants.cjs');
6
6
  const CharacterData = require('./CharacterData.cjs');
7
7
 
8
8
  class Comment extends CharacterData.CharacterData {
9
- nodeType = 8;
9
+ nodeType = constants.NODE_TYPE_COMMENT;
10
10
  [constants.NAME] = '#comment';
11
11
  }
12
12
 
@@ -2,10 +2,29 @@
2
2
 
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
+ const domException = require('./dom-exception.cjs');
6
+
7
+ const VALID_CUSTOM_ELEMENT_NAME = /^[a-z][^A-Z\u0000\t\n\f\r />]*-[^A-Z\u0000\t\n\f\r />]*$/u;
8
+ const RESERVED_CUSTOM_ELEMENT_NAMES = new Set(['annotation-xml', 'color-profile', 'font-face', 'font-face-src', 'font-face-uri', 'font-face-format', 'font-face-name', 'missing-glyph']);
9
+ function isValidCustomElementName(name) {
10
+ return VALID_CUSTOM_ELEMENT_NAME.test(name) && !RESERVED_CUSTOM_ELEMENT_NAMES.has(name);
11
+ }
12
+ function createInvalidCustomElementNameError(name) {
13
+ return domException.createDOMException(`Invalid custom element name: "${name}"`, 'SyntaxError');
14
+ }
5
15
  class CustomElementRegistryImplementation {
6
- registry = (() => new Map())();
7
- listenersByName = (() => new Map())();
16
+ registry = new Map();
17
+ listenersByName = new Map();
8
18
  define(name, Constructor, _options) {
19
+ if (!isValidCustomElementName(name)) {
20
+ throw createInvalidCustomElementNameError(name);
21
+ }
22
+ if (this.registry.has(name)) {
23
+ throw domException.createDOMException(`A custom element named "${name}" has already been defined`, 'NotSupportedError');
24
+ }
25
+ if (this.getName(Constructor) != null) {
26
+ throw domException.createDOMException('This constructor has already been registered in this custom element registry', 'NotSupportedError');
27
+ }
9
28
  this.registry.set(name, Constructor);
10
29
  const listeners = this.listenersByName.get(name);
11
30
  if (listeners == null) return;
@@ -24,6 +43,9 @@ class CustomElementRegistryImplementation {
24
43
  return null;
25
44
  }
26
45
  whenDefined(name) {
46
+ if (!isValidCustomElementName(name)) {
47
+ return Promise.reject(createInvalidCustomElementNameError(name));
48
+ }
27
49
  const Constructor = this.registry.get(name);
28
50
  if (Constructor != null) return Promise.resolve(Constructor);
29
51
  let listeners = this.listenersByName.get(name);
@@ -38,6 +60,9 @@ class CustomElementRegistryImplementation {
38
60
  upgrade(_root) {
39
61
  // TODO
40
62
  }
63
+ initialize(_root) {
64
+ // TODO
65
+ }
41
66
  }
42
67
 
43
68
  exports.CustomElementRegistryImplementation = CustomElementRegistryImplementation;
@@ -13,7 +13,7 @@ class CustomEvent extends Event.Event {
13
13
 
14
14
  constructor(type, eventInitDict) {
15
15
  super(type, eventInitDict);
16
- this.detail = eventInitDict?.detail;
16
+ this.detail = eventInitDict?.detail ?? null;
17
17
  }
18
18
 
19
19
  /**
@@ -23,7 +23,7 @@ class CustomEvent extends Event.Event {
23
23
  */
24
24
  initCustomEvent(type, bubbles, cancelable, detail) {
25
25
  super.initEvent(type, bubbles, cancelable);
26
- this.detail = detail;
26
+ this.detail = detail ?? null;
27
27
  }
28
28
  }
29
29
 
@@ -3,6 +3,7 @@
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
5
  const constants = require('./constants.cjs');
6
+ const names = require('./names.cjs');
6
7
  const Event = require('./Event.cjs');
7
8
  const ParentNode = require('./ParentNode.cjs');
8
9
  const Element = require('./Element.cjs');
@@ -15,9 +16,11 @@ const shared = require('./shared.cjs');
15
16
  const HTMLBodyElement = require('./HTMLBodyElement.cjs');
16
17
  const HTMLHeadElement = require('./HTMLHeadElement.cjs');
17
18
  const HTMLHtmlElement = require('./HTMLHtmlElement.cjs');
19
+ const customElementReactions = require('./custom-element-reactions.cjs');
20
+ const domException = require('./dom-exception.cjs');
18
21
 
19
22
  class Document extends ParentNode.ParentNode {
20
- nodeType = (() => constants.NodeType.DOCUMENT_NODE)();
23
+ nodeType = constants.NODE_TYPE_DOCUMENT;
21
24
  [constants.NAME] = '#document';
22
25
  [constants.IS_CONNECTED] = true;
23
26
  constructor(defaultView) {
@@ -31,15 +34,29 @@ class Document extends ParentNode.ParentNode {
31
34
  this.documentElement.appendChild(this.head);
32
35
  this.documentElement.appendChild(this.body);
33
36
  }
37
+ get textContent() {
38
+ return null;
39
+ }
40
+ set textContent(data) {
41
+ }
42
+ getElementsByClassName(classNames) {
43
+ return shared.getElementsByClassName(this, classNames);
44
+ }
34
45
  createElement(localName) {
35
- return createElement(this, localName);
46
+ const name = String(localName);
47
+ names.validateElementLocalName(name);
48
+ return createElement(this, constants.asciiLowercase(name));
36
49
  }
37
- createElementNS(namespaceURI, localName) {
38
- return createElement(this, localName, namespaceURI);
50
+ createElementNS(namespaceURI, qualifiedName) {
51
+ const name = names.validateAndExtractQualifiedName(namespaceURI, qualifiedName, 'element');
52
+ return createElement(this, name.qualifiedName, name.namespace, name.prefix, name.localName);
53
+ }
54
+ [constants.CREATE_ELEMENT](qualifiedName, namespace, prefix, localName) {
55
+ return createElement(this, qualifiedName, namespace, prefix, localName);
39
56
  }
40
57
  createTextNode(data) {
41
58
  const text = createNode(new Text.Text(data), this);
42
- this[constants.HOOKS].createText?.(text, String(data));
59
+ this[constants.HOOKS].createText?.(text, text.data);
43
60
  return text;
44
61
  }
45
62
  createComment(data) {
@@ -51,14 +68,26 @@ class Document extends ParentNode.ParentNode {
51
68
  createEvent() {
52
69
  return new Event.Event('');
53
70
  }
71
+ getElementById(elementId) {
72
+ return shared.getElementById(this, elementId);
73
+ }
74
+ getElementsByTagName(qualifiedName) {
75
+ return shared.getElementsByTagName(this, qualifiedName);
76
+ }
54
77
  importNode(node, deep) {
78
+ if (node.nodeType === constants.NODE_TYPE_DOCUMENT) {
79
+ throw domException.createDOMException('Cannot import a document node', 'NotSupportedError');
80
+ }
55
81
  return shared.cloneNode(node, deep, this);
56
82
  }
57
83
  adoptNode(node) {
58
84
  if (node[constants.OWNER_DOCUMENT] === this) return node;
59
- node.parentNode?.removeChild(node);
60
- adoptNode(node, this);
61
- return node;
85
+ const adoption = shared.collectAdoptionSnapshot(node);
86
+ return customElementReactions.performWithCustomElementReactions(() => {
87
+ const parent = node.parentNode;
88
+ if (parent) ParentNode.removeChildForAdoption(parent, node, adoption, this);else shared.adoptNodes(adoption.nodes, this);
89
+ return node;
90
+ });
62
91
  }
63
92
  }
64
93
  function createNode(node, ownerDocument) {
@@ -69,43 +98,38 @@ function createNode(node, ownerDocument) {
69
98
  });
70
99
  return node;
71
100
  }
72
- function createElement(ownerDocument, name, namespace) {
101
+ function createElement(ownerDocument, qualifiedName, namespace = constants.HTML_NAMESPACE, prefix = null, localName = qualifiedName) {
73
102
  let element;
74
- const lowerName = String(name).toLowerCase();
75
- if (namespace === constants.NamespaceURI.SVG) {
103
+ if (namespace === constants.SVG_NAMESPACE) {
76
104
  element = new SVGElement.SVGElement();
77
- } else if (lowerName === 'template') {
105
+ } else if (namespace === constants.HTML_NAMESPACE && localName === 'template') {
78
106
  element = new HTMLTemplateElement.HTMLTemplateElement();
79
- } else {
80
- const CustomElement = ownerDocument.defaultView.customElements.get(name);
107
+ } else if (namespace === constants.HTML_NAMESPACE) {
108
+ const CustomElement = ownerDocument.defaultView.customElements.get(localName);
81
109
  element = CustomElement ? new CustomElement() : new Element.Element();
110
+ } else {
111
+ element = new Element.Element();
82
112
  }
83
- return setupElement(element, ownerDocument, name, namespace);
113
+ return setupElement(element, ownerDocument, qualifiedName, namespace, prefix);
84
114
  }
85
- function setupElement(element, ownerDocument, name, namespace) {
115
+ function setupElement(element, ownerDocument, qualifiedName, namespace = constants.HTML_NAMESPACE, prefix = null) {
86
116
  createNode(element, ownerDocument);
87
- Object.defineProperty(element, constants.NAME, {
88
- value: name
89
- });
90
- if (namespace) {
91
- Object.defineProperty(element, constants.NS, {
117
+ Object.defineProperties(element, {
118
+ [constants.NAME]: {
119
+ value: qualifiedName
120
+ },
121
+ [constants.NS]: {
92
122
  value: namespace
93
- });
94
- }
123
+ },
124
+ [constants.PREFIX]: {
125
+ value: prefix
126
+ }
127
+ });
95
128
  ownerDocument[constants.HOOKS].createElement?.(element, namespace);
96
129
  return element;
97
130
  }
98
- function adoptNode(node, document) {
99
- node[constants.OWNER_DOCUMENT] = document;
100
- if (shared.isParentNode(node)) {
101
- for (const child of node.childNodes) {
102
- adoptNode(child, document);
103
- }
104
- }
105
- }
106
131
 
107
132
  exports.Document = Document;
108
- exports.adoptNode = adoptNode;
109
133
  exports.createElement = createElement;
110
134
  exports.createNode = createNode;
111
135
  exports.setupElement = setupElement;
@@ -4,11 +4,15 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
5
  const constants = require('./constants.cjs');
6
6
  const ParentNode = require('./ParentNode.cjs');
7
+ const shared = require('./shared.cjs');
7
8
 
8
9
  class DocumentFragment extends ParentNode.ParentNode {
9
- nodeType = (() => constants.NodeType.DOCUMENT_FRAGMENT_NODE)();
10
+ nodeType = constants.NODE_TYPE_DOCUMENT_FRAGMENT;
10
11
  [constants.NAME] = '#document-fragment';
11
- [constants.OWNER_DOCUMENT] = (() => window.document)();
12
+ [constants.OWNER_DOCUMENT] = typeof window !== 'undefined' ? window.document : null;
13
+ getElementById(elementId) {
14
+ return shared.getElementById(this, elementId);
15
+ }
12
16
  }
13
17
 
14
18
  exports.DocumentFragment = DocumentFragment;