@typed/template 0.1.4 → 0.3.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 (234) hide show
  1. package/Directive/package.json +6 -0
  2. package/ElementRef/package.json +6 -0
  3. package/ElementSource/package.json +6 -0
  4. package/Entry/package.json +6 -0
  5. package/EventHandler/package.json +6 -0
  6. package/Html/package.json +6 -0
  7. package/HtmlChunk/package.json +6 -0
  8. package/Hydrate/package.json +6 -0
  9. package/Many/package.json +6 -0
  10. package/Meta/package.json +6 -0
  11. package/Parser/package.json +6 -0
  12. package/Part/package.json +6 -0
  13. package/Placeholder/package.json +6 -0
  14. package/Platform/package.json +6 -0
  15. package/Render/package.json +6 -0
  16. package/RenderContext/package.json +6 -0
  17. package/RenderEvent/package.json +6 -0
  18. package/RenderTemplate/package.json +6 -0
  19. package/Renderable/package.json +6 -0
  20. package/Template/package.json +6 -0
  21. package/TemplateInstance/package.json +6 -0
  22. package/Test/package.json +6 -0
  23. package/Vitest/package.json +6 -0
  24. package/dist/cjs/Directive.js +1 -1
  25. package/dist/cjs/Directive.js.map +1 -1
  26. package/dist/cjs/ElementRef.js +23 -13
  27. package/dist/cjs/ElementRef.js.map +1 -1
  28. package/dist/cjs/ElementSource.js +16 -18
  29. package/dist/cjs/ElementSource.js.map +1 -1
  30. package/dist/cjs/EventHandler.js +1 -1
  31. package/dist/cjs/EventHandler.js.map +1 -1
  32. package/dist/cjs/Html.js +31 -32
  33. package/dist/cjs/Html.js.map +1 -1
  34. package/dist/cjs/HtmlChunk.js +4 -1
  35. package/dist/cjs/HtmlChunk.js.map +1 -1
  36. package/dist/cjs/Hydrate.js +1 -1
  37. package/dist/cjs/Hydrate.js.map +1 -1
  38. package/dist/cjs/Many.js +14 -13
  39. package/dist/cjs/Many.js.map +1 -1
  40. package/dist/cjs/Parser.js +11 -323
  41. package/dist/cjs/Parser.js.map +1 -1
  42. package/dist/cjs/Placeholder.js +3 -3
  43. package/dist/cjs/Placeholder.js.map +1 -1
  44. package/dist/cjs/Platform.js +4 -4
  45. package/dist/cjs/Platform.js.map +1 -1
  46. package/dist/cjs/Render.js +1 -1
  47. package/dist/cjs/Render.js.map +1 -1
  48. package/dist/cjs/RenderContext.js +48 -27
  49. package/dist/cjs/RenderContext.js.map +1 -1
  50. package/dist/cjs/RenderTemplate.js +2 -17
  51. package/dist/cjs/RenderTemplate.js.map +1 -1
  52. package/dist/cjs/Template.js +27 -1
  53. package/dist/cjs/Template.js.map +1 -1
  54. package/dist/cjs/TemplateInstance.js +2 -2
  55. package/dist/cjs/TemplateInstance.js.map +1 -1
  56. package/dist/cjs/Test.js +20 -7
  57. package/dist/cjs/Test.js.map +1 -1
  58. package/dist/cjs/index.js +0 -12
  59. package/dist/cjs/index.js.map +1 -1
  60. package/dist/cjs/internal/EventSource.js +95 -0
  61. package/dist/cjs/internal/EventSource.js.map +1 -0
  62. package/dist/cjs/internal/browser.js +11 -11
  63. package/dist/cjs/internal/browser.js.map +1 -1
  64. package/dist/cjs/internal/hydrate.js +49 -50
  65. package/dist/cjs/internal/hydrate.js.map +1 -1
  66. package/dist/cjs/internal/indexRefCounter.js +49 -2
  67. package/dist/cjs/internal/indexRefCounter.js.map +1 -1
  68. package/dist/cjs/internal/parser.js +60 -17
  69. package/dist/cjs/internal/parser.js.map +1 -1
  70. package/dist/cjs/internal/parts.js +128 -28
  71. package/dist/cjs/internal/parts.js.map +1 -1
  72. package/dist/cjs/internal/render.js +486 -53
  73. package/dist/cjs/internal/render.js.map +1 -1
  74. package/dist/cjs/internal/server.js +5 -2
  75. package/dist/cjs/internal/server.js.map +1 -1
  76. package/dist/dts/Directive.d.ts.map +1 -1
  77. package/dist/dts/ElementRef.d.ts +4 -2
  78. package/dist/dts/ElementRef.d.ts.map +1 -1
  79. package/dist/dts/ElementSource.d.ts +10 -5
  80. package/dist/dts/ElementSource.d.ts.map +1 -1
  81. package/dist/dts/EventHandler.d.ts.map +1 -1
  82. package/dist/dts/Html.d.ts +1 -1
  83. package/dist/dts/Html.d.ts.map +1 -1
  84. package/dist/dts/HtmlChunk.d.ts.map +1 -1
  85. package/dist/dts/Many.d.ts +13 -11
  86. package/dist/dts/Many.d.ts.map +1 -1
  87. package/dist/dts/Parser.d.ts +3 -6
  88. package/dist/dts/Parser.d.ts.map +1 -1
  89. package/dist/dts/Part.d.ts +13 -3
  90. package/dist/dts/Part.d.ts.map +1 -1
  91. package/dist/dts/Placeholder.d.ts +2 -2
  92. package/dist/dts/Placeholder.d.ts.map +1 -1
  93. package/dist/dts/Render.d.ts +2 -1
  94. package/dist/dts/Render.d.ts.map +1 -1
  95. package/dist/dts/RenderContext.d.ts +5 -4
  96. package/dist/dts/RenderContext.d.ts.map +1 -1
  97. package/dist/dts/RenderTemplate.d.ts +2 -16
  98. package/dist/dts/RenderTemplate.d.ts.map +1 -1
  99. package/dist/dts/Renderable.d.ts +2 -2
  100. package/dist/dts/Renderable.d.ts.map +1 -1
  101. package/dist/dts/Template.d.ts +21 -3
  102. package/dist/dts/Template.d.ts.map +1 -1
  103. package/dist/dts/TemplateInstance.d.ts +3 -2
  104. package/dist/dts/TemplateInstance.d.ts.map +1 -1
  105. package/dist/dts/Test.d.ts +4 -6
  106. package/dist/dts/Test.d.ts.map +1 -1
  107. package/dist/dts/index.d.ts +0 -4
  108. package/dist/dts/index.d.ts.map +1 -1
  109. package/dist/dts/internal/EventSource.d.ts +12 -0
  110. package/dist/dts/internal/EventSource.d.ts.map +1 -0
  111. package/dist/dts/internal/browser.d.ts.map +1 -1
  112. package/dist/dts/internal/hydrate.d.ts +5 -5
  113. package/dist/dts/internal/hydrate.d.ts.map +1 -1
  114. package/dist/dts/internal/indexRefCounter.d.ts +5 -0
  115. package/dist/dts/internal/indexRefCounter.d.ts.map +1 -1
  116. package/dist/dts/internal/module-augmentation.d.ts +0 -4
  117. package/dist/dts/internal/module-augmentation.d.ts.map +1 -1
  118. package/dist/dts/internal/parser.d.ts +8 -0
  119. package/dist/dts/internal/parser.d.ts.map +1 -1
  120. package/dist/dts/internal/parts.d.ts +66 -56
  121. package/dist/dts/internal/parts.d.ts.map +1 -1
  122. package/dist/dts/internal/render.d.ts +7 -7
  123. package/dist/dts/internal/render.d.ts.map +1 -1
  124. package/dist/dts/internal/server.d.ts.map +1 -1
  125. package/dist/esm/Directive.js +1 -1
  126. package/dist/esm/Directive.js.map +1 -1
  127. package/dist/esm/ElementRef.js +12 -7
  128. package/dist/esm/ElementRef.js.map +1 -1
  129. package/dist/esm/ElementSource.js +16 -13
  130. package/dist/esm/ElementSource.js.map +1 -1
  131. package/dist/esm/EventHandler.js +1 -1
  132. package/dist/esm/EventHandler.js.map +1 -1
  133. package/dist/esm/Html.js +29 -24
  134. package/dist/esm/Html.js.map +1 -1
  135. package/dist/esm/HtmlChunk.js +6 -1
  136. package/dist/esm/HtmlChunk.js.map +1 -1
  137. package/dist/esm/Hydrate.js +1 -1
  138. package/dist/esm/Hydrate.js.map +1 -1
  139. package/dist/esm/Many.js +14 -10
  140. package/dist/esm/Many.js.map +1 -1
  141. package/dist/esm/Parser.js +6 -335
  142. package/dist/esm/Parser.js.map +1 -1
  143. package/dist/esm/Placeholder.js +2 -2
  144. package/dist/esm/Placeholder.js.map +1 -1
  145. package/dist/esm/Platform.js +2 -2
  146. package/dist/esm/Platform.js.map +1 -1
  147. package/dist/esm/Render.js +1 -1
  148. package/dist/esm/Render.js.map +1 -1
  149. package/dist/esm/RenderContext.js +38 -17
  150. package/dist/esm/RenderContext.js.map +1 -1
  151. package/dist/esm/RenderTemplate.js +2 -12
  152. package/dist/esm/RenderTemplate.js.map +1 -1
  153. package/dist/esm/Template.js +24 -0
  154. package/dist/esm/Template.js.map +1 -1
  155. package/dist/esm/TemplateInstance.js +2 -2
  156. package/dist/esm/TemplateInstance.js.map +1 -1
  157. package/dist/esm/Test.js +20 -7
  158. package/dist/esm/Test.js.map +1 -1
  159. package/dist/esm/index.js +0 -4
  160. package/dist/esm/index.js.map +1 -1
  161. package/dist/esm/internal/EventSource.js +91 -0
  162. package/dist/esm/internal/EventSource.js.map +1 -0
  163. package/dist/esm/internal/browser.js +12 -12
  164. package/dist/esm/internal/browser.js.map +1 -1
  165. package/dist/esm/internal/hydrate.js +45 -46
  166. package/dist/esm/internal/hydrate.js.map +1 -1
  167. package/dist/esm/internal/indexRefCounter.js +50 -2
  168. package/dist/esm/internal/indexRefCounter.js.map +1 -1
  169. package/dist/esm/internal/parser.js +84 -17
  170. package/dist/esm/internal/parser.js.map +1 -1
  171. package/dist/esm/internal/parts.js +110 -27
  172. package/dist/esm/internal/parts.js.map +1 -1
  173. package/dist/esm/internal/render.js +476 -58
  174. package/dist/esm/internal/render.js.map +1 -1
  175. package/dist/esm/internal/server.js +5 -4
  176. package/dist/esm/internal/server.js.map +1 -1
  177. package/package.json +10 -26
  178. package/src/Directive.ts +1 -1
  179. package/src/ElementRef.ts +18 -14
  180. package/src/ElementSource.ts +62 -47
  181. package/src/EventHandler.ts +1 -1
  182. package/src/Html.ts +58 -57
  183. package/src/HtmlChunk.ts +15 -1
  184. package/src/Hydrate.ts +1 -1
  185. package/src/Many.ts +53 -43
  186. package/src/Parser.ts +10 -453
  187. package/src/Part.ts +15 -3
  188. package/src/Placeholder.ts +4 -4
  189. package/src/Platform.ts +2 -2
  190. package/src/Render.ts +7 -2
  191. package/src/RenderContext.ts +49 -21
  192. package/src/RenderTemplate.ts +9 -54
  193. package/src/Renderable.ts +2 -1
  194. package/src/Template.ts +26 -0
  195. package/src/TemplateInstance.ts +9 -9
  196. package/src/Test.ts +40 -21
  197. package/src/index.ts +0 -4
  198. package/src/internal/EventSource.ts +153 -0
  199. package/src/internal/browser.ts +26 -25
  200. package/src/internal/hydrate.ts +68 -61
  201. package/src/internal/indexRefCounter.ts +63 -2
  202. package/src/internal/module-augmentation.ts +0 -4
  203. package/src/internal/parser.ts +92 -19
  204. package/src/internal/parts.ts +158 -73
  205. package/src/internal/render.ts +701 -89
  206. package/src/internal/server.ts +5 -3
  207. package/dist/cjs/Token.js +0 -270
  208. package/dist/cjs/Token.js.map +0 -1
  209. package/dist/cjs/Tokenizer.js +0 -18
  210. package/dist/cjs/Tokenizer.js.map +0 -1
  211. package/dist/cjs/internal/readAttribute.js +0 -34
  212. package/dist/cjs/internal/readAttribute.js.map +0 -1
  213. package/dist/cjs/internal/tokenizer.js +0 -264
  214. package/dist/cjs/internal/tokenizer.js.map +0 -1
  215. package/dist/dts/Token.d.ts +0 -202
  216. package/dist/dts/Token.d.ts.map +0 -1
  217. package/dist/dts/Tokenizer.d.ts +0 -6
  218. package/dist/dts/Tokenizer.d.ts.map +0 -1
  219. package/dist/dts/internal/readAttribute.d.ts +0 -9
  220. package/dist/dts/internal/readAttribute.d.ts.map +0 -1
  221. package/dist/dts/internal/tokenizer.d.ts +0 -3
  222. package/dist/dts/internal/tokenizer.d.ts.map +0 -1
  223. package/dist/esm/Token.js +0 -264
  224. package/dist/esm/Token.js.map +0 -1
  225. package/dist/esm/Tokenizer.js +0 -9
  226. package/dist/esm/Tokenizer.js.map +0 -1
  227. package/dist/esm/internal/readAttribute.js +0 -24
  228. package/dist/esm/internal/readAttribute.js.map +0 -1
  229. package/dist/esm/internal/tokenizer.js +0 -296
  230. package/dist/esm/internal/tokenizer.js.map +0 -1
  231. package/src/Token.ts +0 -269
  232. package/src/Tokenizer.ts +0 -10
  233. package/src/internal/readAttribute.ts +0 -28
  234. package/src/internal/tokenizer.ts +0 -338
@@ -1,7 +1,6 @@
1
1
  import { diffable, isComment } from "@typed/wire"
2
2
  import udomdiff from "udomdiff"
3
3
  import type { RenderContext } from "../RenderContext.js"
4
- import type { RenderEvent } from "../RenderEvent.js"
5
4
  import { isRenderEvent } from "../RenderEvent.js"
6
5
  import { NodePartImpl } from "./parts.js"
7
6
  import { findHoleComment, isCommentWithValue } from "./utils.js"
@@ -19,16 +18,20 @@ export function makeRenderNodePart(
19
18
 
20
19
  return new NodePartImpl(index, ({ part, value }) => {
21
20
  return ctx.queue.add(part, () => {
22
- matchValue(value, (content) => {
23
- if (text === undefined) {
24
- text = document.createTextNode("")
21
+ matchValue(
22
+ value,
23
+ (content) => {
24
+ if (text === undefined) {
25
+ text = document.createTextNode("")
26
+ }
27
+ text.textContent = content
28
+
29
+ nodes = diffChildren(comment, nodes, [text], document)
30
+ },
31
+ (updatedNodes) => {
32
+ nodes = diffChildren(comment, nodes, updatedNodes, document)
25
33
  }
26
- text.textContent = content
27
-
28
- nodes = diffChildren(comment, nodes, [text], document)
29
- }, (updatedNodes) => {
30
- nodes = diffChildren(comment, nodes, updatedNodes, document)
31
- })
34
+ )
32
35
  })
33
36
  }, nodes)
34
37
  }
@@ -83,22 +86,12 @@ export function diffChildren(
83
86
  comment.parentNode!,
84
87
  // Document Fragments cannot be removed, so we filter them out
85
88
  currentNodes.filter((x) => x.nodeType !== x.DOCUMENT_FRAGMENT_NODE),
86
- nextNodes.flatMap(flattenRenderEvent),
89
+ nextNodes,
87
90
  diffable(document),
88
91
  comment
89
92
  )
90
93
  }
91
94
 
92
- function flattenRenderEvent(x: Node | RenderEvent): Array<Node> {
93
- if (isRenderEvent(x)) {
94
- const value = x.valueOf()
95
-
96
- return Array.isArray(value) ? value : [value]
97
- } else {
98
- return [x]
99
- }
100
- }
101
-
102
95
  function matchValue<A, B>(value: unknown, onText: (text: string) => A, onNodes: (nodes: Array<Node>) => B): A | B {
103
96
  switch (typeof value) {
104
97
  // primitives are handled as text content
@@ -117,16 +110,24 @@ function matchValue<A, B>(value: unknown, onText: (text: string) => A, onNodes:
117
110
  if (value.length === 0) return onNodes([])
118
111
  // or diffed, if these contains nodes or "wires"
119
112
  else if (value.some((x) => typeof x === "object")) {
120
- return onNodes(
121
- value.flatMap((x) => (x === null ? [] : [isRenderEvent(x) ? x.valueOf() : x]))
122
- )
113
+ return onNodes(value.flatMap(renderEventToArray))
123
114
  } // in all other cases the content is stringified as is
124
115
  else return onText(String(value))
125
116
  } else {
126
- return onNodes([isRenderEvent(value) ? (value.valueOf() as Node) : (value as Node)])
117
+ return onNodes(renderEventToArray(value))
127
118
  }
128
119
  }
129
120
  case "function":
130
121
  return onNodes([])
131
122
  }
132
123
  }
124
+
125
+ function renderEventToArray(x: unknown): Array<Node> {
126
+ if (x === null || x === undefined) return []
127
+ if (isRenderEvent(x)) {
128
+ const value = x.valueOf()
129
+ return Array.isArray(value) ? value : [value]
130
+ }
131
+
132
+ return [x as Node]
133
+ }
@@ -1,21 +1,20 @@
1
1
  import * as Fx from "@typed/fx/Fx"
2
- import * as Subject from "@typed/fx/Subject"
3
- import type { Rendered } from "@typed/wire"
4
2
  import type { Cause } from "effect"
5
- import { Effect } from "effect"
6
- import * as ElementRef from "../ElementRef.js"
3
+ import * as Effect from "effect/Effect"
7
4
  import type { Placeholder } from "../Placeholder.js"
8
5
  import type { Renderable } from "../Renderable.js"
9
6
  import type { RenderContext } from "../RenderContext.js"
7
+ import type { RenderEvent } from "../RenderEvent.js"
10
8
  import { DomRenderEvent } from "../RenderEvent.js"
11
9
  import type { RenderTemplate } from "../RenderTemplate.js"
12
- import { TemplateInstance } from "../TemplateInstance.js"
13
10
  import { indexRefCounter } from "./indexRefCounter.js"
14
11
 
15
12
  import { unsafeGet } from "@typed/context"
16
13
 
14
+ import { Scope } from "effect/Scope"
17
15
  import type { Template } from "../Template.js"
18
16
  import { CouldNotFindCommentError, CouldNotFindRootElement } from "./errors.js"
17
+ import { type EventSource, makeEventSource } from "./EventSource.js"
19
18
  import { HydrateContext } from "./HydrateContext.js"
20
19
  import { buildParts, getBrowserEntry, renderTemplate, renderValues } from "./render.js"
21
20
  import {
@@ -33,61 +32,69 @@ import {
33
32
  * Here for "standard" browser rendering, a TemplateInstance is effectively a live
34
33
  * view into the contents rendered by the Template.
35
34
  */
36
- export const hydrateTemplate: (document: Document, ctx: RenderContext) => RenderTemplate =
37
- (document, renderContext) =>
38
- <Values extends ReadonlyArray<Renderable<any, any>>, T extends Rendered = Rendered>(
39
- templateStrings: TemplateStringsArray,
40
- values: Values,
41
- providedRef?: ElementRef.ElementRef<T>
42
- ) => {
43
- return Effect.gen(function*(_) {
44
- const context = yield* _(Effect.context<never>())
45
- const hydrateCtx = unsafeGet(context, HydrateContext)
46
-
47
- // If we're not longer hydrating, just render normally
48
- if (!hydrateCtx.hydrate) {
49
- return yield* _(renderTemplate(document, renderContext)(templateStrings, values, providedRef))
50
- }
35
+ export const hydrateTemplate: (document: Document, ctx: RenderContext) => RenderTemplate = (
36
+ document,
37
+ renderContext
38
+ ) => {
39
+ const render_ = renderTemplate(document, renderContext)
51
40
 
52
- const elementRef = providedRef || (yield* _(ElementRef.make<T>()))
53
- const events = Fx.map(elementRef, DomRenderEvent)
54
- const errors = Subject.make<Placeholder.Error<Values[number]>, never>()
55
-
56
- const { getParts, template, where, wire } = getHydrateEntry({
57
- ...hydrateCtx,
58
- document,
59
- renderContext,
60
- elementRef,
61
- strings: templateStrings,
62
- onCause: errors.onFailure
41
+ return <Values extends ReadonlyArray<Renderable<any, any>>>(
42
+ templateStrings: TemplateStringsArray,
43
+ values: Values
44
+ ): Fx.Fx<
45
+ Scope | Placeholder.Context<Values[number]>,
46
+ Placeholder.Error<Values[number]>,
47
+ RenderEvent
48
+ > => {
49
+ return Fx.make((sink) =>
50
+ Effect.gen(function*(_) {
51
+ const eventSource = makeEventSource()
52
+ const context = yield* _(Effect.context<Scope>())
53
+ const hydrateCtx = unsafeGet(context, HydrateContext)
54
+ const scope = unsafeGet(context, Scope)
55
+
56
+ // If we're not longer hydrating, just render normally
57
+ if (hydrateCtx.hydrate === false) {
58
+ return render_(templateStrings, values)
59
+ }
60
+
61
+ const { parts, template, where, wire } = getHydrateEntry({
62
+ ...hydrateCtx,
63
+ document,
64
+ renderContext,
65
+ eventSource,
66
+ strings: templateStrings,
67
+ onCause: sink.onFailure
68
+ })
69
+
70
+ // If there are parts we need to render them before constructing our Wire
71
+ if (parts.length > 0) {
72
+ const refCounter = yield* _(indexRefCounter(parts.length))
73
+
74
+ // Do the work
75
+ yield* _(
76
+ renderValues(values, parts, refCounter, context, (index: number): HydrateContext => ({
77
+ where,
78
+ rootIndex: index,
79
+ parentTemplate: template,
80
+ hydrate: true
81
+ }))
82
+ )
83
+
84
+ // Wait for initial work to be completed
85
+ yield* _(refCounter.wait)
86
+ }
87
+
88
+ hydrateCtx.hydrate = false
89
+
90
+ yield* _(eventSource.setup(wire, scope))
91
+
92
+ yield* _(sink.onSuccess(DomRenderEvent(wire)))
93
+ yield* _(Effect.never)
63
94
  })
64
- const parts = yield* _(getParts)
65
-
66
- // If there are parts we need to render them before constructing our Wire
67
- if (parts.length > 0) {
68
- const refCounter = yield* _(indexRefCounter(parts.length))
69
-
70
- // Do the work
71
- yield* _(renderValues(values, parts, refCounter, errors.onFailure, (index: number): HydrateContext => ({
72
- where,
73
- rootIndex: index,
74
- parentTemplate: template,
75
- hydrate: true
76
- })))
77
-
78
- // Wait for initial work to be completed
79
- yield* _(refCounter.wait)
80
- }
81
-
82
- hydrateCtx.hydrate = false
83
-
84
- // Set the element when it is ready
85
- yield* _(ElementRef.set(elementRef, wire as T))
86
-
87
- // Return the Template instance
88
- return TemplateInstance(Fx.merge([events, errors]), elementRef)
89
- })
95
+ )
90
96
  }
97
+ }
91
98
 
92
99
  export function findRootParentChildNodes(where: HTMLElement): ParentChildNodes {
93
100
  const childNodes = findRootChildNodes(where)
@@ -205,7 +212,7 @@ export function findPartComment(childNodes: ArrayLike<Node>, partIndex: number)
205
212
  export function getHydrateEntry({
206
213
  childIndex,
207
214
  document,
208
- elementRef,
215
+ eventSource,
209
216
  onCause,
210
217
  parentTemplate,
211
218
  renderContext,
@@ -219,7 +226,7 @@ export function getHydrateEntry({
219
226
  rootIndex: number
220
227
  parentTemplate: Template | null
221
228
  strings: TemplateStringsArray
222
- elementRef: ElementRef.ElementRef<any>
229
+ eventSource: EventSource
223
230
  onCause: (cause: Cause.Cause<any>) => Effect.Effect<never, never, void>
224
231
  childIndex?: number
225
232
  }) {
@@ -229,7 +236,7 @@ export function getHydrateEntry({
229
236
  where = findTemplateResultPartChildNodes(where, parentTemplate, template, rootIndex, childIndex)
230
237
  }
231
238
 
232
- const getParts = buildParts(document, renderContext, template, where, elementRef, onCause, true)
239
+ const parts = buildParts(document, renderContext, template, where, eventSource, onCause, true)
233
240
 
234
241
  const wire = (() => {
235
242
  if (!parentTemplate) {
@@ -256,7 +263,7 @@ export function getHydrateEntry({
256
263
  return {
257
264
  template,
258
265
  wire,
259
- getParts,
266
+ parts,
260
267
  where
261
268
  } as const
262
269
  }
@@ -1,5 +1,6 @@
1
1
  import * as Deferred from "effect/Deferred"
2
2
  import * as Effect from "effect/Effect"
3
+ import * as Option from "effect/Option"
3
4
 
4
5
  export type IndexRefCounter = {
5
6
  release: (index: number) => Effect.Effect<never, never, void>
@@ -15,13 +16,16 @@ export function indexRefCounter(expected: number): Effect.Effect<
15
16
  IndexRefCounter
16
17
  > {
17
18
  return Effect.map(Deferred.make<never, void>(), (deferred) => {
19
+ const done = Deferred.succeed(deferred, undefined)
18
20
  const indexes = new Set<number>(Array.from({ length: expected }, (_, i) => i))
19
21
 
20
22
  function release(index: number) {
21
23
  return Effect.suspend(() => {
22
24
  if (indexes.delete(index) && indexes.size === 0) {
23
- return Deferred.succeed(deferred, undefined)
24
- } else return Effect.unit
25
+ return done
26
+ } else {
27
+ return Effect.unit
28
+ }
25
29
  })
26
30
  }
27
31
 
@@ -31,3 +35,60 @@ export function indexRefCounter(expected: number): Effect.Effect<
31
35
  }
32
36
  })
33
37
  }
38
+
39
+ export type IndexRefCounter2 = {
40
+ release: (index: number) => Effect.Effect<never, never, void>
41
+ expect: (count: number) => Effect.Effect<never, never, boolean>
42
+ wait: Effect.Effect<never, never, void>
43
+ }
44
+
45
+ /**
46
+ * @internal
47
+ */
48
+ export function indexRefCounter2(): Effect.Effect<
49
+ never,
50
+ never,
51
+ IndexRefCounter2
52
+ > {
53
+ return Effect.map(Deferred.make<never, void>(), (deferred) => {
54
+ let expected: Option.Option<number> = Option.none<number>()
55
+ const done = Deferred.succeed(deferred, undefined)
56
+ const indexes = new Set<number>()
57
+
58
+ function isDone() {
59
+ if (Option.isSome(expected)) {
60
+ return indexes.size === expected.value
61
+ } else {
62
+ return false
63
+ }
64
+ }
65
+
66
+ function release(index: number) {
67
+ return Effect.suspend(() => {
68
+ indexes.add(index)
69
+ if (isDone()) {
70
+ return done
71
+ } else {
72
+ return Effect.succeed(false)
73
+ }
74
+ })
75
+ }
76
+
77
+ function expect(count: number) {
78
+ return Effect.suspend(() => {
79
+ expected = Option.some(count)
80
+ if (isDone()) {
81
+ return Effect.as(done, false)
82
+ } else {
83
+ return Effect.succeed(true)
84
+ }
85
+ })
86
+ }
87
+
88
+ return {
89
+ release,
90
+ expect,
91
+ wait: Deferred.await(deferred)
92
+ }
93
+ })
94
+ }
@@ -42,7 +42,3 @@ declare global {
42
42
  declare module "@typed/fx/Fx" {
43
43
  export interface Fx<R, E, A> extends Placeholder<R, E, A> {}
44
44
  }
45
-
46
- declare module "effect/Effect" {
47
- export interface Effect<R, E, A> extends Placeholder<R, E, A> {}
48
- }
@@ -2,7 +2,6 @@ import * as Chunk from "effect/Chunk"
2
2
  import { globalValue } from "effect/GlobalValue"
3
3
  import * as Option from "effect/Option"
4
4
  import * as Template from "../Template.js"
5
- import { SELF_CLOSING_TAGS, TEXT_ONLY_NODES_REGEX } from "../Token.js"
6
5
  import type { TextChunk } from "./chunks.js"
7
6
  import {
8
7
  getPart,
@@ -16,6 +15,40 @@ import {
16
15
  // TODO: Consider ways to surface useful errors and warnings.
17
16
  // TODO: Profile for performance optimization
18
17
 
18
+ /**
19
+ * @since 1.0.0
20
+ */
21
+ export const TEXT_ONLY_NODES_REGEX = new Set([
22
+ "textarea",
23
+ "script",
24
+ "style",
25
+ "title",
26
+ "plaintext",
27
+ "xmp"
28
+ ])
29
+
30
+ /**
31
+ * @since 1.0.0
32
+ */
33
+ export const SELF_CLOSING_TAGS = new Set([
34
+ "area",
35
+ "base",
36
+ "br",
37
+ "col",
38
+ "command",
39
+ "embed",
40
+ "hr",
41
+ "img",
42
+ "input",
43
+ "keygen",
44
+ "link",
45
+ "meta",
46
+ "param",
47
+ "source",
48
+ "track",
49
+ "wbr"
50
+ ])
51
+
19
52
  export interface Parser {
20
53
  parse(templateStrings: ReadonlyArray<string>): Template.Template
21
54
  }
@@ -25,18 +58,38 @@ export function parse(templateStrings: ReadonlyArray<string>): Template.Template
25
58
  }
26
59
 
27
60
  const SPACE_REGEX = /\s/
28
- const isPartToken: TextPredicate = (input, pos) => input[pos] === "{" && input.slice(pos, pos + 8) === "{{__PART"
29
- const isPartEndToken: TextPredicate = (input, pos) => input[pos] === "_" && input.slice(pos, pos + 4) === "__}}"
30
- const isElementOpenToken: TextPredicate = (input, pos) => input[pos] === "<" && input[pos + 1] !== "/"
31
- const isElementCloseToken: TextPredicate = (input, pos) => input[pos] === "<" && input[pos + 1] === "/"
32
- const isEqualsToken: TextPredicate = (input, pos) => input[pos] === "="
33
- const isQuoteToken: TextPredicate = (input, pos) => input[pos] === `"`
34
- const isSingleQuoteToken: TextPredicate = (input, pos) => input[pos] === "'"
61
+ const PART_START = "{{__PART"
62
+ const PART_END = "__}}"
63
+ const chars = {
64
+ openBracket: "{",
65
+ closeBracket: "}",
66
+ underscore: "_",
67
+ equals: "=",
68
+ quote: `"`,
69
+ singleQuote: "'",
70
+ slash: "/",
71
+ greaterThan: ">",
72
+ lessThan: "<",
73
+ hypen: "-"
74
+ } as const
75
+
76
+ const isPartToken: TextPredicate = (input, pos) =>
77
+ input[pos] === chars.openBracket && input.slice(pos, pos + 8) === PART_START
78
+ const isPartEndToken: TextPredicate = (input, pos) =>
79
+ input[pos] === chars.underscore && input.slice(pos, pos + 4) === PART_END
80
+ const isElementOpenToken: TextPredicate = (input, pos) =>
81
+ input[pos] === chars.lessThan && input[pos + 1] !== chars.slash
82
+ const isElementCloseToken: TextPredicate = (input, pos) =>
83
+ input[pos] === chars.lessThan && input[pos + 1] === chars.slash
84
+ const isEqualsToken: TextPredicate = (input, pos) => input[pos] === chars.equals
85
+ const isQuoteToken: TextPredicate = (input, pos) => input[pos] === chars.quote
86
+ const isSingleQuoteToken: TextPredicate = (input, pos) => input[pos] === chars.singleQuote
35
87
  const isWhitespaceToken: TextPredicate = (input, pos) => SPACE_REGEX.test(input[pos])
36
- const isOpenTagEndToken: TextPredicate = (input, pos) => input[pos] === ">"
37
- const isSelfClosingTagEndToken: TextPredicate = (input, pos) => input[pos] === "/" && input[pos + 1] === ">"
88
+ const isOpenTagEndToken: TextPredicate = (input, pos) => input[pos] === chars.greaterThan
89
+ const isSelfClosingTagEndToken: TextPredicate = (input, pos) =>
90
+ input[pos] === chars.slash && input[pos + 1] === chars.greaterThan
38
91
  const isCommentEndToken: TextPredicate = (input, pos) =>
39
- input[pos] === "-" && input[pos + 1] === "-" && input[pos + 2] === ">"
92
+ input[pos] === chars.hypen && input[pos + 1] === chars.hypen && input[pos + 2] === chars.greaterThan
40
93
 
41
94
  type Context = "unknown" | "element"
42
95
 
@@ -154,6 +207,7 @@ class ParserImpl implements Parser {
154
207
 
155
208
  while (this.pos < this.length) {
156
209
  const node = this.parseNodeFromContext(this.context)
210
+
157
211
  if (node === undefined) {
158
212
  return nodes
159
213
  } else {
@@ -187,9 +241,16 @@ class ParserImpl implements Parser {
187
241
  const nextChar = this.nextChar()
188
242
 
189
243
  if (nextChar === "!") { // Comment
190
- this.consumeAmount(3)
244
+ this.consumeAmount(1)
245
+
246
+ const nextChar = this.nextChar()
191
247
 
192
- return [this.parseComment()]
248
+ if (nextChar == "-") {
249
+ this.consumeAmount(2)
250
+ return [this.parseComment()]
251
+ } else {
252
+ return [this.parseDocType()]
253
+ }
193
254
  } else if (nextChar === "/") { // Self-closing tag
194
255
  return this.selfClosingTagEnd()
195
256
  } else { // Elements
@@ -263,6 +324,7 @@ class ParserImpl implements Parser {
263
324
 
264
325
  private parseTextOnlyElement(tagName: string): Template.TextOnlyElement {
265
326
  const attributes = this.parseAttributes()
327
+
266
328
  this.path.push()
267
329
  const children = this.parseTextChildren()
268
330
  this.path.pop()
@@ -289,6 +351,14 @@ class ParserImpl implements Parser {
289
351
  return this.addPart(new Template.SparseCommentNode(textAndParts))
290
352
  }
291
353
 
354
+ private parseDocType(): Template.DocType {
355
+ this.parseTextUntil((char) => char === chars.greaterThan)
356
+ this.consumeAmount(1)
357
+ this.skipWhitespace()
358
+
359
+ return new Template.DocType("html")
360
+ }
361
+
292
362
  private parseTagName() {
293
363
  return this.parseTextUntilMany(tagNameMatches)
294
364
  }
@@ -304,6 +374,7 @@ class ParserImpl implements Parser {
304
374
  case null:
305
375
  return Skip
306
376
  case "whitespace":
377
+ this.skipWhitespace()
307
378
  return Continue([new Template.BooleanNode(name)])
308
379
  case "equals": {
309
380
  this.consumeAmount(1)
@@ -366,11 +437,13 @@ class ParserImpl implements Parser {
366
437
  case ".": {
367
438
  const property = name.slice(1)
368
439
 
369
- return this.addPart(
370
- property === "data"
371
- ? new Template.DataPartNode(unsafeParsePartIndex(text))
372
- : new Template.PropertyPartNode(property, unsafeParsePartIndex(text))
373
- )
440
+ if (property === "data") {
441
+ return this.addPart(new Template.DataPartNode(unsafeParsePartIndex(text)))
442
+ } else if (property === "props" || property === "properties") {
443
+ return this.addPart(new Template.PropertiesPartNode(unsafeParsePartIndex(text)))
444
+ } else {
445
+ return this.addPart(new Template.PropertyPartNode(property, unsafeParsePartIndex(text)))
446
+ }
374
447
  }
375
448
  case "@":
376
449
  return this.addPart(new Template.EventPartNode(name.slice(1), unsafeParsePartIndex(text)))
@@ -617,7 +690,7 @@ function parseTextAndParts<T>(s: string, f: (index: number) => T): Array<Templat
617
690
  return out
618
691
  }
619
692
 
620
- export const parser: Parser = globalValue(Symbol.for("../Parser2.js"), () => new ParserImpl())
693
+ export const parser: Parser = globalValue(Symbol.for("@typed/template/Parser2"), () => new ParserImpl())
621
694
 
622
695
  const digestSize = 2
623
696
  const multiplier = 33