@typed/template 0.9.6 → 0.11.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 (300) hide show
  1. package/RenderQueue/package.json +6 -0
  2. package/dist/cjs/Directive.js +1 -1
  3. package/dist/cjs/Directive.js.map +1 -1
  4. package/dist/cjs/ElementRef.js +1 -1
  5. package/dist/cjs/ElementRef.js.map +1 -1
  6. package/dist/cjs/ElementSource.js +1 -1
  7. package/dist/cjs/ElementSource.js.map +1 -1
  8. package/dist/cjs/EventHandler.js +15 -4
  9. package/dist/cjs/EventHandler.js.map +1 -1
  10. package/dist/cjs/Html.js +84 -44
  11. package/dist/cjs/Html.js.map +1 -1
  12. package/dist/cjs/HtmlChunk.js +67 -21
  13. package/dist/cjs/HtmlChunk.js.map +1 -1
  14. package/dist/cjs/Hydrate.js +6 -6
  15. package/dist/cjs/Hydrate.js.map +1 -1
  16. package/dist/cjs/Many.js +4 -4
  17. package/dist/cjs/Many.js.map +1 -1
  18. package/dist/cjs/Meta.js +10 -3
  19. package/dist/cjs/Meta.js.map +1 -1
  20. package/dist/cjs/Parser.js +1 -1
  21. package/dist/cjs/Placeholder.js +5 -9
  22. package/dist/cjs/Placeholder.js.map +1 -1
  23. package/dist/cjs/Platform.js +7 -5
  24. package/dist/cjs/Platform.js.map +1 -1
  25. package/dist/cjs/Render.js +8 -7
  26. package/dist/cjs/Render.js.map +1 -1
  27. package/dist/cjs/RenderContext.js +8 -92
  28. package/dist/cjs/RenderContext.js.map +1 -1
  29. package/dist/cjs/RenderEvent.js +9 -1
  30. package/dist/cjs/RenderEvent.js.map +1 -1
  31. package/dist/cjs/RenderQueue.js +341 -0
  32. package/dist/cjs/RenderQueue.js.map +1 -0
  33. package/dist/cjs/RenderTemplate.js +1 -1
  34. package/dist/cjs/RenderTemplate.js.map +1 -1
  35. package/dist/cjs/Template.js +12 -0
  36. package/dist/cjs/Template.js.map +1 -1
  37. package/dist/cjs/Test.js +64 -33
  38. package/dist/cjs/Test.js.map +1 -1
  39. package/dist/cjs/Vitest.js +12 -20
  40. package/dist/cjs/Vitest.js.map +1 -1
  41. package/dist/cjs/index.js +6 -3
  42. package/dist/cjs/index.js.map +1 -1
  43. package/dist/cjs/internal/EventSource.js +16 -9
  44. package/dist/cjs/internal/EventSource.js.map +1 -1
  45. package/dist/cjs/internal/HydrateContext.js.map +1 -1
  46. package/dist/cjs/internal/browser.js +11 -10
  47. package/dist/cjs/internal/browser.js.map +1 -1
  48. package/dist/cjs/internal/character-entities.js +2141 -0
  49. package/dist/cjs/internal/character-entities.js.map +1 -0
  50. package/dist/cjs/internal/errors.js +19 -2
  51. package/dist/cjs/internal/errors.js.map +1 -1
  52. package/dist/cjs/internal/indexRefCounter.js +36 -63
  53. package/dist/cjs/internal/indexRefCounter.js.map +1 -1
  54. package/dist/cjs/internal/parser.js +18 -17
  55. package/dist/cjs/internal/parser.js.map +1 -1
  56. package/dist/cjs/internal/parser2.js +382 -0
  57. package/dist/cjs/internal/parser2.js.map +1 -0
  58. package/dist/cjs/internal/server-parts.js +124 -0
  59. package/dist/cjs/internal/server-parts.js.map +1 -0
  60. package/dist/cjs/internal/server.js +15 -185
  61. package/dist/cjs/internal/server.js.map +1 -1
  62. package/dist/cjs/internal/utils.js +73 -9
  63. package/dist/cjs/internal/utils.js.map +1 -1
  64. package/dist/cjs/internal/v2/SyncPart.js +6 -0
  65. package/dist/cjs/internal/v2/SyncPart.js.map +1 -0
  66. package/dist/cjs/internal/v2/helpers.js +15 -0
  67. package/dist/cjs/internal/v2/helpers.js.map +1 -0
  68. package/dist/cjs/internal/v2/hydrate.js +202 -0
  69. package/dist/cjs/internal/v2/hydrate.js.map +1 -0
  70. package/dist/cjs/internal/v2/hydration-template.js +269 -0
  71. package/dist/cjs/internal/v2/hydration-template.js.map +1 -0
  72. package/dist/cjs/internal/v2/parts.js +169 -0
  73. package/dist/cjs/internal/v2/parts.js.map +1 -0
  74. package/dist/cjs/internal/v2/render-entry.js +110 -0
  75. package/dist/cjs/internal/v2/render-entry.js.map +1 -0
  76. package/dist/cjs/internal/v2/render-sync-parts.js +318 -0
  77. package/dist/cjs/internal/v2/render-sync-parts.js.map +1 -0
  78. package/dist/cjs/internal/v2/render.js +417 -0
  79. package/dist/cjs/internal/v2/render.js.map +1 -0
  80. package/dist/cjs/internal/v2/sync-parts.js +115 -0
  81. package/dist/cjs/internal/v2/sync-parts.js.map +1 -0
  82. package/dist/dts/ElementRef.d.ts +1 -1
  83. package/dist/dts/ElementRef.d.ts.map +1 -1
  84. package/dist/dts/ElementSource.d.ts +1 -1
  85. package/dist/dts/ElementSource.d.ts.map +1 -1
  86. package/dist/dts/EventHandler.d.ts +20 -8
  87. package/dist/dts/EventHandler.d.ts.map +1 -1
  88. package/dist/dts/Html.d.ts +6 -5
  89. package/dist/dts/Html.d.ts.map +1 -1
  90. package/dist/dts/HtmlChunk.d.ts.map +1 -1
  91. package/dist/dts/Hydrate.d.ts +1 -3
  92. package/dist/dts/Hydrate.d.ts.map +1 -1
  93. package/dist/dts/Many.d.ts +9 -11
  94. package/dist/dts/Many.d.ts.map +1 -1
  95. package/dist/dts/Meta.d.ts +5 -1
  96. package/dist/dts/Meta.d.ts.map +1 -1
  97. package/dist/dts/Parser.d.ts +1 -1
  98. package/dist/dts/Parser.d.ts.map +1 -1
  99. package/dist/dts/Part.d.ts +20 -56
  100. package/dist/dts/Part.d.ts.map +1 -1
  101. package/dist/dts/Placeholder.d.ts +6 -10
  102. package/dist/dts/Placeholder.d.ts.map +1 -1
  103. package/dist/dts/Platform.d.ts +2 -4
  104. package/dist/dts/Platform.d.ts.map +1 -1
  105. package/dist/dts/Render.d.ts +4 -8
  106. package/dist/dts/Render.d.ts.map +1 -1
  107. package/dist/dts/RenderContext.d.ts +3 -22
  108. package/dist/dts/RenderContext.d.ts.map +1 -1
  109. package/dist/dts/RenderEvent.d.ts +6 -1
  110. package/dist/dts/RenderEvent.d.ts.map +1 -1
  111. package/dist/dts/RenderQueue.d.ts +103 -0
  112. package/dist/dts/RenderQueue.d.ts.map +1 -0
  113. package/dist/dts/RenderTemplate.d.ts +3 -2
  114. package/dist/dts/RenderTemplate.d.ts.map +1 -1
  115. package/dist/dts/Renderable.d.ts +1 -1
  116. package/dist/dts/Template.d.ts +14 -1
  117. package/dist/dts/Template.d.ts.map +1 -1
  118. package/dist/dts/Test.d.ts +14 -1
  119. package/dist/dts/Test.d.ts.map +1 -1
  120. package/dist/dts/Vitest.d.ts +11 -8
  121. package/dist/dts/Vitest.d.ts.map +1 -1
  122. package/dist/dts/index.d.ts +4 -0
  123. package/dist/dts/index.d.ts.map +1 -1
  124. package/dist/dts/internal/EventSource.d.ts +2 -1
  125. package/dist/dts/internal/EventSource.d.ts.map +1 -1
  126. package/dist/dts/internal/browser.d.ts +3 -3
  127. package/dist/dts/internal/browser.d.ts.map +1 -1
  128. package/dist/dts/internal/character-entities.d.ts +2133 -0
  129. package/dist/dts/internal/character-entities.d.ts.map +1 -0
  130. package/dist/dts/internal/errors.d.ts +9 -1
  131. package/dist/dts/internal/errors.d.ts.map +1 -1
  132. package/dist/dts/internal/indexRefCounter.d.ts +0 -4
  133. package/dist/dts/internal/indexRefCounter.d.ts.map +1 -1
  134. package/dist/dts/internal/parser.d.ts +13 -0
  135. package/dist/dts/internal/parser.d.ts.map +1 -1
  136. package/dist/dts/internal/parser2.d.ts +12 -0
  137. package/dist/dts/internal/parser2.d.ts.map +1 -0
  138. package/dist/dts/internal/server-parts.d.ts +223 -0
  139. package/dist/dts/internal/server-parts.d.ts.map +1 -0
  140. package/dist/dts/internal/server.d.ts +2 -28
  141. package/dist/dts/internal/server.d.ts.map +1 -1
  142. package/dist/dts/internal/utils.d.ts +4 -1
  143. package/dist/dts/internal/utils.d.ts.map +1 -1
  144. package/dist/dts/internal/v2/SyncPart.d.ts +87 -0
  145. package/dist/dts/internal/v2/SyncPart.d.ts.map +1 -0
  146. package/dist/dts/internal/v2/helpers.d.ts +3 -0
  147. package/dist/dts/internal/v2/helpers.d.ts.map +1 -0
  148. package/dist/dts/internal/v2/hydrate.d.ts +7 -0
  149. package/dist/dts/internal/v2/hydrate.d.ts.map +1 -0
  150. package/dist/dts/internal/v2/hydration-template.d.ts +54 -0
  151. package/dist/dts/internal/v2/hydration-template.d.ts.map +1 -0
  152. package/dist/dts/internal/v2/parts.d.ts +245 -0
  153. package/dist/dts/internal/v2/parts.d.ts.map +1 -0
  154. package/dist/dts/internal/v2/render-entry.d.ts +6 -0
  155. package/dist/dts/internal/v2/render-entry.d.ts.map +1 -0
  156. package/dist/dts/internal/v2/render-sync-parts.d.ts +22 -0
  157. package/dist/dts/internal/v2/render-sync-parts.d.ts.map +1 -0
  158. package/dist/dts/internal/v2/render.d.ts +62 -0
  159. package/dist/dts/internal/v2/render.d.ts.map +1 -0
  160. package/dist/dts/internal/v2/sync-parts.d.ts +129 -0
  161. package/dist/dts/internal/v2/sync-parts.d.ts.map +1 -0
  162. package/dist/esm/ElementRef.js.map +1 -1
  163. package/dist/esm/EventHandler.js +20 -4
  164. package/dist/esm/EventHandler.js.map +1 -1
  165. package/dist/esm/Html.js +91 -50
  166. package/dist/esm/Html.js.map +1 -1
  167. package/dist/esm/HtmlChunk.js +75 -24
  168. package/dist/esm/HtmlChunk.js.map +1 -1
  169. package/dist/esm/Hydrate.js +5 -5
  170. package/dist/esm/Hydrate.js.map +1 -1
  171. package/dist/esm/Many.js +3 -3
  172. package/dist/esm/Many.js.map +1 -1
  173. package/dist/esm/Meta.js +7 -1
  174. package/dist/esm/Meta.js.map +1 -1
  175. package/dist/esm/Parser.js +1 -1
  176. package/dist/esm/Parser.js.map +1 -1
  177. package/dist/esm/Placeholder.js +4 -8
  178. package/dist/esm/Placeholder.js.map +1 -1
  179. package/dist/esm/Platform.js +3 -1
  180. package/dist/esm/Platform.js.map +1 -1
  181. package/dist/esm/Render.js +6 -5
  182. package/dist/esm/Render.js.map +1 -1
  183. package/dist/esm/RenderContext.js +5 -85
  184. package/dist/esm/RenderContext.js.map +1 -1
  185. package/dist/esm/RenderEvent.js +8 -1
  186. package/dist/esm/RenderEvent.js.map +1 -1
  187. package/dist/esm/RenderQueue.js +336 -0
  188. package/dist/esm/RenderQueue.js.map +1 -0
  189. package/dist/esm/RenderTemplate.js.map +1 -1
  190. package/dist/esm/Template.js +12 -0
  191. package/dist/esm/Template.js.map +1 -1
  192. package/dist/esm/Test.js +71 -30
  193. package/dist/esm/Test.js.map +1 -1
  194. package/dist/esm/Vitest.js +11 -8
  195. package/dist/esm/Vitest.js.map +1 -1
  196. package/dist/esm/index.js +4 -0
  197. package/dist/esm/index.js.map +1 -1
  198. package/dist/esm/internal/EventSource.js +12 -7
  199. package/dist/esm/internal/EventSource.js.map +1 -1
  200. package/dist/esm/internal/HydrateContext.js.map +1 -1
  201. package/dist/esm/internal/browser.js +10 -9
  202. package/dist/esm/internal/browser.js.map +1 -1
  203. package/dist/esm/internal/character-entities.js +2134 -0
  204. package/dist/esm/internal/character-entities.js.map +1 -0
  205. package/dist/esm/internal/errors.js +22 -2
  206. package/dist/esm/internal/errors.js.map +1 -1
  207. package/dist/esm/internal/indexRefCounter.js +36 -61
  208. package/dist/esm/internal/indexRefCounter.js.map +1 -1
  209. package/dist/esm/internal/parser.js +18 -18
  210. package/dist/esm/internal/parser.js.map +1 -1
  211. package/dist/esm/internal/parser2.js +393 -0
  212. package/dist/esm/internal/parser2.js.map +1 -0
  213. package/dist/esm/internal/server-parts.js +109 -0
  214. package/dist/esm/internal/server-parts.js.map +1 -0
  215. package/dist/esm/internal/server.js +12 -161
  216. package/dist/esm/internal/server.js.map +1 -1
  217. package/dist/esm/internal/utils.js +71 -7
  218. package/dist/esm/internal/utils.js.map +1 -1
  219. package/dist/esm/internal/v2/SyncPart.js +5 -0
  220. package/dist/esm/internal/v2/SyncPart.js.map +1 -0
  221. package/dist/esm/internal/v2/helpers.js +12 -0
  222. package/dist/esm/internal/v2/helpers.js.map +1 -0
  223. package/dist/esm/internal/v2/hydrate.js +195 -0
  224. package/dist/esm/internal/v2/hydrate.js.map +1 -0
  225. package/dist/esm/internal/v2/hydration-template.js +265 -0
  226. package/dist/esm/internal/v2/hydration-template.js.map +1 -0
  227. package/dist/esm/internal/v2/parts.js +150 -0
  228. package/dist/esm/internal/v2/parts.js.map +1 -0
  229. package/dist/esm/internal/v2/render-entry.js +102 -0
  230. package/dist/esm/internal/v2/render-entry.js.map +1 -0
  231. package/dist/esm/internal/v2/render-sync-parts.js +265 -0
  232. package/dist/esm/internal/v2/render-sync-parts.js.map +1 -0
  233. package/dist/esm/internal/v2/render.js +353 -0
  234. package/dist/esm/internal/v2/render.js.map +1 -0
  235. package/dist/esm/internal/v2/sync-parts.js +102 -0
  236. package/dist/esm/internal/v2/sync-parts.js.map +1 -0
  237. package/package.json +20 -13
  238. package/src/ElementRef.ts +1 -1
  239. package/src/ElementSource.ts +1 -1
  240. package/src/EventHandler.ts +50 -12
  241. package/src/Html.ts +207 -98
  242. package/src/HtmlChunk.ts +77 -30
  243. package/src/Hydrate.ts +20 -14
  244. package/src/Many.ts +17 -14
  245. package/src/Meta.ts +8 -1
  246. package/src/Parser.ts +1 -1
  247. package/src/Part.ts +22 -66
  248. package/src/Placeholder.ts +17 -15
  249. package/src/Platform.ts +5 -5
  250. package/src/Render.ts +23 -26
  251. package/src/RenderContext.ts +14 -142
  252. package/src/RenderEvent.ts +10 -1
  253. package/src/RenderQueue.ts +445 -0
  254. package/src/RenderTemplate.ts +7 -2
  255. package/src/Renderable.ts +1 -1
  256. package/src/Template.ts +15 -1
  257. package/src/Test.ts +122 -38
  258. package/src/Vitest.ts +20 -10
  259. package/src/index.ts +4 -0
  260. package/src/internal/EventSource.ts +14 -8
  261. package/src/internal/HydrateContext.ts +3 -4
  262. package/src/internal/browser.ts +26 -21
  263. package/src/internal/character-entities.ts +2136 -0
  264. package/src/internal/errors.ts +30 -3
  265. package/src/internal/indexRefCounter.ts +38 -70
  266. package/src/internal/parser.ts +19 -19
  267. package/src/internal/parser2.ts +468 -0
  268. package/src/internal/server-parts.ts +161 -0
  269. package/src/internal/server.ts +16 -272
  270. package/src/internal/utils.ts +83 -7
  271. package/src/internal/v2/SyncPart.ts +112 -0
  272. package/src/internal/v2/helpers.ts +13 -0
  273. package/src/internal/v2/hydrate.ts +289 -0
  274. package/src/internal/v2/hydration-template.ts +308 -0
  275. package/src/internal/v2/parts.ts +254 -0
  276. package/src/internal/v2/render-entry.ts +131 -0
  277. package/src/internal/v2/render-sync-parts.ts +440 -0
  278. package/src/internal/v2/render.ts +588 -0
  279. package/src/internal/v2/sync-parts.ts +133 -0
  280. package/dist/cjs/internal/hydrate.js +0 -274
  281. package/dist/cjs/internal/hydrate.js.map +0 -1
  282. package/dist/cjs/internal/parts.js +0 -451
  283. package/dist/cjs/internal/parts.js.map +0 -1
  284. package/dist/cjs/internal/render.js +0 -704
  285. package/dist/cjs/internal/render.js.map +0 -1
  286. package/dist/dts/internal/hydrate.d.ts +0 -33
  287. package/dist/dts/internal/hydrate.d.ts.map +0 -1
  288. package/dist/dts/internal/parts.d.ts +0 -314
  289. package/dist/dts/internal/parts.d.ts.map +0 -1
  290. package/dist/dts/internal/render.d.ts +0 -16
  291. package/dist/dts/internal/render.d.ts.map +0 -1
  292. package/dist/esm/internal/hydrate.js +0 -239
  293. package/dist/esm/internal/hydrate.js.map +0 -1
  294. package/dist/esm/internal/parts.js +0 -373
  295. package/dist/esm/internal/parts.js.map +0 -1
  296. package/dist/esm/internal/render.js +0 -689
  297. package/dist/esm/internal/render.js.map +0 -1
  298. package/src/internal/hydrate.ts +0 -366
  299. package/src/internal/parts.ts +0 -609
  300. package/src/internal/render.ts +0 -971
@@ -1,609 +0,0 @@
1
- import { isText } from "@typed/wire"
2
- import type { Cause } from "effect/Cause"
3
- import * as Data from "effect/Data"
4
- import * as Effect from "effect/Effect"
5
- import { equals } from "effect/Equal"
6
- import * as Equivalence from "effect/Equivalence"
7
- import * as ReadonlyArray from "effect/ReadonlyArray"
8
- import type { Scope } from "effect/Scope"
9
- import type { ElementSource } from "../ElementSource.js"
10
- import type { EventHandler } from "../EventHandler.js"
11
- import { unescape } from "../HtmlChunk.js"
12
- import type {
13
- AttributePart,
14
- BooleanPart,
15
- ClassNamePart,
16
- CommentPart,
17
- DataPart,
18
- EventPart,
19
- NodePart,
20
- Part,
21
- PropertiesPart,
22
- PropertyPart,
23
- RefPart,
24
- SparseAttributePart,
25
- SparseClassNamePart,
26
- SparseCommentPart,
27
- SparsePart,
28
- StaticText,
29
- TextPart
30
- } from "../Part.js"
31
- import type { RenderContext } from "../RenderContext.js"
32
- import { findHoleComment } from "./utils.js"
33
-
34
- const strictEq = Equivalence.strict<any>()
35
-
36
- const base = <T extends Part["_tag"]>(tag: T) => (class Base {
37
- readonly _tag: T = tag
38
-
39
- constructor(
40
- readonly index: number,
41
- readonly commit: (
42
- params: {
43
- previous: Extract<Part, { readonly _tag: T }>["value"]
44
- value: Extract<Part, { readonly _tag: T }>["value"]
45
- part: Extract<Part, { readonly _tag: T }>
46
- }
47
- ) => Effect.Effect<void, never, Scope>,
48
- public value: Extract<Part, { readonly _tag: T }>["value"],
49
- readonly eq: Equivalence.Equivalence<Extract<Part, { readonly _tag: T }>["value"]> = equals
50
- ) {
51
- this.update = this.update.bind(this)
52
- }
53
-
54
- update(input: this["value"]) {
55
- const previous = this.value as any
56
- const value = this.getValue(input) as any
57
-
58
- if (this.eq(previous as any, value as any)) {
59
- return Effect.unit
60
- }
61
-
62
- return Effect.flatMap(
63
- this.commit.call(this, {
64
- previous,
65
- value,
66
- part: this as any
67
- }),
68
- () => Effect.sync(() => this.value = value)
69
- )
70
- }
71
-
72
- getValue(value: unknown) {
73
- return value
74
- }
75
- })
76
-
77
- export class AttributePartImpl extends base("attribute") implements AttributePart {
78
- constructor(
79
- readonly name: string,
80
- index: number,
81
- commit: AttributePartImpl["commit"],
82
- value: AttributePart["value"]
83
- ) {
84
- super(index, commit, value, strictEq)
85
- }
86
-
87
- static browser(index: number, element: Element, name: string, context: RenderContext): AttributePartImpl {
88
- return new AttributePartImpl(
89
- name,
90
- index,
91
- ({ part, value }) =>
92
- context.queue.add(
93
- part,
94
- () => value == null ? element.removeAttribute(name) : element.setAttribute(name, value)
95
- ),
96
- element.getAttribute(name)
97
- )
98
- }
99
-
100
- static server(
101
- name: string,
102
- index: number,
103
- commit: AttributePartImpl["commit"]
104
- ) {
105
- return new AttributePartImpl(name, index, commit, null)
106
- }
107
- }
108
-
109
- export class BooleanPartImpl extends base("boolean") implements BooleanPart {
110
- constructor(
111
- readonly name: string,
112
- index: number,
113
- commit: BooleanPartImpl["commit"],
114
- value: BooleanPart["value"]
115
- ) {
116
- super(index, commit, value, strictEq)
117
- }
118
-
119
- static browser(index: number, element: Element, name: string, context: RenderContext): BooleanPartImpl {
120
- return new BooleanPartImpl(
121
- name,
122
- index,
123
- ({ part, value }) =>
124
- context.queue.add(
125
- part,
126
- () => element.toggleAttribute(name, value === true)
127
- ),
128
- element.hasAttribute(name)
129
- )
130
- }
131
-
132
- static server(
133
- name: string,
134
- index: number,
135
- commit: BooleanPartImpl["commit"]
136
- ) {
137
- return new BooleanPartImpl(name, index, commit, null)
138
- }
139
- }
140
-
141
- const isString = (x: unknown): x is string => typeof x === "string"
142
-
143
- export class ClassNamePartImpl extends base("className") implements ClassNamePart {
144
- constructor(
145
- index: number,
146
- commit: ClassNamePartImpl["commit"],
147
- value: ClassNamePart["value"]
148
- ) {
149
- super(index, commit, value, strictEq)
150
- }
151
-
152
- getValue(value: unknown): ReadonlyArray<string> {
153
- if (isString(value)) {
154
- return value.split(" ").filter((x) => isString(x) && x.trim() !== "")
155
- }
156
-
157
- if (Array.isArray(value)) {
158
- return value.filter((x) => isString(x) && x.trim() !== "")
159
- }
160
-
161
- return []
162
- }
163
-
164
- static browser(index: number, element: Element, context: RenderContext): ClassNamePartImpl {
165
- return new ClassNamePartImpl(
166
- index,
167
- ({ part, previous, value }) =>
168
- context.queue.add(
169
- part,
170
- () => {
171
- const { added, removed } = diffStrings(
172
- previous,
173
- value
174
- )
175
-
176
- element.classList.add(...added)
177
- element.classList.remove(...removed)
178
- }
179
- ),
180
- Array.from(element.classList)
181
- )
182
- }
183
-
184
- static server(
185
- index: number,
186
- commit: ClassNamePartImpl["commit"]
187
- ) {
188
- return new ClassNamePartImpl(index, commit, null)
189
- }
190
- }
191
-
192
- function diffStrings(
193
- previous: ReadonlyArray<string> | null | undefined,
194
- current: ReadonlyArray<string> | null | undefined
195
- ): { added: ReadonlyArray<string>; removed: ReadonlyArray<string>; unchanged: ReadonlyArray<string> } {
196
- if (previous == null || previous.length === 0) {
197
- return {
198
- added: current || [],
199
- removed: [],
200
- unchanged: []
201
- }
202
- } else if (current == null || current.length === 0) {
203
- return {
204
- added: [],
205
- removed: previous,
206
- unchanged: []
207
- }
208
- } else {
209
- const added = current.filter((c) => !previous.includes(c))
210
- const removed: Array<string> = []
211
- const unchanged: Array<string> = []
212
-
213
- for (let i = 0; i < previous.length; ++i) {
214
- if (current.includes(previous[i])) {
215
- unchanged.push(previous[i])
216
- } else {
217
- removed.push(previous[i])
218
- }
219
- }
220
-
221
- return {
222
- added,
223
- removed,
224
- unchanged
225
- }
226
- }
227
- }
228
-
229
- export class CommentPartImpl extends base("comment") implements CommentPart {
230
- static browser(index: number, comment: globalThis.Comment, ctx: RenderContext) {
231
- return new CommentPartImpl(
232
- index,
233
- ({ part, value }) => ctx.queue.add(part, () => comment.data = value || ""),
234
- comment.data,
235
- strictEq
236
- )
237
- }
238
-
239
- static server(index: number, commit: CommentPartImpl["commit"]) {
240
- return new CommentPartImpl(index, commit, null)
241
- }
242
- }
243
-
244
- export class DataPartImpl extends base("data") implements DataPart {
245
- static browser(index: number, element: HTMLElement | SVGElement, ctx: RenderContext) {
246
- return new DataPartImpl(
247
- index,
248
- ({ part, previous, value }) =>
249
- ctx.queue.add(
250
- part,
251
- () => {
252
- const diff = diffDataSet(previous, value)
253
-
254
- if (diff) {
255
- const { added, removed } = diff
256
-
257
- removed.forEach((k) => delete element.dataset[k])
258
- added.forEach(([k, v]) => element.dataset[k] = v)
259
- }
260
- }
261
- ),
262
- element.dataset
263
- )
264
- }
265
-
266
- static server(index: number, commit: DataPartImpl["commit"]) {
267
- return new DataPartImpl(index, commit, null)
268
- }
269
- }
270
-
271
- function diffDataSet(
272
- a: Record<string, string | undefined> | null | undefined,
273
- b: Record<string, string | undefined> | null | undefined
274
- ):
275
- | { added: Array<readonly [string, string | undefined]>; removed: ReadonlyArray<string> }
276
- | null
277
- {
278
- if (!a) return b ? { added: Object.entries(b), removed: [] } : null
279
- if (!b) return { added: [], removed: Object.keys(a) }
280
-
281
- const { added, removed, unchanged } = diffStrings(Object.keys(a), Object.keys(b))
282
-
283
- return {
284
- added: added.concat(unchanged).map((k) => [k, b[k]] as const),
285
- removed
286
- }
287
- }
288
-
289
- export class EventPartImpl implements EventPart {
290
- readonly _tag = "event"
291
- readonly value: EventPart["value"] = null
292
-
293
- constructor(
294
- readonly name: string,
295
- readonly index: number,
296
- readonly source: ElementSource<any>,
297
- readonly onCause: <E>(cause: Cause<E>) => Effect.Effect<unknown>,
298
- readonly addEventListener: <Ev extends Event>(handler: EventHandler<Ev>) => void
299
- ) {
300
- }
301
- }
302
-
303
- export class NodePartImpl extends base("node") implements NodePart {}
304
-
305
- export class PropertyPartImpl extends base("property") implements PropertyPart {
306
- constructor(
307
- readonly name: string,
308
- index: number,
309
- commit: PropertyPartImpl["commit"],
310
- value: PropertyPartImpl["value"]
311
- ) {
312
- super(index, commit, value, strictEq)
313
- }
314
-
315
- static browser(index: number, node: Node, name: string, ctx: RenderContext) {
316
- const existing = (node as Element).getAttribute(name)
317
-
318
- return new PropertyPartImpl(
319
- name,
320
- index,
321
- ({ part, value }) => ctx.queue.add(part, () => (node as any)[name] = value),
322
- existing ? unescape(existing) : null
323
- )
324
- }
325
- }
326
-
327
- export class RefPartImpl implements RefPart {
328
- readonly _tag = "ref"
329
-
330
- constructor(readonly value: ElementSource<any>, readonly index: number) {}
331
- }
332
-
333
- export class TextPartImpl extends base("text") implements TextPart {
334
- // TODO: Make this properly
335
- static browser(document: Document, index: number, element: Element, ctx: RenderContext) {
336
- const comment = findHoleComment(element, index)
337
- const text = document.createTextNode("")
338
- element.insertBefore(text, comment)
339
-
340
- return new TextPartImpl(
341
- index,
342
- ({ part, value }) => ctx.queue.add(part, () => text.nodeValue = value ?? null),
343
- text.nodeValue,
344
- strictEq
345
- )
346
- }
347
-
348
- static fromText(text: Text, index: number, ctx: RenderContext) {
349
- return new TextPartImpl(
350
- index,
351
- ({ part, value }) => ctx.queue.add(part, () => text.nodeValue = value ?? null),
352
- text.nodeValue,
353
- strictEq
354
- )
355
- }
356
-
357
- static getOrCreateText(document: Document, index: number, element: Element) {
358
- const comment = findHoleComment(element, index)
359
-
360
- return comment.previousSibling && isText(comment.previousSibling)
361
- ? comment.previousSibling.nodeValue
362
- : document.createTextNode("")
363
- }
364
- }
365
-
366
- export class PropertiesPartImpl extends base("properties") implements PropertiesPart {
367
- constructor(
368
- index: number,
369
- commit: PropertiesPartImpl["commit"],
370
- value: PropertiesPartImpl["value"]
371
- ) {
372
- super(index, commit, value, equals)
373
- }
374
-
375
- getValue(value: unknown): unknown {
376
- if (value == null) return null
377
- return Data.struct(value)
378
- }
379
-
380
- static browser(index: number, element: HTMLElement | SVGElement, ctx: RenderContext) {
381
- return new PropertiesPartImpl(
382
- index,
383
- ({ part, previous, value }) =>
384
- ctx.queue.add(
385
- part,
386
- () => {
387
- const diff = diffProperties(previous, value)
388
- if (diff) {
389
- const { added, removed } = diff
390
-
391
- removed.forEach((nv) => removeNameValue(element, nv))
392
- added.forEach((nv) => {
393
- if ((nv.name[0] === "o" && nv.name[1] === "n") || nv.name[0] === "@") return
394
-
395
- return addNameValue(element, nv)
396
- })
397
- }
398
- }
399
- ),
400
- {}
401
- )
402
- }
403
- }
404
-
405
- function removeNameValue(element: HTMLElement | SVGElement, { name, type }: NameValue) {
406
- switch (type) {
407
- case "attr":
408
- case "bool":
409
- return element.removeAttribute(name)
410
- case "prop":
411
- return delete (element as any)[name]
412
- }
413
- }
414
-
415
- function addNameValue(element: HTMLElement | SVGElement, { name, type, value }: NameValue) {
416
- switch (type) {
417
- case "attr":
418
- return value == null ? element.removeAttribute(name) : element.setAttribute(name, value)
419
- case "bool":
420
- return value == null ? element.removeAttribute(name) : element.toggleAttribute(name, value)
421
- case "prop":
422
- return value == null ? (delete (element as any)[name]) : (element as any)[name] = value
423
- }
424
- }
425
-
426
- type AttrNameValue = {
427
- readonly type: "attr"
428
- readonly name: string
429
- readonly value: string
430
- }
431
-
432
- type BoolAttrNameValue = {
433
- readonly type: "bool"
434
- readonly name: string
435
- readonly value: boolean
436
- }
437
-
438
- type PropNameValue = {
439
- readonly type: "prop"
440
- readonly name: string
441
- readonly value: unknown
442
- }
443
-
444
- type NameValue = AttrNameValue | BoolAttrNameValue | PropNameValue
445
-
446
- function diffProperties(
447
- a: Record<string, unknown> | null | undefined,
448
- b: Record<string, unknown> | null | undefined
449
- ): { added: Array<NameValue>; removed: ReadonlyArray<NameValue> } | null {
450
- if (!a) {
451
- if (b) {
452
- return { added: Object.entries(b).flatMap(([k, v]) => fromKeyValue(k, v)), removed: [] }
453
- } else return null
454
- } else if (!b) {
455
- return { added: [], removed: Object.entries(a).flatMap(([k, v]) => fromKeyValue(k, v)) }
456
- } else {
457
- const { added, removed, unchanged } = diffStrings(Object.keys(a), Object.keys(b))
458
-
459
- return {
460
- added: added.concat(unchanged).flatMap((k) => fromKeyValue(k, b[k])),
461
- removed: removed.flatMap((k) => fromKeyValue(k, a[k]))
462
- }
463
- }
464
- }
465
-
466
- function fromKeyValue(name: string, value: unknown): Array<NameValue> {
467
- if (name[0] === ".") {
468
- return value == null ? [] : [{
469
- type: "prop",
470
- name: name.slice(1),
471
- value
472
- }]
473
- } else if (typeof value === "boolean") {
474
- return [{
475
- type: "bool",
476
- name,
477
- value
478
- }]
479
- } else {
480
- if (name[0] === "o" || name[1] === "n") return []
481
-
482
- return value == null ? [] : [{
483
- type: "attr",
484
- name,
485
- value: String(value)
486
- }]
487
- }
488
- }
489
-
490
- const sparse = <T extends SparsePart["_tag"]>(tag: T) => (class Base {
491
- readonly _tag: T = tag
492
-
493
- constructor(
494
- readonly commit: (
495
- params: {
496
- previous: SparseAttributeValues<Extract<SparsePart, { readonly _tag: T }>["parts"]>
497
- value: SparseAttributeValues<Extract<SparsePart, { readonly _tag: T }>["parts"]>
498
- part: Extract<SparsePart, { readonly _tag: T }>
499
- }
500
- ) => Effect.Effect<void, never, Scope>,
501
- public value: SparseAttributeValues<Extract<SparsePart, { readonly _tag: T }>["parts"]>,
502
- readonly eq: Equivalence.Equivalence<SparseAttributeValues<Extract<SparsePart, { readonly _tag: T }>["parts"]>> =
503
- equals
504
- ) {}
505
-
506
- update = (value: this["value"]) => {
507
- if (this.eq(this.value as any, value as any)) {
508
- return Effect.unit
509
- }
510
-
511
- return this.commit({
512
- previous: this.value,
513
- value: this.value = value as any,
514
- part: this
515
- } as any)
516
- }
517
- })
518
-
519
- type SparseAttributeValues<T extends ReadonlyArray<AttributePart | ClassNamePart | CommentPart | StaticText>> =
520
- ReadonlyArray<
521
- SparseAttributeValue<T[number]>
522
- >
523
- type SparseAttributeValue<T extends AttributePart | ClassNamePart | CommentPart | StaticText> = T["value"]
524
-
525
- export class SparseAttributePartImpl extends sparse("sparse/attribute") implements SparseAttributePart {
526
- constructor(
527
- readonly name: string,
528
- readonly parts: ReadonlyArray<AttributePart | StaticText>,
529
- commit: SparseAttributePartImpl["commit"]
530
- ) {
531
- super(commit, [], ReadonlyArray.getEquivalence(strictEq))
532
- }
533
-
534
- static browser(
535
- name: string,
536
- parts: ReadonlyArray<AttributePart | StaticText>,
537
- element: HTMLElement | SVGElement,
538
- ctx: RenderContext
539
- ) {
540
- return new SparseAttributePartImpl(
541
- name,
542
- parts,
543
- ({ part, value }) =>
544
- ctx.queue.add(part, () => element.setAttribute(name, value.flatMap((s) => isNonEmptyString(s, true)).join("")))
545
- )
546
- }
547
- }
548
-
549
- export class SparseClassNamePartImpl extends sparse("sparse/className") implements SparseClassNamePart {
550
- constructor(
551
- readonly parts: ReadonlyArray<ClassNamePart | StaticText>,
552
- commit: SparseClassNamePartImpl["commit"],
553
- values: Array<string | Array<string>>
554
- ) {
555
- super(commit, values, ReadonlyArray.getEquivalence(strictEq))
556
- }
557
-
558
- static browser(
559
- parts: ReadonlyArray<ClassNamePart | StaticText>,
560
- element: HTMLElement | SVGElement,
561
- ctx: RenderContext,
562
- values: Array<string | Array<string>> = []
563
- ) {
564
- return new SparseClassNamePartImpl(
565
- parts,
566
- ({ part, value }) =>
567
- ctx.queue.add(part, () => {
568
- return element.setAttribute("class", value.flatMap((s) => isNonEmptyString(s, true)).join(" "))
569
- }),
570
- values
571
- )
572
- }
573
- }
574
-
575
- export class SparseCommentPartImpl extends sparse("sparse/comment") implements SparseCommentPart {
576
- constructor(
577
- readonly parts: ReadonlyArray<CommentPart | StaticText>,
578
- commit: SparseCommentPartImpl["commit"],
579
- value: SparseCommentPartImpl["value"]
580
- ) {
581
- super(commit, value, ReadonlyArray.getEquivalence(strictEq))
582
- }
583
-
584
- static browser(comment: Comment, parts: ReadonlyArray<CommentPart | StaticText>, ctx: RenderContext) {
585
- return new SparseCommentPartImpl(
586
- parts,
587
- ({ part, value }) =>
588
- ctx.queue.add(part, () => comment.nodeValue = value.flatMap((s) => isNonEmptyString(s, false)).join("")),
589
- []
590
- )
591
- }
592
- }
593
-
594
- export class StaticTextImpl implements StaticText {
595
- readonly _tag = "static/text"
596
-
597
- constructor(readonly value: string) {}
598
- }
599
-
600
- function isNonEmptyString(s: string | ReadonlyArray<string> | null | undefined, trim: boolean): Array<string> {
601
- if (s == null) return []
602
- if (Array.isArray(s)) return s.flatMap((s) => isNonEmptyString(s, trim))
603
-
604
- const trimmed = trim ? (s as string).trim() : s
605
-
606
- if (trimmed.length === 0) return []
607
-
608
- return [trimmed as string]
609
- }