@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
package/source/Text.ts CHANGED
@@ -1,7 +1,7 @@
1
- import {NAME, NodeType} from './constants.ts';
1
+ import {NAME, NODE_TYPE_TEXT, type NodeType} from './constants.ts';
2
2
  import {CharacterData} from './CharacterData.ts';
3
3
 
4
4
  export class Text extends CharacterData {
5
- nodeType = NodeType.TEXT_NODE;
5
+ nodeType: NodeType = NODE_TYPE_TEXT;
6
6
  [NAME] = '#text';
7
7
  }
package/source/Window.ts CHANGED
@@ -20,7 +20,8 @@ import {DocumentFragment} from './DocumentFragment.ts';
20
20
  import {HTMLTemplateElement} from './HTMLTemplateElement.ts';
21
21
  import {CustomElementRegistryImplementation} from './CustomElementRegistry.ts';
22
22
  import {MutationObserver} from './MutationObserver.ts';
23
- import {HOOKS} from './constants.ts';
23
+ import {EXTENSIONS, HOOKS, HOOKS_DISPATCH} from './constants.ts';
24
+ import type {WindowExtension} from './extensions.ts';
24
25
  import type {Hooks} from './hooks.ts';
25
26
 
26
27
  type OnErrorHandler =
@@ -33,8 +34,104 @@ type OnErrorHandler =
33
34
  ) => void)
34
35
  | null;
35
36
 
37
+ const EVENT_HANDLER_PROPERTIES = ['onerror', 'onunhandledrejection'] as const;
38
+ const POLYFILL_GLOBAL_PROPERTY = Symbol.for(
39
+ '@remote-dom/polyfill/global-property',
40
+ );
41
+
42
+ type PropertyDescriptors = Record<PropertyKey, PropertyDescriptor>;
43
+
44
+ function windowPropertyDescriptors(window: Window): PropertyDescriptors {
45
+ return Object.getOwnPropertyDescriptors(window);
46
+ }
47
+
48
+ function eventTargetPropertyDescriptors(window: Window): PropertyDescriptors {
49
+ const properties: PropertyDescriptors = {};
50
+
51
+ for (const [property, descriptor] of Object.entries(
52
+ Object.getOwnPropertyDescriptors(EventTarget.prototype),
53
+ )) {
54
+ if (property !== 'constructor' && typeof descriptor.value === 'function') {
55
+ descriptor.value = markPolyfillGlobalProperty(
56
+ descriptor.value.bind(window),
57
+ );
58
+ properties[property] = descriptor;
59
+ }
60
+ }
61
+
62
+ return properties;
63
+ }
64
+
65
+ function eventHandlerPropertyDescriptors(window: Window): PropertyDescriptors {
66
+ const properties: PropertyDescriptors = {};
67
+
68
+ for (const property of EVENT_HANDLER_PROPERTIES) {
69
+ properties[property] = {
70
+ configurable: true,
71
+ enumerable: true,
72
+ get: markPolyfillGlobalProperty(() => window[property]),
73
+ set: markPolyfillGlobalProperty((handler) => {
74
+ window[property] = handler as any;
75
+ }),
76
+ };
77
+ }
78
+
79
+ return properties;
80
+ }
81
+
82
+ function globalPropertyDescriptors(window: Window): PropertyDescriptors {
83
+ return Object.assign(
84
+ windowPropertyDescriptors(window),
85
+ eventTargetPropertyDescriptors(window),
86
+ eventHandlerPropertyDescriptors(window),
87
+ );
88
+ }
89
+
90
+ function markPolyfillGlobalProperty<
91
+ FunctionType extends (...args: any[]) => any,
92
+ >(value: FunctionType) {
93
+ Object.defineProperty(value, POLYFILL_GLOBAL_PROPERTY, {value: true});
94
+ return value;
95
+ }
96
+
97
+ function installMissingGlobalProperties(
98
+ target: object,
99
+ properties: PropertyDescriptors,
100
+ ) {
101
+ for (const property of Reflect.ownKeys(properties)) {
102
+ const currentDescriptor = findPropertyDescriptor(target, property);
103
+ if (
104
+ currentDescriptor == null ||
105
+ isPolyfillGlobalProperty(currentDescriptor)
106
+ ) {
107
+ Object.defineProperty(target, property, properties[property]!);
108
+ }
109
+ }
110
+ }
111
+
112
+ function findPropertyDescriptor(target: object, property: PropertyKey) {
113
+ let currentTarget: object | null = target;
114
+
115
+ while (currentTarget) {
116
+ const descriptor = Object.getOwnPropertyDescriptor(currentTarget, property);
117
+ if (descriptor) return descriptor;
118
+ currentTarget = Object.getPrototypeOf(currentTarget);
119
+ }
120
+
121
+ return undefined;
122
+ }
123
+
124
+ function isPolyfillGlobalProperty(descriptor: PropertyDescriptor) {
125
+ return [descriptor.value, descriptor.get, descriptor.set].some(
126
+ (value) =>
127
+ typeof value === 'function' && value[POLYFILL_GLOBAL_PROPERTY] === true,
128
+ );
129
+ }
130
+
36
131
  export class Window extends EventTarget {
37
132
  [HOOKS]: Partial<Hooks> = {};
133
+ [EXTENSIONS]: Partial<Hooks>[] = [];
134
+ [HOOKS_DISPATCH]: Hooks = createHooksDispatcher(this);
38
135
  name = '';
39
136
  window = this;
40
137
  parent = this;
@@ -71,6 +168,20 @@ export class Window extends EventTarget {
71
168
  #currentOnUnhandledRejectionHandler: WindowEventHandlers['onunhandledrejection'] =
72
169
  null;
73
170
 
171
+ static with(...extensions: readonly WindowExtension[]): typeof Window {
172
+ const BaseWindow = this;
173
+
174
+ return class ExtendedWindow extends BaseWindow {
175
+ constructor() {
176
+ super();
177
+
178
+ for (const extension of extensions) {
179
+ installExtension(this, extension);
180
+ }
181
+ }
182
+ };
183
+ }
184
+
74
185
  get onerror() {
75
186
  return this.#currentOriginalOnErrorHandler;
76
187
  }
@@ -125,24 +236,35 @@ export class Window extends EventTarget {
125
236
  }
126
237
 
127
238
  static setGlobal(window: Window) {
128
- const properties = Object.getOwnPropertyDescriptors(window);
239
+ const currentSelf = globalThis.self;
240
+ const currentWindow = globalThis.window;
241
+ const properties = windowPropertyDescriptors(window);
242
+ const missingProperties = Object.assign(
243
+ eventTargetPropertyDescriptors(window),
244
+ eventHandlerPropertyDescriptors(window),
245
+ );
129
246
 
130
- delete (properties as any).self;
247
+ delete properties.self;
131
248
 
132
249
  Object.defineProperties(globalThis, properties);
250
+ installMissingGlobalProperties(globalThis, missingProperties);
133
251
 
134
- if (typeof globalThis.self === 'undefined') {
252
+ if (
253
+ typeof currentSelf === 'undefined' ||
254
+ (currentSelf === currentWindow && currentSelf !== globalThis)
255
+ ) {
135
256
  Object.defineProperty(globalThis, 'self', {
136
257
  value: window,
137
258
  configurable: true,
138
259
  writable: true,
139
260
  enumerable: true,
140
261
  });
141
- } else {
262
+ } else if (currentSelf !== globalThis) {
142
263
  // There can already be a `self`, like when polyfilling the DOM
143
264
  // in a Web Worker. In those cases, just mirror all the `Window`
144
265
  // properties onto `self`, rather than wholly redefining it.
145
- Object.defineProperties(self, properties);
266
+ Object.defineProperties(currentSelf, properties);
267
+ installMissingGlobalProperties(currentSelf, missingProperties);
146
268
  }
147
269
  }
148
270
 
@@ -153,18 +275,35 @@ export class Window extends EventTarget {
153
275
  }
154
276
  }
155
277
 
156
- const properties = Object.getOwnPropertyDescriptors(window);
157
- const eventTargetPrototypeProperties = Object.getOwnPropertyDescriptors(
158
- EventTarget.prototype,
159
- );
278
+ Object.defineProperties(globalThis, globalPropertyDescriptors(window));
279
+ }
280
+ }
160
281
 
161
- for (const descriptor of Object.values(eventTargetPrototypeProperties)) {
162
- if (typeof descriptor.value === 'function') {
163
- descriptor.value = descriptor.value.bind(window);
164
- }
165
- }
282
+ function installExtension(window: Window, extension: WindowExtension) {
283
+ window[EXTENSIONS].push(extension(window) ?? {});
284
+ }
166
285
 
167
- Object.defineProperties(globalThis, properties);
168
- Object.defineProperties(globalThis, eventTargetPrototypeProperties);
286
+ function createHooksDispatcher(window: Window): Hooks {
287
+ return {
288
+ createElement: dispatchHook.bind(null, window, 'createElement'),
289
+ setAttribute: dispatchHook.bind(null, window, 'setAttribute'),
290
+ removeAttribute: dispatchHook.bind(null, window, 'removeAttribute'),
291
+ createText: dispatchHook.bind(null, window, 'createText'),
292
+ setText: dispatchHook.bind(null, window, 'setText'),
293
+ insertChild: dispatchHook.bind(null, window, 'insertChild'),
294
+ removeChild: dispatchHook.bind(null, window, 'removeChild'),
295
+ addEventListener: dispatchHook.bind(null, window, 'addEventListener'),
296
+ removeEventListener: dispatchHook.bind(null, window, 'removeEventListener'),
297
+ };
298
+ }
299
+
300
+ function dispatchHook(window: Window, name: keyof Hooks, ...args: unknown[]) {
301
+ for (const hooks of window[EXTENSIONS]) {
302
+ const hook = hooks[name] as ((...args: unknown[]) => void) | undefined;
303
+ hook?.apply(hooks, args);
169
304
  }
305
+
306
+ const hooks = window[HOOKS];
307
+ const legacyHook = hooks[name] as ((...args: unknown[]) => void) | undefined;
308
+ legacyHook?.apply(hooks, args);
170
309
  }
@@ -0,0 +1,32 @@
1
+ import type {NamespaceURI} from './constants.ts';
2
+ import type {Element} from './Element.ts';
3
+ import {enqueueCustomElementReaction} from './custom-element-reactions.ts';
4
+
5
+ export function enqueueAttributeReaction(
6
+ element: Element,
7
+ localName: string,
8
+ oldValue: string | null,
9
+ newValue: string | null,
10
+ namespace: NamespaceURI,
11
+ ) {
12
+ const {observedAttributes} = element.constructor as typeof Element;
13
+ const {attributeChangedCallback} = element;
14
+
15
+ if (
16
+ attributeChangedCallback == null ||
17
+ observedAttributes == null ||
18
+ !observedAttributes.includes(localName)
19
+ ) {
20
+ return;
21
+ }
22
+
23
+ enqueueCustomElementReaction(element, () =>
24
+ attributeChangedCallback.call(
25
+ element,
26
+ localName,
27
+ oldValue,
28
+ newValue,
29
+ namespace,
30
+ ),
31
+ );
32
+ }
@@ -1,4 +1,5 @@
1
1
  export const NAME = Symbol('name');
2
+ export const PREFIX = Symbol('prefix');
2
3
  export const VALUE = Symbol('value');
3
4
  export const NS = Symbol('ns');
4
5
  export const OWNER_ELEMENT = Symbol('owner');
@@ -8,33 +9,55 @@ export const PREV = Symbol('prev');
8
9
  export const NEXT = Symbol('next');
9
10
  export const CHILD = Symbol('child');
10
11
  export const PARENT = Symbol('parent');
12
+ export const HOST = Symbol('host');
11
13
  export const DATA = Symbol('data');
12
14
  export const USER_PROPERTIES = Symbol('user_properties');
13
15
  export const LISTENERS = Symbol('listeners');
14
16
  export const IS_TRUSTED = Symbol('isTrusted');
15
17
  export const PATH = Symbol('path');
16
18
  export const STOP_IMMEDIATE_PROPAGATION = Symbol('stop_immediate_propagation');
19
+ export const DISPATCHING = Symbol('dispatching');
17
20
  export const CONTENT = Symbol('content');
18
21
  export const HOOKS = Symbol('hooks');
22
+ export const EXTENSIONS = Symbol('extensions');
23
+ export const HOOKS_DISPATCH = Symbol('hooks_dispatch');
19
24
  export const IS_CONNECTED = Symbol('is_connected');
25
+ export const CREATE_ELEMENT = Symbol('create_element');
20
26
 
21
- // @TODO remove explicit values
22
- export const enum NodeType {
23
- NODE = 0,
24
- ELEMENT_NODE = 1,
25
- ATTRIBUTE_NODE = 2,
26
- TEXT_NODE = 3,
27
- CDATA_SECTION_NODE = 4,
28
- ENTITY_REFERENCE_NODE = 5,
29
- ENTITY_NODE = 6,
30
- PROCESSING_INSTRUCTION_NODE = 7,
31
- COMMENT_NODE = 8,
32
- DOCUMENT_NODE = 9,
33
- DOCUMENT_TYPE_NODE = 10,
34
- DOCUMENT_FRAGMENT_NODE = 11,
27
+ export const NODE_TYPE_NODE = 0;
28
+ export const NODE_TYPE_ELEMENT = 1;
29
+ export const NODE_TYPE_ATTRIBUTE = 2;
30
+ export const NODE_TYPE_TEXT = 3;
31
+ export const NODE_TYPE_CDATA_SECTION = 4;
32
+ export const NODE_TYPE_ENTITY_REFERENCE = 5;
33
+ export const NODE_TYPE_ENTITY = 6;
34
+ export const NODE_TYPE_PROCESSING_INSTRUCTION = 7;
35
+ export const NODE_TYPE_COMMENT = 8;
36
+ export const NODE_TYPE_DOCUMENT = 9;
37
+ export const NODE_TYPE_DOCUMENT_TYPE = 10;
38
+ export const NODE_TYPE_DOCUMENT_FRAGMENT = 11;
39
+
40
+ export type NodeType = number;
41
+
42
+ export const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
43
+ export const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
44
+ export const XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace';
45
+ export const XMLNS_NAMESPACE = 'http://www.w3.org/2000/xmlns/';
46
+
47
+ export type NamespaceURI = string | null;
48
+
49
+ export function splitOnASCIIWhitespace(value: string) {
50
+ return value.split(/[\t\n\f\r ]+/).filter(Boolean);
51
+ }
52
+
53
+ export function asciiLowercase(value: string) {
54
+ return value.replace(/[A-Z]/g, (character) =>
55
+ String.fromCharCode(character.charCodeAt(0) + 0x20),
56
+ );
35
57
  }
36
58
 
37
- export const enum NamespaceURI {
38
- XHTML = 'http://www.w3.org/1999/xhtml',
39
- SVG = 'http://www.w3.org/2000/svg',
59
+ export function asciiUppercase(value: string) {
60
+ return value.replace(/[a-z]/g, (character) =>
61
+ String.fromCharCode(character.charCodeAt(0) - 0x20),
62
+ );
40
63
  }
@@ -0,0 +1,64 @@
1
+ type CustomElementReaction = () => void;
2
+ type ReactionTarget = object;
3
+
4
+ const scopes: ReactionTarget[][] = [];
5
+ const reactionsByTarget = new WeakMap<
6
+ ReactionTarget,
7
+ CustomElementReaction[]
8
+ >();
9
+
10
+ export function enqueueCustomElementReaction(
11
+ target: ReactionTarget,
12
+ reaction: CustomElementReaction,
13
+ ) {
14
+ const reactions = reactionsByTarget.get(target);
15
+ if (reactions) reactions.push(reaction);
16
+ else reactionsByTarget.set(target, [reaction]);
17
+ scopes[scopes.length - 1]!.push(target);
18
+ }
19
+
20
+ export function performWithCustomElementReactions<T>(mutation: () => T): T {
21
+ const scope: ReactionTarget[] = [];
22
+ scopes.push(scope);
23
+
24
+ let result: T;
25
+ try {
26
+ result = mutation();
27
+ } catch (error) {
28
+ scopes.pop();
29
+ try {
30
+ flushCustomElementReactions(scope);
31
+ } catch {}
32
+ throw error;
33
+ }
34
+
35
+ scopes.pop();
36
+ flushCustomElementReactions(scope);
37
+ return result;
38
+ }
39
+
40
+ function flushCustomElementReactions(scope: ReactionTarget[]) {
41
+ let firstError: unknown;
42
+ let didThrow = false;
43
+
44
+ for (const target of scope) {
45
+ const reactions = reactionsByTarget.get(target);
46
+ if (!reactions) continue;
47
+
48
+ while (reactions.length > 0) {
49
+ const reaction = reactions.shift()!;
50
+ try {
51
+ reaction();
52
+ } catch (error) {
53
+ if (!didThrow) {
54
+ firstError = error;
55
+ didThrow = true;
56
+ }
57
+ }
58
+ }
59
+
60
+ reactionsByTarget.delete(target);
61
+ }
62
+
63
+ if (didThrow) throw firstError;
64
+ }
@@ -0,0 +1,14 @@
1
+ export function createDOMException(
2
+ message: string,
3
+ name: string,
4
+ ): DOMException {
5
+ const DOMExceptionConstructor = globalThis.DOMException;
6
+
7
+ if (typeof DOMExceptionConstructor === 'function') {
8
+ return new DOMExceptionConstructor(message, name);
9
+ }
10
+
11
+ const error = new Error(message);
12
+ error.name = name;
13
+ return error as DOMException;
14
+ }
@@ -0,0 +1,4 @@
1
+ import type {Hooks} from './hooks.ts';
2
+ import type {Window} from './Window.ts';
3
+
4
+ export type WindowExtension = (window: Window) => Partial<Hooks> | void;
@@ -0,0 +1,56 @@
1
+ import {HOST, PARENT} from './constants.ts';
2
+ import type {Node} from './Node.ts';
3
+
4
+ type HookEffect = readonly [run: () => void, publishes?: Node];
5
+
6
+ const hookEffects: HookEffect[] = [];
7
+ const pendingPublications: Node[] = [];
8
+ let isDispatchingHookEffects = false;
9
+
10
+ export function performHookEffects(effects: readonly HookEffect[]) {
11
+ for (const effect of effects) {
12
+ hookEffects.push(effect);
13
+ if (effect[1]) pendingPublications.push(effect[1]);
14
+ }
15
+ if (isDispatchingHookEffects) return;
16
+
17
+ isDispatchingHookEffects = true;
18
+
19
+ try {
20
+ let firstError: unknown;
21
+ let didThrow = false;
22
+
23
+ for (let index = 0; index < hookEffects.length; index++) {
24
+ const [run, publication] = hookEffects[index]!;
25
+ if (publication) {
26
+ pendingPublications.splice(pendingPublications.indexOf(publication), 1);
27
+ }
28
+
29
+ try {
30
+ run();
31
+ } catch (error) {
32
+ if (!didThrow) {
33
+ firstError = error;
34
+ didThrow = true;
35
+ }
36
+ }
37
+ }
38
+
39
+ if (didThrow) throw firstError;
40
+ } finally {
41
+ hookEffects.length = 0;
42
+ pendingPublications.length = 0;
43
+ isDispatchingHookEffects = false;
44
+ }
45
+ }
46
+
47
+ export function isCoveredByPendingPublication(node: Node) {
48
+ let current: Node | null = node;
49
+ while (current) {
50
+ if (pendingPublications.includes(current)) return true;
51
+ current = current[PARENT] ?? current[HOST];
52
+ }
53
+ return false;
54
+ }
55
+
56
+ export type {HookEffect};
package/source/hooks.ts CHANGED
@@ -8,9 +8,17 @@ export interface Hooks {
8
8
  ): void;
9
9
  removeAttribute(element: Element, name: string, ns?: string | null): void;
10
10
  createText(text: Text, data: string): void;
11
- setText(text: Text, data: string): void;
12
- insertChild(parent: Element, node: Element | Text, index: number): void;
13
- removeChild(parent: Element, node: Element | Text, index: number): void;
11
+ setText(text: Text | Comment, data: string): void;
12
+ insertChild(
13
+ parent: Element,
14
+ node: Element | Text | Comment,
15
+ index: number,
16
+ ): void;
17
+ removeChild(
18
+ parent: Element,
19
+ node: Element | Text | Comment,
20
+ index: number,
21
+ ): void;
14
22
  addEventListener(
15
23
  element: EventTarget,
16
24
  type: string,
package/source/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export {type Hooks} from './hooks.ts';
2
+ export {type WindowExtension} from './extensions.ts';
2
3
  export {HOOKS} from './constants.ts';
3
4
  export {Window} from './Window.ts';
4
5
  export {HTMLElement} from './HTMLElement.ts';
@@ -0,0 +1,94 @@
1
+ import {
2
+ XML_NAMESPACE,
3
+ XMLNS_NAMESPACE,
4
+ type NamespaceURI,
5
+ } from './constants.ts';
6
+ import {createDOMException} from './dom-exception.ts';
7
+
8
+ const VALID_ELEMENT_LOCAL_NAME =
9
+ /^(?:[A-Za-z][^\0\t\n\f\r\u0020/>]*|[:_\u0080-\u{10FFFF}][A-Za-z0-9-.:_\u0080-\u{10FFFF}]*)$/u;
10
+ const INVALID_ATTRIBUTE_LOCAL_NAME = /[\0\t\n\f\r />=]/u;
11
+ const INVALID_NAMESPACE_PREFIX = /[\0\t\n\f\r />]/u;
12
+
13
+ export function normalizeNamespace(namespace: unknown): NamespaceURI {
14
+ if (namespace == null) return null;
15
+
16
+ const normalized = String(namespace);
17
+ return normalized === '' ? null : normalized;
18
+ }
19
+
20
+ export function validateElementLocalName(name: string) {
21
+ if (!VALID_ELEMENT_LOCAL_NAME.test(name)) {
22
+ throwInvalidCharacterError(name);
23
+ }
24
+ }
25
+
26
+ export function validateAttributeLocalName(name: string) {
27
+ if (name === '' || INVALID_ATTRIBUTE_LOCAL_NAME.test(name)) {
28
+ throwInvalidCharacterError(name);
29
+ }
30
+ }
31
+
32
+ export function validateAndExtractQualifiedName(
33
+ namespaceValue: unknown,
34
+ qualifiedNameValue: unknown,
35
+ kind: 'element' | 'attribute',
36
+ ) {
37
+ const namespace = normalizeNamespace(namespaceValue);
38
+ const qualifiedName = String(qualifiedNameValue);
39
+ const separator = qualifiedName.indexOf(':');
40
+ const prefix = separator < 0 ? null : qualifiedName.slice(0, separator);
41
+ const localName =
42
+ separator < 0 ? qualifiedName : qualifiedName.slice(separator + 1);
43
+
44
+ if (
45
+ (prefix != null &&
46
+ (prefix === '' || INVALID_NAMESPACE_PREFIX.test(prefix))) ||
47
+ (kind === 'element'
48
+ ? !VALID_ELEMENT_LOCAL_NAME.test(localName)
49
+ : localName === '' || INVALID_ATTRIBUTE_LOCAL_NAME.test(localName))
50
+ ) {
51
+ throwInvalidCharacterError(qualifiedName);
52
+ }
53
+
54
+ if (prefix != null && namespace == null) {
55
+ throw createDOMException(
56
+ `A namespace is required for the prefix in "${qualifiedName}"`,
57
+ 'NamespaceError',
58
+ );
59
+ }
60
+
61
+ if (prefix === 'xml' && namespace !== XML_NAMESPACE) {
62
+ throw createDOMException(
63
+ `The xml prefix requires the XML namespace`,
64
+ 'NamespaceError',
65
+ );
66
+ }
67
+
68
+ if (
69
+ (qualifiedName === 'xmlns' || prefix === 'xmlns') &&
70
+ namespace !== XMLNS_NAMESPACE
71
+ ) {
72
+ throw createDOMException(
73
+ `The xmlns name requires the XMLNS namespace`,
74
+ 'NamespaceError',
75
+ );
76
+ }
77
+
78
+ if (
79
+ namespace === XMLNS_NAMESPACE &&
80
+ qualifiedName !== 'xmlns' &&
81
+ prefix !== 'xmlns'
82
+ ) {
83
+ throw createDOMException(
84
+ `The XMLNS namespace requires the xmlns name or prefix`,
85
+ 'NamespaceError',
86
+ );
87
+ }
88
+
89
+ return {namespace, qualifiedName, prefix, localName};
90
+ }
91
+
92
+ function throwInvalidCharacterError(name: string): never {
93
+ throw createDOMException(`Invalid name: "${name}"`, 'InvalidCharacterError');
94
+ }