@vidact/runtime 0.2.0-beta.0 → 0.2.0-beta.4

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 (244) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +1 -1
  3. package/dist/actions.d.ts +32 -30
  4. package/dist/actions.d.ts.map +1 -1
  5. package/dist/actions.js +251 -266
  6. package/dist/actions.js.map +1 -1
  7. package/dist/async.d.ts +20 -17
  8. package/dist/async.d.ts.map +1 -1
  9. package/dist/async.js +18 -17
  10. package/dist/async.js.map +1 -1
  11. package/dist/compiled/async-resource.d.ts +9 -6
  12. package/dist/compiled/async-resource.d.ts.map +1 -1
  13. package/dist/compiled/async-resource.js +69 -77
  14. package/dist/compiled/async-resource.js.map +1 -1
  15. package/dist/compiled/core.d.ts +136 -147
  16. package/dist/compiled/core.d.ts.map +1 -1
  17. package/dist/compiled/core.js +2669 -2985
  18. package/dist/compiled/core.js.map +1 -1
  19. package/dist/compiled/types.d.ts +44 -56
  20. package/dist/compiled/types.d.ts.map +1 -1
  21. package/dist/compiled/types.js +10 -6
  22. package/dist/compiled/types.js.map +1 -1
  23. package/dist/compiled/validation.d.ts +4 -1
  24. package/dist/compiled/validation.d.ts.map +1 -1
  25. package/dist/compiled/validation.js +17 -20
  26. package/dist/compiled/validation.js.map +1 -1
  27. package/dist/component-spread.d.ts +5 -3
  28. package/dist/component-spread.d.ts.map +1 -1
  29. package/dist/component-spread.js +49 -67
  30. package/dist/component-spread.js.map +1 -1
  31. package/dist/concurrent.d.ts +20 -18
  32. package/dist/concurrent.d.ts.map +1 -1
  33. package/dist/concurrent.js +191 -210
  34. package/dist/concurrent.js.map +1 -1
  35. package/dist/direct-dom.d.ts +17 -12
  36. package/dist/direct-dom.d.ts.map +1 -1
  37. package/dist/direct-dom.js +187 -221
  38. package/dist/direct-dom.js.map +1 -1
  39. package/dist/dom/attributes.d.ts +9 -6
  40. package/dist/dom/attributes.d.ts.map +1 -1
  41. package/dist/dom/attributes.js +65 -47
  42. package/dist/dom/attributes.js.map +1 -1
  43. package/dist/dom/events.d.ts +5 -2
  44. package/dist/dom/events.d.ts.map +1 -1
  45. package/dist/dom/events.js +53 -65
  46. package/dist/dom/events.js.map +1 -1
  47. package/dist/dom/forms.d.ts +9 -6
  48. package/dist/dom/forms.d.ts.map +1 -1
  49. package/dist/dom/forms.js +152 -198
  50. package/dist/dom/forms.js.map +1 -1
  51. package/dist/dom/intrinsic.d.ts +17 -15
  52. package/dist/dom/intrinsic.d.ts.map +1 -1
  53. package/dist/dom/intrinsic.js +48 -50
  54. package/dist/dom/intrinsic.js.map +1 -1
  55. package/dist/dom/namespace.d.ts +4 -1
  56. package/dist/dom/namespace.d.ts.map +1 -1
  57. package/dist/dom/namespace.js +25 -28
  58. package/dist/dom/namespace.js.map +1 -1
  59. package/dist/dom/namespaces.d.ts +11 -8
  60. package/dist/dom/namespaces.d.ts.map +1 -1
  61. package/dist/dom/namespaces.js +14 -12
  62. package/dist/dom/namespaces.js.map +1 -1
  63. package/dist/dom/properties.d.ts +18 -16
  64. package/dist/dom/properties.d.ts.map +1 -1
  65. package/dist/dom/properties.js +273 -296
  66. package/dist/dom/properties.js.map +1 -1
  67. package/dist/dom/style-properties.d.ts +4 -1
  68. package/dist/dom/style-properties.d.ts.map +1 -1
  69. package/dist/dom/style-properties.js +49 -45
  70. package/dist/dom/style-properties.js.map +1 -1
  71. package/dist/dom/styles.d.ts +5 -2
  72. package/dist/dom/styles.d.ts.map +1 -1
  73. package/dist/dom/styles.js +47 -67
  74. package/dist/dom/styles.js.map +1 -1
  75. package/dist/dom-forms.d.ts +4 -1
  76. package/dist/dom-forms.d.ts.map +1 -1
  77. package/dist/dom-forms.js +16 -13
  78. package/dist/dom-forms.js.map +1 -1
  79. package/dist/dom-styles.d.ts +4 -1
  80. package/dist/dom-styles.d.ts.map +1 -1
  81. package/dist/dom-styles.js +10 -7
  82. package/dist/dom-styles.js.map +1 -1
  83. package/dist/framework-hydrate.d.ts +20 -17
  84. package/dist/framework-hydrate.d.ts.map +1 -1
  85. package/dist/framework-hydrate.js +128 -146
  86. package/dist/framework-hydrate.js.map +1 -1
  87. package/dist/framework-protocol.d.ts +27 -25
  88. package/dist/framework-protocol.d.ts.map +1 -1
  89. package/dist/framework-protocol.js +173 -211
  90. package/dist/framework-protocol.js.map +1 -1
  91. package/dist/framework.d.ts +34 -32
  92. package/dist/framework.d.ts.map +1 -1
  93. package/dist/framework.js +74 -93
  94. package/dist/framework.js.map +1 -1
  95. package/dist/hydrate.d.ts +11 -8
  96. package/dist/hydrate.d.ts.map +1 -1
  97. package/dist/hydrate.js +13 -9
  98. package/dist/hydrate.js.map +1 -1
  99. package/dist/hydration-bridge.d.ts +61 -58
  100. package/dist/hydration-bridge.d.ts.map +1 -1
  101. package/dist/hydration-bridge.js +67 -66
  102. package/dist/hydration-bridge.js.map +1 -1
  103. package/dist/hydration.d.ts +31 -28
  104. package/dist/hydration.d.ts.map +1 -1
  105. package/dist/hydration.js +376 -462
  106. package/dist/hydration.js.map +1 -1
  107. package/dist/index.d.ts +13 -13
  108. package/dist/index.js +11 -13
  109. package/dist/indexed-list.d.ts +8 -5
  110. package/dist/indexed-list.d.ts.map +1 -1
  111. package/dist/indexed-list.js +14 -7
  112. package/dist/indexed-list.js.map +1 -1
  113. package/dist/jsx-runtime.d.ts +10 -8
  114. package/dist/jsx-runtime.d.ts.map +1 -1
  115. package/dist/jsx-runtime.js +28 -23
  116. package/dist/jsx-runtime.js.map +1 -1
  117. package/dist/keyed-list.d.ts +13 -14
  118. package/dist/keyed-list.d.ts.map +1 -1
  119. package/dist/keyed-list.js +132 -160
  120. package/dist/keyed-list.js.map +1 -1
  121. package/dist/metadata.d.ts +6 -3
  122. package/dist/metadata.d.ts.map +1 -1
  123. package/dist/metadata.js +131 -164
  124. package/dist/metadata.js.map +1 -1
  125. package/dist/nested-prop.d.ts +4 -2
  126. package/dist/nested-prop.d.ts.map +1 -1
  127. package/dist/nested-prop.js +17 -16
  128. package/dist/nested-prop.js.map +1 -1
  129. package/dist/profiling.d.ts +11 -8
  130. package/dist/profiling.d.ts.map +1 -1
  131. package/dist/profiling.js +17 -17
  132. package/dist/profiling.js.map +1 -1
  133. package/dist/protocol.d.ts +5 -2
  134. package/dist/protocol.d.ts.map +1 -1
  135. package/dist/protocol.js +8 -5
  136. package/dist/protocol.js.map +1 -1
  137. package/dist/raw-html.d.ts +5 -2
  138. package/dist/raw-html.d.ts.map +1 -1
  139. package/dist/raw-html.js +86 -146
  140. package/dist/raw-html.js.map +1 -1
  141. package/dist/ref.d.ts +8 -3
  142. package/dist/ref.d.ts.map +1 -1
  143. package/dist/ref.js +10 -2
  144. package/dist/ref.js.map +1 -1
  145. package/dist/renderable-protocol.d.ts +26 -8
  146. package/dist/renderable-protocol.d.ts.map +1 -1
  147. package/dist/renderable-protocol.js +30 -9
  148. package/dist/renderable-protocol.js.map +1 -1
  149. package/dist/renderable.d.ts +27 -22
  150. package/dist/renderable.d.ts.map +1 -1
  151. package/dist/renderable.js +141 -133
  152. package/dist/renderable.js.map +1 -1
  153. package/dist/retained-ui.d.ts +9 -6
  154. package/dist/retained-ui.d.ts.map +1 -1
  155. package/dist/retained-ui.js +12 -10
  156. package/dist/retained-ui.js.map +1 -1
  157. package/dist/root.d.ts +18 -15
  158. package/dist/root.d.ts.map +1 -1
  159. package/dist/root.js +73 -85
  160. package/dist/root.js.map +1 -1
  161. package/dist/scheduler.d.ts +10 -8
  162. package/dist/scheduler.d.ts.map +1 -1
  163. package/dist/scheduler.js +97 -105
  164. package/dist/scheduler.js.map +1 -1
  165. package/dist/server-framework.d.ts +45 -42
  166. package/dist/server-framework.d.ts.map +1 -1
  167. package/dist/server-framework.js +209 -217
  168. package/dist/server-framework.js.map +1 -1
  169. package/dist/server.d.ts +130 -128
  170. package/dist/server.d.ts.map +1 -1
  171. package/dist/server.js +592 -706
  172. package/dist/server.js.map +1 -1
  173. package/dist/shared/promise.d.ts +6 -3
  174. package/dist/shared/promise.d.ts.map +1 -1
  175. package/dist/shared/promise.js +21 -19
  176. package/dist/shared/promise.js.map +1 -1
  177. package/dist/source-mask.d.ts +9 -6
  178. package/dist/source-mask.d.ts.map +1 -1
  179. package/dist/source-mask.js +45 -63
  180. package/dist/source-mask.js.map +1 -1
  181. package/dist/spread.d.ts +5 -2
  182. package/dist/spread.d.ts.map +1 -1
  183. package/dist/spread.js +79 -99
  184. package/dist/spread.js.map +1 -1
  185. package/dist/state-slot.d.ts +18 -15
  186. package/dist/state-slot.d.ts.map +1 -1
  187. package/dist/state-slot.js +43 -42
  188. package/dist/state-slot.js.map +1 -1
  189. package/dist/testing.d.ts +9 -6
  190. package/dist/testing.d.ts.map +1 -1
  191. package/dist/testing.js +16 -12
  192. package/dist/testing.js.map +1 -1
  193. package/package.json +8 -7
  194. package/src/actions.ts +415 -0
  195. package/src/async.ts +78 -0
  196. package/src/compiled/async-resource.ts +113 -0
  197. package/src/compiled/core.ts +4026 -0
  198. package/src/compiled/types.ts +96 -0
  199. package/src/compiled/validation.ts +23 -0
  200. package/src/component-spread.ts +86 -0
  201. package/src/concurrent.ts +310 -0
  202. package/src/direct-dom.ts +365 -0
  203. package/src/dom/attributes.ts +55 -0
  204. package/src/dom/events.ts +93 -0
  205. package/src/dom/forms.ts +220 -0
  206. package/src/dom/intrinsic.ts +94 -0
  207. package/src/dom/namespace.ts +30 -0
  208. package/src/dom/namespaces.ts +14 -0
  209. package/src/dom/properties.ts +355 -0
  210. package/src/dom/style-properties.ts +46 -0
  211. package/src/dom/styles.ts +86 -0
  212. package/src/dom-forms.ts +23 -0
  213. package/src/dom-styles.ts +11 -0
  214. package/src/env.d.ts +6 -0
  215. package/src/framework-hydrate.ts +224 -0
  216. package/src/framework-protocol.ts +335 -0
  217. package/src/framework.ts +161 -0
  218. package/src/hydrate.ts +38 -0
  219. package/src/hydration-bridge.ts +183 -0
  220. package/src/hydration.ts +556 -0
  221. package/src/index.ts +92 -0
  222. package/src/indexed-list.ts +20 -0
  223. package/src/jsx-runtime.ts +55 -0
  224. package/src/keyed-list.ts +233 -0
  225. package/src/metadata.ts +212 -0
  226. package/src/nested-prop.ts +27 -0
  227. package/src/profiling.ts +43 -0
  228. package/src/protocol.ts +8 -0
  229. package/src/raw-html.ts +206 -0
  230. package/src/ref.ts +13 -0
  231. package/src/renderable-protocol.ts +63 -0
  232. package/src/renderable.ts +283 -0
  233. package/src/retained-ui.ts +19 -0
  234. package/src/root.ts +122 -0
  235. package/src/scheduler.ts +136 -0
  236. package/src/server-framework.ts +397 -0
  237. package/src/server.ts +1143 -0
  238. package/src/shared/promise.ts +28 -0
  239. package/src/source-mask.ts +83 -0
  240. package/src/spread.ts +131 -0
  241. package/src/state-slot.ts +76 -0
  242. package/src/testing.ts +27 -0
  243. package/dist/index.d.ts.map +0 -1
  244. package/dist/index.js.map +0 -1
@@ -0,0 +1,365 @@
1
+ import {
2
+ isCompiledBinding,
3
+ isStructuralBinding,
4
+ adoptCompiledRoot,
5
+ constructCompiledComponent,
6
+ mountCompiledBinding,
7
+ mountCompiledProp,
8
+ mountCompiledRef,
9
+ mountCompiledPropTransition,
10
+ queueElementRef,
11
+ registerCompiledCleanup,
12
+ } from './compiled/core.ts'
13
+ import type {
14
+ CompiledBinding,
15
+ CompiledContext,
16
+ CompiledRenderValue,
17
+ StructuralBinding,
18
+ } from './compiled/types.ts'
19
+ import { hasInvalidChild } from './compiled/validation.ts'
20
+ import { attachEventProp, isEventProp } from './dom/events.ts'
21
+ import {
22
+ INTERNAL_NAMESPACE_PROP,
23
+ createComponentProps,
24
+ createIntrinsicElement,
25
+ intrinsicChildrenNamespace,
26
+ readIntrinsicNamespace,
27
+ resolveIntrinsicNamespace,
28
+ withIntrinsicNamespace,
29
+ } from './dom/intrinsic.ts'
30
+ import { intrinsicNamespaceUrl } from './dom/namespaces.ts'
31
+ import {
32
+ applyDomProp,
33
+ ensureControlledFormRestoration,
34
+ isControlledFormProp,
35
+ restoreControlledFormState,
36
+ } from './dom/properties.ts'
37
+ import {
38
+ claimHydrationElement,
39
+ claimHydrationArrayRange,
40
+ claimHydrationNode,
41
+ claimHydrationText,
42
+ createHydrationFragment,
43
+ finishHydrationArrayRange,
44
+ hydrationFragmentChildren,
45
+ isHydrating,
46
+ } from './hydration-bridge.ts'
47
+ import { mountRawHtmlProp } from './raw-html.ts'
48
+ import {
49
+ isRenderableProtocol,
50
+ materializeRenderable,
51
+ type RenderableProtocol,
52
+ } from './renderable-protocol.ts'
53
+
54
+ const DEV = typeof __VIDACT_DEV__ === 'undefined' || __VIDACT_DEV__
55
+ const UNSAFE_HTML = typeof __VIDACT_UNSAFE_HTML__ === 'undefined' || __VIDACT_UNSAFE_HTML__
56
+
57
+ export type DirectChild =
58
+ | Node
59
+ | string
60
+ | number
61
+ | bigint
62
+ | boolean
63
+ | null
64
+ | undefined
65
+ | readonly DirectChild[]
66
+ | CompiledBinding<unknown>
67
+ | StructuralBinding
68
+ | RenderableProtocol
69
+ export type DirectProps = Record<string, unknown> | null
70
+ export type DirectComponent = (props: Record<string, unknown>) => CompiledRenderValue
71
+
72
+ export const Fragment: symbol = Symbol.for('vidact.v1.Fragment')
73
+ let frameworkMetadataHandler: ((element: Element, props: DirectProps) => Node) | undefined
74
+
75
+ /** @internal */
76
+ export function installFrameworkMetadata(
77
+ handler: (element: Element, props: DirectProps) => Node,
78
+ ): void {
79
+ frameworkMetadataHandler = handler
80
+ }
81
+
82
+ export function h<Tag extends keyof HTMLElementTagNameMap>(
83
+ type: Tag,
84
+ props: DirectProps,
85
+ ...children: DirectChild[]
86
+ ): HTMLElementTagNameMap[Tag]
87
+ export function h<Tag extends keyof MathMLElementTagNameMap>(
88
+ type: Tag,
89
+ props: DirectProps,
90
+ ...children: DirectChild[]
91
+ ): MathMLElementTagNameMap[Tag]
92
+ export function h(
93
+ type: typeof Fragment,
94
+ props: DirectProps,
95
+ ...children: DirectChild[]
96
+ ): DocumentFragment
97
+ export function h(
98
+ type: DirectComponent,
99
+ props: DirectProps,
100
+ ...children: DirectChild[]
101
+ ): DirectChild
102
+ export function h<T>(
103
+ type: CompiledContext<T>,
104
+ props: DirectProps,
105
+ ...children: DirectChild[]
106
+ ): DirectChild
107
+ export function h(
108
+ type: string | typeof Fragment | DirectComponent | CompiledContext<unknown>,
109
+ props: DirectProps,
110
+ ...children: DirectChild[]
111
+ ): DirectChild
112
+ export function h(
113
+ type: string | typeof Fragment | DirectComponent | CompiledContext<unknown>,
114
+ props: DirectProps,
115
+ ...children: DirectChild[]
116
+ ): DirectChild {
117
+ if (typeof type === 'symbol') {
118
+ if (isHydrating()) return createHydrationFragment(children)
119
+ const fragment = document.createDocumentFragment()
120
+ appendChildren(fragment, children)
121
+ return fragment
122
+ }
123
+ if (typeof type === 'function') {
124
+ const namespace = readIntrinsicNamespace(props)
125
+ const component = type as DirectComponent
126
+ const root = constructCompiledComponent(
127
+ () =>
128
+ withIntrinsicNamespace(namespace, () => component(createComponentProps(props, children))),
129
+ type,
130
+ )
131
+ adoptCompiledRoot(root)
132
+ return root
133
+ }
134
+
135
+ const namespace = resolveIntrinsicNamespace(type, readIntrinsicNamespace(props))
136
+ const namespaceUrl = intrinsicNamespaceUrl(namespace)
137
+ const element =
138
+ claimHydrationElement(type, namespaceUrl, (candidate) =>
139
+ matchesHydrationElement(candidate, props, children),
140
+ ) ?? createIntrinsicElement(document, type, namespace)
141
+ const restoreAfterChildren = applyProps(element, props, children)
142
+ withIntrinsicNamespace(intrinsicChildrenNamespace(type, namespace), () =>
143
+ appendChildren(element, children),
144
+ )
145
+ if (restoreAfterChildren) restoreControlledFormState(element)
146
+ if (frameworkMetadataHandler !== undefined && namespace === 'html') {
147
+ return frameworkMetadataHandler(element, props)
148
+ }
149
+ return element
150
+ }
151
+
152
+ export function createElement(
153
+ type: string | typeof Fragment | DirectComponent,
154
+ props: DirectProps,
155
+ ...children: DirectChild[]
156
+ ): DirectChild {
157
+ if (children.length === 0 && props !== null && Object.hasOwn(props, 'children')) {
158
+ return h(type, props, props.children as DirectChild)
159
+ }
160
+ return h(type, props, ...children)
161
+ }
162
+
163
+ function matchesHydrationElement(
164
+ element: Element,
165
+ props: DirectProps,
166
+ children: readonly DirectChild[],
167
+ ): boolean {
168
+ const classInput = props?.className
169
+ const expectedClass = isCompiledBinding(classInput) ? classInput[1]() : classInput
170
+ if (typeof expectedClass === 'string' && element.getAttribute('class') !== expectedClass) {
171
+ return false
172
+ }
173
+ const idInput = props?.id
174
+ const expectedId = isCompiledBinding(idInput) ? idInput[1]() : idInput
175
+ if (typeof expectedId === 'string' && element.getAttribute('id') !== expectedId) return false
176
+
177
+ const expectedNodes: Node[] = []
178
+ collectHydrationChildNodes(children, expectedNodes)
179
+ if (expectedNodes.some((node) => !element.contains(node))) return false
180
+
181
+ const expectedText = staticHydrationText(children)
182
+ return expectedText === undefined || element.textContent === expectedText
183
+ }
184
+
185
+ function collectHydrationChildNodes(children: readonly DirectChild[], nodes: Node[]): void {
186
+ for (const child of children) {
187
+ if (Array.isArray(child)) {
188
+ collectHydrationChildNodes(child, nodes)
189
+ continue
190
+ }
191
+ if (child instanceof DocumentFragment) {
192
+ const fragmentChildren = hydrationFragmentChildren(child)
193
+ if (fragmentChildren !== undefined) {
194
+ collectHydrationChildNodes(fragmentChildren as readonly DirectChild[], nodes)
195
+ }
196
+ continue
197
+ }
198
+ if (child instanceof Node) nodes.push(child)
199
+ }
200
+ }
201
+
202
+ function staticHydrationText(children: readonly DirectChild[]): string | undefined {
203
+ let text = ''
204
+ for (const child of children) {
205
+ if (Array.isArray(child)) {
206
+ const nested = staticHydrationText(child)
207
+ if (nested === undefined) return undefined
208
+ text += nested
209
+ continue
210
+ }
211
+ if (child === null || child === undefined || typeof child === 'boolean') continue
212
+ if (isCompiledBinding(child)) {
213
+ const value = child[1]()
214
+ if (value === null || value === undefined || typeof value === 'boolean') continue
215
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'bigint') {
216
+ text += String(value)
217
+ continue
218
+ }
219
+ return undefined
220
+ }
221
+ if (typeof child === 'string' || typeof child === 'number' || typeof child === 'bigint') {
222
+ text += String(child)
223
+ continue
224
+ }
225
+ return undefined
226
+ }
227
+ return text
228
+ }
229
+
230
+ function applyProps(
231
+ element: Element,
232
+ props: DirectProps,
233
+ children: readonly DirectChild[],
234
+ ): boolean {
235
+ if (props === null) return false
236
+ const rawHtml = props.dangerouslySetInnerHTML
237
+ let restoreAfterChildren = false
238
+ let hasControlledRestoration = false
239
+ for (const name in props) {
240
+ if (!Object.hasOwn(props, name)) continue
241
+ const value = props[name]
242
+ if (name.startsWith('__vidactSpread') && typeof value === 'function') {
243
+ restoreAfterChildren = Boolean(value(element)) || restoreAfterChildren
244
+ continue
245
+ }
246
+ if (name === 'key' || name === 'children' || name === INTERNAL_NAMESPACE_PROP) continue
247
+ if (name === 'dangerouslySetInnerHTML') continue
248
+ if (!hasControlledRestoration && isControlledFormProp(element, name)) {
249
+ registerCompiledCleanup(ensureControlledFormRestoration(element))
250
+ hasControlledRestoration = true
251
+ }
252
+ if (name === 'value' && element instanceof HTMLSelectElement) restoreAfterChildren = true
253
+ if (name === 'ref') {
254
+ if (isCompiledBinding(value)) {
255
+ mountCompiledRef(element, value)
256
+ continue
257
+ }
258
+ // A generated renderable wrapper always projects a `ref` field, even when
259
+ // its input does not own one. Do not let that nullish projection replace a
260
+ // pending ref mounted by an earlier reactive spread. Authored overrides
261
+ // are already excluded from that spread by `compiledSpread`.
262
+ if (value !== null && value !== undefined) queueElementRef(element, value)
263
+ continue
264
+ }
265
+ if (isCompiledBinding(value)) {
266
+ if (isEventProp(name)) {
267
+ mountCompiledProp(value, (next) => attachEventProp(element, name, next))
268
+ continue
269
+ }
270
+ if (element instanceof HTMLSelectElement && name === 'multiple') {
271
+ mountCompiledPropTransition(
272
+ value,
273
+ (initial) => applyDomProp(element, name, initial),
274
+ // oxlint-disable no-sparse-arrays -- Preserve the compact positional ABI.
275
+ (next, previous) => [
276
+ () => applyDomProp(element, name, next),
277
+ () => {
278
+ applyDomProp(element, name, previous)
279
+ restoreControlledFormState(element)
280
+ },
281
+ ,
282
+ () => restoreControlledFormState(element),
283
+ -1,
284
+ ],
285
+ // oxlint-enable no-sparse-arrays
286
+ )
287
+ continue
288
+ }
289
+ mountCompiledProp(value, (next) => applyDomProp(element, name, next))
290
+ continue
291
+ }
292
+ if (isEventProp(name)) {
293
+ registerCompiledCleanup(attachEventProp(element, name, value))
294
+ continue
295
+ }
296
+ applyDomProp(element, name, value)
297
+ }
298
+ if (rawHtml === null || rawHtml === undefined) return restoreAfterChildren
299
+ if (!(element instanceof HTMLElement)) {
300
+ throw new Error(
301
+ DEV ? 'dangerouslySetInnerHTML on SVG and MathML elements is not supported' : 'V102',
302
+ )
303
+ }
304
+ if (!UNSAFE_HTML) {
305
+ throw new Error(
306
+ DEV ? 'dangerouslySetInnerHTML requires the unsafe-html compiler feature' : 'V104',
307
+ )
308
+ }
309
+ mountRawHtmlProp(element, rawHtml, children)
310
+ return restoreAfterChildren
311
+ }
312
+
313
+ function appendChildren(parent: Node, children: readonly DirectChild[]): void {
314
+ if (hasInvalidChild(children)) {
315
+ throw new TypeError(
316
+ DEV ? 'unsupported direct child value; expected a DOM node or owned block' : 'V103',
317
+ )
318
+ }
319
+ for (const child of children) appendChild(parent, child)
320
+ }
321
+
322
+ function appendChild(parent: Node, child: DirectChild): void {
323
+ if (child === null || child === undefined || typeof child === 'boolean') {
324
+ claimHydrationText(parent, '')
325
+ return
326
+ }
327
+ if (isStructuralBinding(child)) {
328
+ child[1](parent, null)
329
+ return
330
+ }
331
+ if (isCompiledBinding(child)) {
332
+ mountCompiledBinding(parent, child)
333
+ return
334
+ }
335
+ if (isRenderableProtocol(child)) {
336
+ appendChild(parent, materializeRenderable(child) as DirectChild)
337
+ return
338
+ }
339
+ if (Array.isArray(child)) {
340
+ const hydratedRange = claimHydrationArrayRange(parent)
341
+ for (const item of child) appendChild(parent, item)
342
+ if (hydratedRange !== undefined) finishHydrationArrayRange(parent, hydratedRange[1])
343
+ return
344
+ }
345
+ if (child instanceof Node) {
346
+ if (child instanceof DocumentFragment) {
347
+ const hydrationChildren = hydrationFragmentChildren(child)
348
+ if (hydrationChildren !== undefined) {
349
+ for (const item of hydrationChildren) appendChild(parent, item as DirectChild)
350
+ return
351
+ }
352
+ }
353
+ if (!claimHydrationNode(parent, child)) parent.appendChild(child)
354
+ return
355
+ }
356
+ if (typeof child === 'object' || typeof child === 'function' || typeof child === 'symbol') {
357
+ throw new TypeError(
358
+ DEV ? 'unsupported direct child value; expected a DOM node or owned block' : 'V103',
359
+ )
360
+ }
361
+ const content = String(child)
362
+ if (claimHydrationText(parent, content) === undefined) {
363
+ parent.appendChild(document.createTextNode(content))
364
+ }
365
+ }
@@ -0,0 +1,55 @@
1
+ export const SERVER_BOOLEAN_ATTRIBUTES: ReadonlySet<string> = new Set([
2
+ 'allowFullScreen',
3
+ 'async',
4
+ 'autoFocus',
5
+ 'autoPlay',
6
+ 'checked',
7
+ 'controls',
8
+ 'default',
9
+ 'defaultChecked',
10
+ 'defer',
11
+ 'disabled',
12
+ 'disablePictureInPicture',
13
+ 'disableRemotePlayback',
14
+ 'formNoValidate',
15
+ 'hidden',
16
+ 'inert',
17
+ 'itemScope',
18
+ 'loop',
19
+ 'multiple',
20
+ 'muted',
21
+ 'noModule',
22
+ 'noValidate',
23
+ 'open',
24
+ 'playsInline',
25
+ 'readOnly',
26
+ 'required',
27
+ 'reversed',
28
+ 'selected',
29
+ ])
30
+
31
+ const SERVER_ATTRIBUTE_ALIASES: Readonly<Record<string, string>> = {
32
+ acceptCharset: 'accept-charset',
33
+ className: 'class',
34
+ crossOrigin: 'crossorigin',
35
+ defaultChecked: 'checked',
36
+ defaultValue: 'value',
37
+ formAction: 'formaction',
38
+ htmlFor: 'for',
39
+ httpEquiv: 'http-equiv',
40
+ itemID: 'itemid',
41
+ itemProp: 'itemprop',
42
+ itemRef: 'itemref',
43
+ itemScope: 'itemscope',
44
+ itemType: 'itemtype',
45
+ }
46
+
47
+ export const VALID_ATTRIBUTE_NAME = /^[A-Za-z_:][A-Za-z0-9:._-]*$/
48
+
49
+ export const META_IDENTITY_PROPS = ['charSet', 'name', 'property', 'httpEquiv'] as const
50
+ export const LINK_IDENTITY_PROPS = ['rel', 'href', 'as'] as const
51
+ export const META_IDENTITY_ATTRIBUTES = ['charset', 'name', 'property', 'http-equiv'] as const
52
+
53
+ export function serverHtmlAttributeName(name: string): string {
54
+ return SERVER_ATTRIBUTE_ALIASES[name] ?? name
55
+ }
@@ -0,0 +1,93 @@
1
+ import { isReactFormChangeEvent, restoreControlledFormState } from './properties.ts'
2
+
3
+ const DEV = typeof __VIDACT_DEV__ === 'undefined' || __VIDACT_DEV__
4
+
5
+ function isSupportedReactEventName(name: string): boolean {
6
+ return '|Abort|AnimationEnd|AnimationIteration|AnimationStart|AuxClick|BeforeInput|BeforeToggle|Blur|CanPlay|CanPlayThrough|Cancel|Change|Click|Close|CompositionEnd|CompositionStart|CompositionUpdate|ContextMenu|Copy|Cut|DoubleClick|Drag|DragEnd|DragEnter|DragExit|DragLeave|DragOver|DragStart|Drop|DurationChange|Emptied|Encrypted|Ended|Error|Focus|GotPointerCapture|Input|Invalid|KeyDown|KeyPress|KeyUp|Load|LoadedData|LoadedMetadata|LoadStart|LostPointerCapture|MouseDown|MouseEnter|MouseLeave|MouseMove|MouseOut|MouseOver|MouseUp|Paste|Pause|Play|Playing|PointerCancel|PointerDown|PointerEnter|PointerLeave|PointerMove|PointerOut|PointerOver|PointerUp|Progress|RateChange|Reset|Resize|Scroll|ScrollEnd|Seeked|Seeking|Select|Stalled|Submit|Suspend|TimeUpdate|Toggle|TouchCancel|TouchEnd|TouchMove|TouchStart|TransitionCancel|TransitionEnd|TransitionRun|TransitionStart|VolumeChange|Waiting|Wheel|'.includes(
7
+ `|${name}|`,
8
+ )
9
+ }
10
+
11
+ export function attachEventProp(element: Element, name: string, value: unknown): () => void {
12
+ if (value === null || value === undefined) return () => {}
13
+ if (typeof value !== 'function') {
14
+ throw new TypeError(DEV ? `event prop ${name} must be a function, null, or undefined` : 'V201')
15
+ }
16
+
17
+ const reactEventName = name.slice(2)
18
+ const capture = reactEventName.endsWith('Capture') && !reactEventName.endsWith('PointerCapture')
19
+ const eventNameWithoutPhase = capture
20
+ ? reactEventName.slice(0, -'Capture'.length)
21
+ : reactEventName
22
+ if (DEV && !isSupportedReactEventName(eventNameWithoutPhase)) {
23
+ throw new TypeError(DEV ? `unsupported event prop ${name}` : 'V202')
24
+ }
25
+ if (eventNameWithoutPhase === 'Change') {
26
+ return attachReactChangeEvent(element, value as EventListener, capture)
27
+ }
28
+
29
+ const eventName = nativeEventName(eventNameWithoutPhase)
30
+ if (eventName === 'input') {
31
+ const dispatch = (event: Event): void =>
32
+ invokeFormListener(element, value as EventListener, event)
33
+ element.addEventListener(eventName, dispatch, capture)
34
+ return () => element.removeEventListener(eventName, dispatch, capture)
35
+ }
36
+ const dispatch = (event: Event): void => (value as EventListener)(reactShapedEvent(event))
37
+ element.addEventListener(eventName, dispatch, capture)
38
+ return () => element.removeEventListener(eventName, dispatch, capture)
39
+ }
40
+
41
+ export function isEventProp(name: string): boolean {
42
+ return /^on[A-Z]/.test(name)
43
+ }
44
+
45
+ function attachReactChangeEvent(
46
+ element: Element,
47
+ listener: EventListener,
48
+ capture: boolean,
49
+ ): () => void {
50
+ const dispatch = (event: Event): void => {
51
+ const target = event.target
52
+ if (!(target instanceof Element) || !isReactFormChangeEvent(target, event.type)) return
53
+ invokeFormListener(element, listener, event)
54
+ }
55
+ element.addEventListener('input', dispatch, capture)
56
+ element.addEventListener('change', dispatch, capture)
57
+ return () => {
58
+ element.removeEventListener('input', dispatch, capture)
59
+ element.removeEventListener('change', dispatch, capture)
60
+ }
61
+ }
62
+
63
+ function invokeFormListener(element: Element, listener: EventListener, event: Event): void {
64
+ try {
65
+ listener(reactShapedEvent(event))
66
+ } finally {
67
+ const target = event.target as Element
68
+ if (isReactFormChangeEvent(target, event.type) && (target === element || event.cancelBubble)) {
69
+ restoreControlledFormState(target)
70
+ }
71
+ }
72
+ }
73
+
74
+ function reactShapedEvent(event: Event): Event {
75
+ const shaped = event as Event & {
76
+ nativeEvent?: Event
77
+ isDefaultPrevented?: () => boolean
78
+ isPropagationStopped?: () => boolean
79
+ persist?: () => void
80
+ }
81
+ shaped.nativeEvent ??= event
82
+ shaped.isDefaultPrevented ??= () => event.defaultPrevented
83
+ shaped.isPropagationStopped ??= () => event.cancelBubble
84
+ shaped.persist ??= () => {}
85
+ return shaped
86
+ }
87
+
88
+ function nativeEventName(reactEventName: string): string {
89
+ if (reactEventName === 'DoubleClick') return 'dblclick'
90
+ if (reactEventName === 'Focus') return 'focusin'
91
+ if (reactEventName === 'Blur') return 'focusout'
92
+ return reactEventName.toLowerCase()
93
+ }