@typed/template 0.2.0 → 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 (213) hide show
  1. package/dist/cjs/Directive.js +1 -1
  2. package/dist/cjs/Directive.js.map +1 -1
  3. package/dist/cjs/ElementRef.js +23 -13
  4. package/dist/cjs/ElementRef.js.map +1 -1
  5. package/dist/cjs/ElementSource.js +16 -18
  6. package/dist/cjs/ElementSource.js.map +1 -1
  7. package/dist/cjs/EventHandler.js +1 -1
  8. package/dist/cjs/EventHandler.js.map +1 -1
  9. package/dist/cjs/Html.js +31 -32
  10. package/dist/cjs/Html.js.map +1 -1
  11. package/dist/cjs/HtmlChunk.js +4 -1
  12. package/dist/cjs/HtmlChunk.js.map +1 -1
  13. package/dist/cjs/Hydrate.js +1 -1
  14. package/dist/cjs/Hydrate.js.map +1 -1
  15. package/dist/cjs/Many.js +14 -13
  16. package/dist/cjs/Many.js.map +1 -1
  17. package/dist/cjs/Parser.js +11 -323
  18. package/dist/cjs/Parser.js.map +1 -1
  19. package/dist/cjs/Placeholder.js +3 -3
  20. package/dist/cjs/Placeholder.js.map +1 -1
  21. package/dist/cjs/Platform.js +4 -4
  22. package/dist/cjs/Platform.js.map +1 -1
  23. package/dist/cjs/Render.js +1 -1
  24. package/dist/cjs/Render.js.map +1 -1
  25. package/dist/cjs/RenderContext.js +48 -27
  26. package/dist/cjs/RenderContext.js.map +1 -1
  27. package/dist/cjs/RenderTemplate.js +2 -17
  28. package/dist/cjs/RenderTemplate.js.map +1 -1
  29. package/dist/cjs/Template.js +27 -1
  30. package/dist/cjs/Template.js.map +1 -1
  31. package/dist/cjs/TemplateInstance.js +2 -2
  32. package/dist/cjs/TemplateInstance.js.map +1 -1
  33. package/dist/cjs/Test.js +20 -7
  34. package/dist/cjs/Test.js.map +1 -1
  35. package/dist/cjs/index.js +0 -12
  36. package/dist/cjs/index.js.map +1 -1
  37. package/dist/cjs/internal/EventSource.js +95 -0
  38. package/dist/cjs/internal/EventSource.js.map +1 -0
  39. package/dist/cjs/internal/browser.js +11 -11
  40. package/dist/cjs/internal/browser.js.map +1 -1
  41. package/dist/cjs/internal/hydrate.js +49 -50
  42. package/dist/cjs/internal/hydrate.js.map +1 -1
  43. package/dist/cjs/internal/indexRefCounter.js +49 -2
  44. package/dist/cjs/internal/indexRefCounter.js.map +1 -1
  45. package/dist/cjs/internal/parser.js +60 -17
  46. package/dist/cjs/internal/parser.js.map +1 -1
  47. package/dist/cjs/internal/parts.js +128 -28
  48. package/dist/cjs/internal/parts.js.map +1 -1
  49. package/dist/cjs/internal/render.js +486 -53
  50. package/dist/cjs/internal/render.js.map +1 -1
  51. package/dist/cjs/internal/server.js +5 -2
  52. package/dist/cjs/internal/server.js.map +1 -1
  53. package/dist/dts/Directive.d.ts.map +1 -1
  54. package/dist/dts/ElementRef.d.ts +4 -2
  55. package/dist/dts/ElementRef.d.ts.map +1 -1
  56. package/dist/dts/ElementSource.d.ts +10 -5
  57. package/dist/dts/ElementSource.d.ts.map +1 -1
  58. package/dist/dts/EventHandler.d.ts.map +1 -1
  59. package/dist/dts/Html.d.ts +1 -1
  60. package/dist/dts/Html.d.ts.map +1 -1
  61. package/dist/dts/HtmlChunk.d.ts.map +1 -1
  62. package/dist/dts/Many.d.ts +13 -11
  63. package/dist/dts/Many.d.ts.map +1 -1
  64. package/dist/dts/Parser.d.ts +3 -6
  65. package/dist/dts/Parser.d.ts.map +1 -1
  66. package/dist/dts/Part.d.ts +13 -3
  67. package/dist/dts/Part.d.ts.map +1 -1
  68. package/dist/dts/Placeholder.d.ts +2 -2
  69. package/dist/dts/Placeholder.d.ts.map +1 -1
  70. package/dist/dts/Render.d.ts +2 -1
  71. package/dist/dts/Render.d.ts.map +1 -1
  72. package/dist/dts/RenderContext.d.ts +5 -4
  73. package/dist/dts/RenderContext.d.ts.map +1 -1
  74. package/dist/dts/RenderTemplate.d.ts +2 -16
  75. package/dist/dts/RenderTemplate.d.ts.map +1 -1
  76. package/dist/dts/Renderable.d.ts +2 -2
  77. package/dist/dts/Renderable.d.ts.map +1 -1
  78. package/dist/dts/Template.d.ts +21 -3
  79. package/dist/dts/Template.d.ts.map +1 -1
  80. package/dist/dts/TemplateInstance.d.ts +3 -2
  81. package/dist/dts/TemplateInstance.d.ts.map +1 -1
  82. package/dist/dts/Test.d.ts +4 -6
  83. package/dist/dts/Test.d.ts.map +1 -1
  84. package/dist/dts/index.d.ts +0 -4
  85. package/dist/dts/index.d.ts.map +1 -1
  86. package/dist/dts/internal/EventSource.d.ts +12 -0
  87. package/dist/dts/internal/EventSource.d.ts.map +1 -0
  88. package/dist/dts/internal/browser.d.ts.map +1 -1
  89. package/dist/dts/internal/hydrate.d.ts +5 -5
  90. package/dist/dts/internal/hydrate.d.ts.map +1 -1
  91. package/dist/dts/internal/indexRefCounter.d.ts +5 -0
  92. package/dist/dts/internal/indexRefCounter.d.ts.map +1 -1
  93. package/dist/dts/internal/module-augmentation.d.ts +0 -4
  94. package/dist/dts/internal/module-augmentation.d.ts.map +1 -1
  95. package/dist/dts/internal/parser.d.ts +8 -0
  96. package/dist/dts/internal/parser.d.ts.map +1 -1
  97. package/dist/dts/internal/parts.d.ts +66 -56
  98. package/dist/dts/internal/parts.d.ts.map +1 -1
  99. package/dist/dts/internal/render.d.ts +7 -7
  100. package/dist/dts/internal/render.d.ts.map +1 -1
  101. package/dist/dts/internal/server.d.ts.map +1 -1
  102. package/dist/esm/Directive.js +1 -1
  103. package/dist/esm/Directive.js.map +1 -1
  104. package/dist/esm/ElementRef.js +12 -7
  105. package/dist/esm/ElementRef.js.map +1 -1
  106. package/dist/esm/ElementSource.js +16 -13
  107. package/dist/esm/ElementSource.js.map +1 -1
  108. package/dist/esm/EventHandler.js +1 -1
  109. package/dist/esm/EventHandler.js.map +1 -1
  110. package/dist/esm/Html.js +29 -24
  111. package/dist/esm/Html.js.map +1 -1
  112. package/dist/esm/HtmlChunk.js +6 -1
  113. package/dist/esm/HtmlChunk.js.map +1 -1
  114. package/dist/esm/Hydrate.js +1 -1
  115. package/dist/esm/Hydrate.js.map +1 -1
  116. package/dist/esm/Many.js +14 -10
  117. package/dist/esm/Many.js.map +1 -1
  118. package/dist/esm/Parser.js +6 -335
  119. package/dist/esm/Parser.js.map +1 -1
  120. package/dist/esm/Placeholder.js +2 -2
  121. package/dist/esm/Placeholder.js.map +1 -1
  122. package/dist/esm/Platform.js +2 -2
  123. package/dist/esm/Platform.js.map +1 -1
  124. package/dist/esm/Render.js +1 -1
  125. package/dist/esm/Render.js.map +1 -1
  126. package/dist/esm/RenderContext.js +38 -17
  127. package/dist/esm/RenderContext.js.map +1 -1
  128. package/dist/esm/RenderTemplate.js +2 -12
  129. package/dist/esm/RenderTemplate.js.map +1 -1
  130. package/dist/esm/Template.js +24 -0
  131. package/dist/esm/Template.js.map +1 -1
  132. package/dist/esm/TemplateInstance.js +2 -2
  133. package/dist/esm/TemplateInstance.js.map +1 -1
  134. package/dist/esm/Test.js +20 -7
  135. package/dist/esm/Test.js.map +1 -1
  136. package/dist/esm/index.js +0 -4
  137. package/dist/esm/index.js.map +1 -1
  138. package/dist/esm/internal/EventSource.js +91 -0
  139. package/dist/esm/internal/EventSource.js.map +1 -0
  140. package/dist/esm/internal/browser.js +12 -12
  141. package/dist/esm/internal/browser.js.map +1 -1
  142. package/dist/esm/internal/hydrate.js +45 -46
  143. package/dist/esm/internal/hydrate.js.map +1 -1
  144. package/dist/esm/internal/indexRefCounter.js +50 -2
  145. package/dist/esm/internal/indexRefCounter.js.map +1 -1
  146. package/dist/esm/internal/parser.js +84 -17
  147. package/dist/esm/internal/parser.js.map +1 -1
  148. package/dist/esm/internal/parts.js +110 -27
  149. package/dist/esm/internal/parts.js.map +1 -1
  150. package/dist/esm/internal/render.js +476 -58
  151. package/dist/esm/internal/render.js.map +1 -1
  152. package/dist/esm/internal/server.js +5 -4
  153. package/dist/esm/internal/server.js.map +1 -1
  154. package/package.json +10 -26
  155. package/src/Directive.ts +1 -1
  156. package/src/ElementRef.ts +18 -14
  157. package/src/ElementSource.ts +62 -47
  158. package/src/EventHandler.ts +1 -1
  159. package/src/Html.ts +58 -57
  160. package/src/HtmlChunk.ts +15 -1
  161. package/src/Hydrate.ts +1 -1
  162. package/src/Many.ts +53 -43
  163. package/src/Parser.ts +10 -453
  164. package/src/Part.ts +15 -3
  165. package/src/Placeholder.ts +4 -4
  166. package/src/Platform.ts +2 -2
  167. package/src/Render.ts +7 -2
  168. package/src/RenderContext.ts +47 -19
  169. package/src/RenderTemplate.ts +9 -54
  170. package/src/Renderable.ts +2 -1
  171. package/src/Template.ts +26 -0
  172. package/src/TemplateInstance.ts +9 -9
  173. package/src/Test.ts +40 -21
  174. package/src/index.ts +0 -4
  175. package/src/internal/EventSource.ts +153 -0
  176. package/src/internal/browser.ts +26 -25
  177. package/src/internal/hydrate.ts +68 -61
  178. package/src/internal/indexRefCounter.ts +63 -2
  179. package/src/internal/module-augmentation.ts +0 -4
  180. package/src/internal/parser.ts +92 -19
  181. package/src/internal/parts.ts +158 -73
  182. package/src/internal/render.ts +701 -89
  183. package/src/internal/server.ts +5 -3
  184. package/Token/package.json +0 -6
  185. package/Tokenizer/package.json +0 -6
  186. package/dist/cjs/Token.js +0 -270
  187. package/dist/cjs/Token.js.map +0 -1
  188. package/dist/cjs/Tokenizer.js +0 -18
  189. package/dist/cjs/Tokenizer.js.map +0 -1
  190. package/dist/cjs/internal/readAttribute.js +0 -34
  191. package/dist/cjs/internal/readAttribute.js.map +0 -1
  192. package/dist/cjs/internal/tokenizer.js +0 -264
  193. package/dist/cjs/internal/tokenizer.js.map +0 -1
  194. package/dist/dts/Token.d.ts +0 -202
  195. package/dist/dts/Token.d.ts.map +0 -1
  196. package/dist/dts/Tokenizer.d.ts +0 -6
  197. package/dist/dts/Tokenizer.d.ts.map +0 -1
  198. package/dist/dts/internal/readAttribute.d.ts +0 -9
  199. package/dist/dts/internal/readAttribute.d.ts.map +0 -1
  200. package/dist/dts/internal/tokenizer.d.ts +0 -3
  201. package/dist/dts/internal/tokenizer.d.ts.map +0 -1
  202. package/dist/esm/Token.js +0 -264
  203. package/dist/esm/Token.js.map +0 -1
  204. package/dist/esm/Tokenizer.js +0 -9
  205. package/dist/esm/Tokenizer.js.map +0 -1
  206. package/dist/esm/internal/readAttribute.js +0 -24
  207. package/dist/esm/internal/readAttribute.js.map +0 -1
  208. package/dist/esm/internal/tokenizer.js +0 -296
  209. package/dist/esm/internal/tokenizer.js.map +0 -1
  210. package/src/Token.ts +0 -269
  211. package/src/Tokenizer.ts +0 -10
  212. package/src/internal/readAttribute.ts +0 -28
  213. package/src/internal/tokenizer.ts +0 -338
package/src/Parser.ts CHANGED
@@ -2,456 +2,13 @@
2
2
  * @since 1.0.0
3
3
  */
4
4
 
5
- import * as Chunk from "effect/Chunk"
6
- import { globalValue } from "effect/GlobalValue"
7
- import { templateHash } from "./internal/parser.js"
8
- import * as Template from "./Template.js"
9
- import type { Token } from "./Token.js"
10
- import { tokenize } from "./Tokenizer.js"
11
-
12
- /**
13
- * @since 1.0.0
14
- */
15
- export interface Parser {
16
- parse(template: ReadonlyArray<string>, tokenStream?: Iterator<Token>): Template.Template
17
- }
18
-
19
- const iterator = <A>(iterable: Iterable<A>): Iterator<A> => iterable[Symbol.iterator]()
20
-
21
- const dropLast = Chunk.dropRight(1)
22
-
23
- class ParserImpl {
24
- protected _template: ReadonlyArray<string> = []
25
- protected _tokenStream!: Iterator<Token>
26
- protected _lookahead!: Token | null
27
- protected _stack!: Chunk.Chunk<number>
28
- protected _parts!: Array<[Template.PartNode | Template.SparsePartNode, Chunk.Chunk<number>]>
29
- protected _skipWhitespace!: boolean
30
-
31
- parse(
32
- template: ReadonlyArray<string>,
33
- tokenStream: Iterator<Token> = iterator(tokenize(template))
34
- ): Template.Template {
35
- this._template = template
36
- this._tokenStream = tokenStream
37
- this._lookahead = this.getNextToken()
38
- this._stack = Chunk.empty()
39
- this._parts = []
40
- this._skipWhitespace = true
41
-
42
- const ast = this.Template(templateHash(template))
43
-
44
- return ast
45
- }
46
-
47
- protected Template(hash: string): Template.Template {
48
- const template = new Template.Template(this.Children(), hash, this._parts)
49
-
50
- return template
51
- }
52
-
53
- protected Node(): Template.Node | null {
54
- const token = this.findTokenOfType(
55
- "opening-tag",
56
- "text",
57
- "comment",
58
- "comment-start",
59
- "part-token",
60
- "closing-tag"
61
- )
62
-
63
- if (token._tag === "closing-tag") return null
64
-
65
- if (token._tag === "text") {
66
- if (this._skipWhitespace && token.value.trim() === "") return null
67
-
68
- return new Template.TextNode(token.value)
69
- }
70
-
71
- if (token._tag === "comment") {
72
- return new Template.CommentNode(token.value)
73
- }
74
-
75
- if (token._tag === "comment-start") {
76
- const node = this.Comment("")
77
- return node
78
- }
79
-
80
- // Some annoyances here for generating the correct path for node parts
81
- if (token._tag === "part-token") {
82
- this._skipWhitespace = false
83
- return this.addPartWithoutCurrent(new Template.NodePart(token.index))
84
- }
85
-
86
- this._skipWhitespace = true
87
-
88
- if (token.isSelfClosing) {
89
- return this.SelfClosingElementNode(token.name)
90
- } else if (token.textOnly) {
91
- return this.TextOnlyElement(token.name)
92
- } else {
93
- return this.ElementNode(token.name)
94
- }
95
- }
96
-
97
- protected ElementNode(tagName: string): Template.ElementNode {
98
- const attrs: Array<Template.Attribute> = []
99
- const children: Array<Template.Node> = []
100
- const element = new Template.ElementNode(tagName, attrs, children)
101
-
102
- this.Attributes(attrs)
103
- this.Children(children)
104
-
105
- return element
106
- }
107
-
108
- protected SelfClosingElementNode(tagName: string): Template.SelfClosingElementNode {
109
- const attrs: Array<Template.Attribute> = []
110
- const element = new Template.SelfClosingElementNode(tagName, attrs)
111
-
112
- this.Attributes(attrs)
113
-
114
- return element
115
- }
116
-
117
- protected TextOnlyElement(tagName: string): Template.TextOnlyElement {
118
- const attrs: Array<Template.Attribute> = []
119
- const children: Array<Template.Text> = []
120
- const element = new Template.TextOnlyElement(tagName, attrs, children)
121
-
122
- this.Attributes(attrs)
123
- this.TextChildren(children)
124
-
125
- return element
126
- }
127
-
128
- protected Attributes(attributes: Array<Template.Attribute> = []): Array<Template.Attribute> {
129
- while (this._lookahead !== null) {
130
- if (this._lookahead._tag === "opening-tag-end") {
131
- this._lookahead = this.getNextToken()
132
- break
133
- } else {
134
- const attr = this.Attribute()
135
- if (attr) {
136
- attributes.push(attr)
137
- } else {
138
- break
139
- }
140
- }
141
- }
142
-
143
- return attributes
144
- }
145
-
146
- protected Attribute(): Template.Attribute | null {
147
- const token = this.findTokenOfType(
148
- "attribute",
149
- "attribute-start",
150
- "boolean-attribute",
151
- "boolean-attribute-start",
152
- "className-attribute-start",
153
- "data-attribute-start",
154
- "event-attribute-start",
155
- "property-attribute-start",
156
- "ref-attribute-start",
157
- "text"
158
- )
159
-
160
- switch (token._tag) {
161
- case "attribute":
162
- return new Template.AttributeNode(token.name, token.value)
163
- case "attribute-start":
164
- return this.SparseAttrNode(token.name)
165
- case "boolean-attribute":
166
- return new Template.BooleanNode(token.name)
167
- case "boolean-attribute-start":
168
- return this.BooleanNode(token.name)
169
- case "className-attribute-start":
170
- return this.SparseClassNameNode()
171
- case "data-attribute-start":
172
- return this.DataNode()
173
- case "event-attribute-start":
174
- return this.EventNode(token.name)
175
- case "property-attribute-start":
176
- return this.PropertyNode(token.name)
177
- case "ref-attribute-start":
178
- return this.RefNode()
179
- case "text":
180
- return null
181
- }
182
- }
183
-
184
- protected SparseAttrNode(name: string): Template.SparseAttrNode | Template.AttrPartNode | Template.BooleanNode {
185
- const nodes: Array<Template.AttrPartNode | Template.TextNode> = []
186
-
187
- while (this._lookahead !== null) {
188
- const token = this.findTokenOfType("text", "part-token", "attribute-end")
189
-
190
- if (token._tag === "text") {
191
- if (token.value === "") continue
192
- nodes.push(new Template.TextNode(token.value))
193
- } else if (token._tag === "part-token") {
194
- nodes.push(new Template.AttrPartNode(name, token.index))
195
- } else {
196
- if (nodes.length === 0) {
197
- return new Template.BooleanNode(name)
198
- }
199
-
200
- break
201
- }
202
- }
203
-
204
- if (nodes.length === 1) {
205
- return this.addPart(nodes[0] as Template.AttrPartNode)
206
- }
207
-
208
- if (nodes.length === 0) {
209
- return new Template.BooleanNode(name)
210
- }
211
-
212
- return this.addPart(new Template.SparseAttrNode(name, nodes))
213
- }
214
-
215
- protected BooleanNode(name: string): Template.BooleanPartNode {
216
- // We know that the next token MUST be a part-token
217
- const part = this.predictNextToken("part-token")
218
-
219
- // We don't need to do anything with a boolean-attribute-end token, skip it
220
- this.skipIfNextToken("boolean-attribute-end")
221
-
222
- return this.addPart(new Template.BooleanPartNode(name, part.index))
223
- }
224
-
225
- protected SparseClassNameNode(): Template.SparseClassNameNode | Template.ClassNameNode {
226
- const nodes: Array<Template.ClassNameNode> = []
227
-
228
- while (this._lookahead !== null) {
229
- const token = this.findTokenOfType("text", "part-token", "className-attribute-end")
230
-
231
- if (token._tag === "text") {
232
- if (token.value.trim() === "") continue
233
- nodes.push(new Template.TextNode(token.value))
234
- } else if (token._tag === "part-token") {
235
- nodes.push(new Template.ClassNamePartNode(token.index))
236
- } else {
237
- break
238
- }
239
- }
240
-
241
- if (nodes.length === 0) {
242
- throw new SyntaxError("Expected at least one node in class name attribute")
243
- }
244
-
245
- if (nodes.length === 1) {
246
- return this.addPart(nodes[0] as Template.ClassNamePartNode)
247
- }
248
-
249
- return this.addPart(new Template.SparseClassNameNode(nodes))
250
- }
251
-
252
- protected DataNode(): Template.DataPartNode {
253
- const part = this.predictNextToken("part-token")
254
-
255
- // We don't need to do anything with a data-attribute-end token, skip it
256
- this.skipIfNextToken("data-attribute-end")
257
-
258
- return this.addPart(new Template.DataPartNode(part.index))
259
- }
260
-
261
- protected EventNode(name: string): Template.EventPartNode {
262
- const part = this.predictNextToken("part-token")
263
-
264
- // We don't need to do anything with a event-attribute-end token, skip it
265
- this.skipIfNextToken("event-attribute-end")
266
-
267
- return this.addPart(new Template.EventPartNode(name, part.index))
268
- }
269
-
270
- protected PropertyNode(name: string): Template.PropertyPartNode {
271
- const part = this.predictNextToken("part-token")
272
-
273
- // We don't need to do anything with a property-attribute-end token, skip it
274
- this.skipIfNextToken("property-attribute-end")
275
-
276
- return this.addPart(new Template.PropertyPartNode(name, part.index))
277
- }
278
-
279
- protected RefNode(): Template.RefPartNode {
280
- const part = this.predictNextToken("part-token")
281
-
282
- // We don't need to do anything with a ref-attribute-end token, skip it
283
- this.skipIfNextToken("ref-attribute-end")
284
-
285
- return this.addPart(new Template.RefPartNode(part.index))
286
- }
287
-
288
- protected TextPartNode(): Template.TextPartNode {
289
- const token = this.predictNextToken("part-token")
290
-
291
- return this.addPart(new Template.TextPartNode(token.index))
292
- }
293
-
294
- protected Children(children: Array<Template.Node> = []): Array<Template.Node> {
295
- let i = 0
296
- while (this._lookahead) {
297
- if (this._lookahead._tag === "closing-tag") {
298
- this._lookahead = this.getNextToken()
299
- this._skipWhitespace = true
300
-
301
- break
302
- } else if (
303
- this._skipWhitespace &&
304
- this._lookahead._tag === "text" &&
305
- this._lookahead.value.trim() === ""
306
- ) {
307
- this._lookahead = this.getNextToken()
308
- } else {
309
- this._stack = Chunk.append(this._stack, i++)
310
- const child = this.Node()
311
- this._stack = Chunk.dropRight(this._stack, 1)
312
-
313
- if (child) {
314
- children.push(child)
315
- } else {
316
- break
317
- }
318
- }
319
- }
320
-
321
- return children
322
- }
323
-
324
- protected TextChildren(children: Array<Template.Text> = []): Array<Template.Text> {
325
- while (this._lookahead !== null) {
326
- const token = this.findTokenOfType("text", "part-token", "closing-tag")
327
-
328
- if (token._tag === "text") {
329
- if (token.value) children.push(new Template.TextNode(token.value))
330
- } else if (token._tag === `part-token`) {
331
- children.push(this.addPart(new Template.TextPartNode(token.index)))
332
- } else {
333
- this._skipWhitespace = true
334
- break
335
- }
336
- }
337
-
338
- return children
339
- }
340
-
341
- protected Comment(before: string): Template.Comment {
342
- const nodes: Array<Template.CommentPartNode | Template.TextNode> = []
343
-
344
- if (before) {
345
- nodes.push(new Template.TextNode(before))
346
- }
347
-
348
- while (this._lookahead !== null) {
349
- const token = this.findTokenOfType("part-token", "text", "comment-end")
350
-
351
- if (token._tag === "part-token") {
352
- nodes.push(new Template.CommentPartNode(token.index))
353
- } else if (token._tag === "text") {
354
- nodes.push(new Template.TextNode(token.value))
355
- } else {
356
- break
357
- }
358
- }
359
-
360
- if (nodes.length === 1) {
361
- const node = nodes[0]
362
-
363
- if (node._tag === "comment-part") {
364
- return new Template.CommentPartNode(node.index)
365
- } else {
366
- return new Template.CommentNode(node.value)
367
- }
368
- }
369
-
370
- return this.addPart(new Template.SparseCommentNode(nodes))
371
- }
372
-
373
- protected findTokenOfType<T extends ReadonlyArray<Token["_tag"]>>(
374
- ...tokenTypes: T
375
- ): Extract<Token, { readonly _tag: T[number] }> {
376
- const token = this._lookahead
377
-
378
- if (token === null) {
379
- throw new SyntaxError(
380
- `Unexpected end of template. Expected one of types ${tokenTypes.join(", ")}.`
381
- )
382
- }
383
-
384
- for (let i = 0; i < tokenTypes.length; i++) {
385
- if (token._tag === tokenTypes[i]) {
386
- this._lookahead = this.getNextToken()
387
-
388
- return token as Extract<Token, { readonly _tag: T[number] }>
389
- }
390
- }
391
-
392
- throw new SyntaxError(
393
- `Unexpected token ${token._tag}. Expected one of types ${tokenTypes.join(", ")}.`
394
- )
395
- }
396
-
397
- protected predictNextToken<T extends Token["_tag"]>(
398
- type: T
399
- ): Extract<Token, { readonly _tag: T }> {
400
- const token = this._lookahead
401
-
402
- if (token === null) {
403
- throw new SyntaxError(`Unexpected end of template. Expected ${type}.`)
404
- }
405
-
406
- if (token._tag !== type) {
407
- throw new SyntaxError(`Unexpected token ${token._tag}. Expected ${type}.`)
408
- }
409
-
410
- this._lookahead = this.getNextToken()
411
-
412
- return token as Extract<Token, { readonly _tag: T }>
413
- }
414
-
415
- protected skipIfNextToken<T extends Token["_tag"]>(type: T): boolean {
416
- const token = this._lookahead
417
-
418
- if (token === null) {
419
- return false
420
- }
421
-
422
- if (token._tag !== type) {
423
- return false
424
- }
425
-
426
- this._lookahead = this.getNextToken()
427
-
428
- return true
429
- }
430
-
431
- protected getNextToken(): Token | null {
432
- const { done, value } = this._tokenStream.next()
433
-
434
- if (done) {
435
- return null
436
- }
437
-
438
- return value
439
- }
440
-
441
- protected addPart<A extends Template.PartNode | Template.SparsePartNode>(part: A): A {
442
- this._parts.push([part, this._stack])
443
-
444
- return part
445
- }
446
-
447
- protected addPartWithoutCurrent<A extends Template.PartNode>(part: A): A {
448
- this._parts.push([part, dropLast(this._stack)])
449
-
450
- return part
451
- }
452
- }
453
-
454
- /**
455
- * @since 1.0.0
456
- */
457
- export const parser: Parser = globalValue(Symbol.for("./Parser.js"), () => new ParserImpl())
5
+ export {
6
+ /**
7
+ * @since 1.0.0
8
+ */
9
+ parse,
10
+ /**
11
+ * @since 1.0.0
12
+ */
13
+ templateHash
14
+ } from "./internal/parser.js"
package/src/Part.ts CHANGED
@@ -22,6 +22,7 @@ export type Part =
22
22
  | PropertyPart
23
23
  | RefPart
24
24
  | TextPart
25
+ | PropertiesPart
25
26
 
26
27
  /**
27
28
  * @since 1.0.0
@@ -75,11 +76,11 @@ export interface DataPart {
75
76
  export interface EventPart {
76
77
  readonly _tag: "event"
77
78
  readonly name: string
78
- readonly value: EventHandler<unknown, never> | null | undefined
79
+ readonly source: ElementSource<any>
80
+ readonly value: null
79
81
  readonly index: number
80
82
  readonly onCause: (cause: Cause<unknown>) => Effect<never, never, unknown>
81
-
82
- readonly update: <R = never>(value: EventHandler<R, never> | null | undefined) => Effect<R | Scope, never, void>
83
+ readonly addEventListener: (handler: EventHandler<never, never, Event>) => void
83
84
  }
84
85
 
85
86
  /**
@@ -136,6 +137,17 @@ export interface NodePart {
136
137
  readonly update: (value: this["value"]) => Effect<Scope, never, void>
137
138
  }
138
139
 
140
+ /**
141
+ * @since 1.0.0
142
+ */
143
+ export interface PropertiesPart {
144
+ readonly _tag: "properties"
145
+ readonly value: Readonly<Record<string, any>> | null | undefined
146
+ readonly index: number
147
+
148
+ readonly update: (value: this["value"]) => Effect<Scope, never, void>
149
+ }
150
+
139
151
  /**
140
152
  * @since 1.0.0
141
153
  */
@@ -6,13 +6,13 @@ import "./internal/module-augmentation.js"
6
6
  import type { Fx } from "@typed/fx/Fx"
7
7
  import { isFx } from "@typed/fx/Fx"
8
8
  import * as RefSubject from "@typed/fx/RefSubject"
9
- import type { Scope } from "effect"
10
- import { Effect } from "effect"
9
+ import * as Effect from "effect/Effect"
10
+ import type * as Scope from "effect/Scope"
11
11
 
12
12
  /**
13
13
  * @since 1.0.0
14
14
  */
15
- export const PlaceholderTypeId = Symbol.for("./Placholder.js")
15
+ export const PlaceholderTypeId = Symbol.for("@typed/template/Placholder")
16
16
  /**
17
17
  * @since 1.0.0
18
18
  */
@@ -64,7 +64,7 @@ export namespace Placeholder {
64
64
  if (isFx<R, E, A>(placeholder) || Effect.isEffect(placeholder)) {
65
65
  return RefSubject.make(placeholder as Fx<R, E, A>)
66
66
  } else {
67
- return RefSubject.of(placeholder as A)
67
+ return RefSubject.of<A, E>(placeholder as A)
68
68
  }
69
69
  }
70
70
  }
package/src/Platform.ts CHANGED
@@ -30,7 +30,7 @@ export function htmlResponse<R, E>(
30
30
  {
31
31
  ...CAMEL_CASE_CONTENT_TYPE,
32
32
  ...options,
33
- headers: { ...HYPHENATED_CONTENT_TYPE, ...options?.headers }
33
+ headers: Headers.unsafeFromRecord({ ...HYPHENATED_CONTENT_TYPE, ...options?.headers })
34
34
  }
35
35
  )
36
36
  )
@@ -48,7 +48,7 @@ export function htmlResponseString(
48
48
  {
49
49
  ...CAMEL_CASE_CONTENT_TYPE,
50
50
  ...options,
51
- headers: { ...HYPHENATED_CONTENT_TYPE, ...options?.headers }
51
+ headers: Headers.unsafeFromRecord({ ...HYPHENATED_CONTENT_TYPE, ...options?.headers })
52
52
  }
53
53
  )
54
54
  }
package/src/Render.ts CHANGED
@@ -7,6 +7,7 @@ import { Document } from "@typed/dom/Document"
7
7
  import { RootElement } from "@typed/dom/RootElement"
8
8
  import * as Fx from "@typed/fx/Fx"
9
9
  import { type Rendered } from "@typed/wire"
10
+ import type { Layer, Scope } from "effect"
10
11
  import * as Effect from "effect/Effect"
11
12
  import { attachRoot, renderTemplate } from "./internal/render.js"
12
13
  import { RenderContext } from "./RenderContext.js"
@@ -40,6 +41,10 @@ export function render<R, E, T extends RenderEvent | null>(
40
41
  */
41
42
  export function renderLayer<R, E, T extends RenderEvent | null>(
42
43
  rendered: Fx.Fx<R, E, T>
43
- ) {
44
- return Fx.drainLayer(Fx.switchMapCause(render(rendered), Effect.logError))
44
+ ): Layer.Layer<
45
+ Document | RenderContext | RootElement | Exclude<Exclude<R, RenderTemplate>, Scope.Scope>,
46
+ never,
47
+ never
48
+ > {
49
+ return Fx.drainLayer(Fx.switchMapCause(render(rendered), (e) => Fx.fromEffect(Effect.logError(e))))
45
50
  }
@@ -11,10 +11,13 @@ import type { Environment } from "@typed/environment"
11
11
  import { CurrentEnvironment } from "@typed/environment"
12
12
  import * as Idle from "@typed/fx/Idle"
13
13
  import type { Rendered } from "@typed/wire"
14
- import { Effect, Layer, Option } from "effect"
14
+ import * as Effect from "effect/Effect"
15
+ import * as Layer from "effect/Layer"
16
+ import * as Option from "effect/Option"
15
17
  import * as Scope from "effect/Scope"
16
18
  import type { Entry } from "./Entry.js"
17
- import type { Part, SparsePart } from "./Part.js"
19
+
20
+ // TODO: We should probably have a more explicit environment type between DOM/HTML rendering
18
21
 
19
22
  /**
20
23
  * The context in which templates are rendered within
@@ -54,7 +57,7 @@ export const RenderContext: Context.Tagged<RenderContext, RenderContext> = Conte
54
57
  * @since 1.0.0
55
58
  */
56
59
  export interface RenderQueue {
57
- readonly add: (part: Part | SparsePart, task: () => void) => Effect.Effect<Scope.Scope, never, void>
60
+ readonly add: (part: unknown, task: () => void) => Effect.Effect<Scope.Scope, never, void>
58
61
  }
59
62
 
60
63
  /**
@@ -114,14 +117,14 @@ const buildWithCurrentEnvironment = (environment: Environment, skipRenderSchedul
114
117
  /**
115
118
  * @since 1.0.0
116
119
  */
117
- export const browser: (
120
+ export const dom: (
118
121
  window: Window & GlobalThis,
119
122
  options?: DomServicesElementParams & { readonly skipRenderScheduling?: boolean }
120
123
  ) => Layer.Layer<never, never, RenderContext | CurrentEnvironment | DomServices> = (window, options) =>
121
124
  Layer.provideMerge(
122
125
  Layer.mergeAll(
123
126
  buildWithCurrentEnvironment(
124
- "browser",
127
+ "dom",
125
128
  options?.skipRenderScheduling
126
129
  ),
127
130
  domServices(options)
@@ -146,8 +149,9 @@ export {
146
149
  }
147
150
 
148
151
  class RenderQueueImpl implements RenderQueue {
149
- queue = new Map<Part | SparsePart, () => void>()
152
+ queue = new Map<unknown, () => void>()
150
153
  scheduled = false
154
+ run: Effect.Effect<Scope.Scope, never, void>
151
155
 
152
156
  constructor(
153
157
  readonly scope: Scope.Scope,
@@ -155,9 +159,11 @@ class RenderQueueImpl implements RenderQueue {
155
159
  readonly skipRenderScheduling: boolean = false
156
160
  ) {
157
161
  this.add.bind(this)
162
+
163
+ this.run = typeof requestAnimationFrame === "undefined" ? this.runIdle : this.runAnimationFrame
158
164
  }
159
165
 
160
- add(part: Part | SparsePart, task: () => void) {
166
+ add(part: unknown, task: () => void) {
161
167
  if (this.skipRenderScheduling) return Effect.sync(task)
162
168
 
163
169
  return Effect.suspend(() => {
@@ -190,26 +196,20 @@ class RenderQueueImpl implements RenderQueue {
190
196
  )
191
197
  })
192
198
 
193
- run: Effect.Effect<Scope.Scope, never, void> = Effect.suspend(() =>
194
- Effect.flatMap(
199
+ runIdle: Effect.Effect<Scope.Scope, never, void> = Effect.suspend(() => {
200
+ return Effect.flatMap(
195
201
  Idle.whenIdle(this.options),
196
202
  (deadline) =>
197
203
  Effect.suspend(() => {
198
204
  const iterator = this.queue.entries()
199
205
 
200
- while (Idle.shouldContinue(deadline)) {
201
- const result = iterator.next()
202
-
203
- if (result.done) break
204
- else {
205
- const [part, task] = result.value
206
- this.queue.delete(part)
207
- task()
208
- }
206
+ while (Idle.shouldContinue(deadline) && this.runTask(iterator)) {
207
+ // Continue
209
208
  }
210
209
 
210
+ // If we have more work to do, schedule another run
211
211
  if (this.queue.size > 0) {
212
- return this.run
212
+ return this.runIdle
213
213
  }
214
214
 
215
215
  this.scheduled = false
@@ -217,5 +217,33 @@ class RenderQueueImpl implements RenderQueue {
217
217
  return Effect.unit
218
218
  })
219
219
  )
220
+ })
221
+
222
+ runAnimationFrame: Effect.Effect<Scope.Scope, never, void> = Effect.zipRight(
223
+ Effect.asyncOption<never, never, void>((cb) => {
224
+ const id = requestAnimationFrame(() => cb(Effect.unit))
225
+ return Option.some(Effect.sync(() => cancelAnimationFrame(id)))
226
+ }),
227
+ Effect.sync(() => {
228
+ const iterator = this.queue.entries()
229
+
230
+ while (this.runTask(iterator)) {
231
+ // Continue
232
+ }
233
+
234
+ this.scheduled = false
235
+ })
220
236
  )
237
+
238
+ private runTask = (iterator: Iterator<[unknown, () => void]>) => {
239
+ const result = iterator.next()
240
+
241
+ if (result.done) return false
242
+ else {
243
+ const [part, task] = result.value
244
+ this.queue.delete(part)
245
+ task()
246
+ return true
247
+ }
248
+ }
221
249
  }