@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
@@ -1,45 +1,387 @@
1
1
  import * as Fx from "@typed/fx/Fx";
2
- import * as Subject from "@typed/fx/Subject";
3
2
  import { TypeId } from "@typed/fx/TypeId";
4
3
  import { persistent } from "@typed/wire";
5
4
  import { Effect } from "effect";
5
+ import * as Context from "effect/Context";
6
6
  import { replace } from "effect/ReadonlyArray";
7
+ import { Scope } from "effect/Scope";
7
8
  import { isDirective } from "../Directive.js";
8
9
  import * as ElementRef from "../ElementRef.js";
10
+ import * as ElementSource from "../ElementSource.js";
9
11
  import * as EventHandler from "../EventHandler.js";
10
12
  import { DomRenderEvent } from "../RenderEvent.js";
11
- import { TemplateInstance } from "../TemplateInstance.js";
12
13
  import { makeRenderNodePart } from "./browser.js";
14
+ import { makeEventSource } from "./EventSource.js";
13
15
  import { HydrateContext } from "./HydrateContext.js";
14
- import { indexRefCounter } from "./indexRefCounter.js";
16
+ import { indexRefCounter2 } from "./indexRefCounter.js";
15
17
  import { parse } from "./parser.js";
16
- import { AttributePartImpl, BooleanPartImpl, ClassNamePartImpl, CommentPartImpl, DataPartImpl, EventPartImpl, PropertyPartImpl, RefPartImpl, SparseAttributePartImpl, SparseClassNamePartImpl, SparseCommentPartImpl, StaticTextImpl, TextPartImpl } from "./parts.js";
17
- import { findPath } from "./utils.js";
18
+ import { AttributePartImpl, BooleanPartImpl, ClassNamePartImpl, CommentPartImpl, DataPartImpl, EventPartImpl, PropertiesPartImpl, PropertyPartImpl, RefPartImpl, SparseAttributePartImpl, SparseClassNamePartImpl, SparseCommentPartImpl, StaticTextImpl, TextPartImpl } from "./parts.js";
19
+ import { findHoleComment, findPath } from "./utils.js";
20
+ const RenderPartMap = {
21
+ "attr": (templatePart, node, ctx) => {
22
+ const { document, refCounter, renderContext, values } = ctx;
23
+ const element = node;
24
+ const attr = createAttribute(document, element, templatePart.name);
25
+ const renderable = values[templatePart.index];
26
+ let isSet = true;
27
+ const setValue = (value) => {
28
+ if (isNullOrUndefined(value)) {
29
+ element.removeAttribute(templatePart.name);
30
+ isSet = false;
31
+ }
32
+ else {
33
+ attr.value = String(value);
34
+ if (isSet === false) {
35
+ element.setAttributeNode(attr);
36
+ isSet = true;
37
+ }
38
+ }
39
+ };
40
+ return matchSettablePart(renderable, setValue, () => AttributePartImpl.browser(templatePart.index, element, templatePart.name, renderContext), (f) => Effect.zipRight(renderContext.queue.add(element, f), refCounter.release(templatePart.index)), () => ctx.expected++);
41
+ },
42
+ "boolean-part": (templatePart, node, ctx) => {
43
+ const { refCounter, renderContext, values } = ctx;
44
+ const element = node;
45
+ const renderable = values[templatePart.index];
46
+ const setValue = (value) => {
47
+ element.toggleAttribute(templatePart.name, isNullOrUndefined(value) ? false : Boolean(value));
48
+ };
49
+ return matchSettablePart(renderable, setValue, () => BooleanPartImpl.browser(templatePart.index, element, templatePart.name, renderContext), (f) => Effect.zipRight(renderContext.queue.add(element, f), refCounter.release(templatePart.index)), () => ctx.expected++);
50
+ },
51
+ "className-part": (templatePart, node, ctx) => {
52
+ const { refCounter, renderContext, values } = ctx;
53
+ const element = node;
54
+ const renderable = values[templatePart.index];
55
+ let classNames = new Set();
56
+ const setValue = (value) => {
57
+ if (isNullOrUndefined(value)) {
58
+ element.classList.remove(...classNames);
59
+ classNames.clear();
60
+ }
61
+ else {
62
+ const newClassNames = new Set(Array.isArray(value) ? value : [String(value)]);
63
+ const { added, removed } = diffClassNames(classNames, newClassNames);
64
+ if (removed.length > 0) {
65
+ element.classList.remove(...removed);
66
+ }
67
+ if (added.length > 0)
68
+ element.classList.add(...added);
69
+ classNames = newClassNames;
70
+ }
71
+ };
72
+ return matchSettablePart(renderable, setValue, () => ClassNamePartImpl.browser(templatePart.index, element, renderContext), (f) => Effect.zipRight(renderContext.queue.add(element, f), refCounter.release(templatePart.index)), () => ctx.expected++);
73
+ },
74
+ "comment-part": (templatePart, node, ctx) => {
75
+ const { refCounter, renderContext, values } = ctx;
76
+ const comment = findHoleComment(node, templatePart.index);
77
+ const renderable = values[templatePart.index];
78
+ const setValue = (value) => {
79
+ comment.textContent = isNullOrUndefined(value) ? "" : String(value);
80
+ };
81
+ return matchSettablePart(renderable, setValue, () => CommentPartImpl.browser(templatePart.index, comment, renderContext), (f) => Effect.zipRight(renderContext.queue.add(comment, f), refCounter.release(templatePart.index)), () => ctx.expected++);
82
+ },
83
+ "data": (templatePart, node, ctx) => {
84
+ const element = node;
85
+ const renderable = ctx.values[templatePart.index];
86
+ const previousKeys = new Set(Object.keys(element.dataset));
87
+ const setValue = (value) => {
88
+ if (isNullOrUndefined(value)) {
89
+ for (const key of previousKeys) {
90
+ delete element.dataset[key];
91
+ }
92
+ previousKeys.clear();
93
+ }
94
+ else {
95
+ for (const key of previousKeys) {
96
+ if (!(key in value)) {
97
+ delete element.dataset[key];
98
+ previousKeys.delete(key);
99
+ }
100
+ }
101
+ for (const key of Object.keys(value)) {
102
+ if (!previousKeys.has(key)) {
103
+ previousKeys.add(key);
104
+ }
105
+ element.dataset[key] = value[key] || "";
106
+ }
107
+ }
108
+ };
109
+ return matchSettablePart(renderable, setValue, () => DataPartImpl.browser(templatePart.index, element, ctx.renderContext), (f) => Effect.zipRight(ctx.renderContext.queue.add(element, f), ctx.refCounter.release(templatePart.index)), () => ctx.expected++);
110
+ },
111
+ "event": (templatePart, node, ctx) => {
112
+ const element = node;
113
+ const renderable = ctx.values[templatePart.index];
114
+ const handler = getEventHandler(renderable, ctx.context, ctx.onCause);
115
+ if (handler) {
116
+ ctx.eventSource.addEventListener(element, templatePart.name, handler);
117
+ }
118
+ return null;
119
+ },
120
+ "node": (templatePart, node, ctx) => {
121
+ const part = makeRenderNodePart(templatePart.index, node, ctx.renderContext, ctx.document, false);
122
+ ctx.expected++;
123
+ return handlePart(ctx.values[templatePart.index], (value) => Effect.zipRight(part.update(value), ctx.refCounter.release(templatePart.index)));
124
+ },
125
+ "property": (templatePart, node, ctx) => {
126
+ const element = node;
127
+ const renderable = ctx.values[templatePart.index];
128
+ const setValue = (value) => {
129
+ if (isNullOrUndefined(value)) {
130
+ delete element[templatePart.name];
131
+ }
132
+ else {
133
+ ;
134
+ element[templatePart.name] = value;
135
+ }
136
+ };
137
+ return matchSettablePart(renderable, setValue, () => PropertyPartImpl.browser(templatePart.index, element, templatePart.name, ctx.renderContext), (f) => Effect.zipRight(ctx.renderContext.queue.add(element, f), ctx.refCounter.release(templatePart.index)), () => ctx.expected++);
138
+ },
139
+ "properties": (templatePart, node, ctx) => {
140
+ const renderable = ctx.values[templatePart.index];
141
+ if (isNullOrUndefined(renderable))
142
+ return null;
143
+ else if (Fx.isFx(renderable) || Effect.isEffect(renderable)) {
144
+ throw new Error(`Properties Part must utilize an Record of renderable values.`);
145
+ }
146
+ else if (typeof renderable === "object" && !Array.isArray(renderable)) {
147
+ const element = node;
148
+ const toggleBoolean = (key, value) => {
149
+ element.toggleAttribute(key, isNullOrUndefined(value) ? false : Boolean(value));
150
+ };
151
+ const setAttribute = (key, value) => {
152
+ if (isNullOrUndefined(value)) {
153
+ element.removeAttribute(key);
154
+ }
155
+ else {
156
+ element.setAttribute(key, String(value));
157
+ }
158
+ };
159
+ const setProperty = (key, value) => {
160
+ if (isNullOrUndefined(value)) {
161
+ delete element[key];
162
+ }
163
+ else {
164
+ ;
165
+ element[key] = value;
166
+ }
167
+ };
168
+ const effects = [];
169
+ // We need indexes to track async values that won't conflict
170
+ // with any other Parts, we can start end of the current values.length
171
+ // As there should only ever be exactly 1 properties part.
172
+ let i = ctx.values.length;
173
+ loop: for (const [key, value] of Object.entries(renderable)) {
174
+ const index = ++i;
175
+ switch (key[0]) {
176
+ case "?": {
177
+ const name = key.slice(1);
178
+ const eff = matchSettablePart(value, (value) => toggleBoolean(name, value), () => BooleanPartImpl.browser(index, element, name, ctx.renderContext), (f) => Effect.zipRight(ctx.renderContext.queue.add(element, f), ctx.refCounter.release(index)), () => ctx.expected++);
179
+ if (eff !== null) {
180
+ effects.push(eff);
181
+ }
182
+ continue loop;
183
+ }
184
+ case ".": {
185
+ const name = key.slice(1);
186
+ const eff = matchSettablePart(value, (value) => setProperty(name, value), () => PropertyPartImpl.browser(index, element, name, ctx.renderContext), (f) => Effect.zipRight(ctx.renderContext.queue.add(element, f), ctx.refCounter.release(index)), () => ctx.expected++);
187
+ if (eff !== null) {
188
+ effects.push(eff);
189
+ }
190
+ continue loop;
191
+ }
192
+ case "@": {
193
+ const name = key.slice(1);
194
+ const handler = getEventHandler(value, ctx.context, ctx.onCause);
195
+ if (handler) {
196
+ ctx.eventSource.addEventListener(element, name, handler);
197
+ }
198
+ continue loop;
199
+ }
200
+ case "o": {
201
+ if (key[1] === "n") {
202
+ const name = key.slice(2);
203
+ const handler = getEventHandler(value, ctx.context, ctx.onCause);
204
+ if (handler) {
205
+ ctx.eventSource.addEventListener(element, name, handler);
206
+ }
207
+ }
208
+ continue loop;
209
+ }
210
+ }
211
+ const eff = matchSettablePart(value, (value) => setAttribute(key, value), () => AttributePartImpl.browser(index, element, key, ctx.renderContext), (f) => Effect.zipRight(ctx.renderContext.queue.add(element, f), ctx.refCounter.release(index)), () => ctx.expected++);
212
+ if (eff !== null) {
213
+ effects.push(eff);
214
+ }
215
+ }
216
+ return effects;
217
+ }
218
+ else {
219
+ return null;
220
+ }
221
+ },
222
+ "ref": (templatePart, node, ctx) => {
223
+ const element = node;
224
+ const renderable = ctx.values[templatePart.index];
225
+ if (isDirective(renderable)) {
226
+ return renderable(new RefPartImpl(ElementSource.fromElement(element), templatePart.index));
227
+ }
228
+ else if (ElementRef.isElementRef(renderable)) {
229
+ return ElementRef.set(renderable, element);
230
+ }
231
+ return null;
232
+ },
233
+ "sparse-attr": (templatePart, node, ctx) => {
234
+ const values = Array.from({ length: templatePart.nodes.length }, () => "");
235
+ const element = node;
236
+ const attr = createAttribute(ctx.document, element, templatePart.name);
237
+ const setValue = (value, index) => Effect.suspend(() => {
238
+ values[index] = value || "";
239
+ return ctx.renderContext.queue.add(element, () => attr.value = values.join(""));
240
+ });
241
+ const effects = [];
242
+ for (let i = 0; i < templatePart.nodes.length; ++i) {
243
+ const node = templatePart.nodes[i];
244
+ if (node._tag === "text") {
245
+ values[i] = node.value;
246
+ }
247
+ else {
248
+ const renderable = ctx.values[node.index];
249
+ const index = i;
250
+ const effect = matchSettablePart(renderable, (value) => setValue(value, index), () => new AttributePartImpl(templatePart.name, node.index, ({ value }) => setValue(value, index), null), (f) => Effect.zipRight(ctx.renderContext.queue.add(element, f), ctx.refCounter.release(node.index)), () => ctx.expected++);
251
+ if (effect !== null) {
252
+ effects.push(effect);
253
+ }
254
+ }
255
+ }
256
+ return effects;
257
+ },
258
+ "sparse-class-name": (templatePart, node, ctx) => {
259
+ const element = node;
260
+ const effects = templatePart.nodes.flatMap((node) => {
261
+ if (node._tag === "text") {
262
+ const split = splitClassNames(node.value);
263
+ if (split.length > 0)
264
+ element.classList.add(...split);
265
+ return [];
266
+ }
267
+ else {
268
+ const eff = RenderPartMap[node._tag](node, element, ctx);
269
+ if (eff === null)
270
+ return [];
271
+ return Array.isArray(eff) ? eff : [eff];
272
+ }
273
+ });
274
+ return effects;
275
+ },
276
+ "sparse-comment": (templatePart, node, ctx) => {
277
+ const values = Array.from({ length: templatePart.nodes.length }, () => "");
278
+ const comment = node;
279
+ const setValue = (value, index) => Effect.suspend(() => {
280
+ values[index] = value || "";
281
+ return ctx.renderContext.queue.add(comment, () => comment.textContent = values.join(""));
282
+ });
283
+ const effects = [];
284
+ for (let i = 0; i < templatePart.nodes.length; ++i) {
285
+ const node = templatePart.nodes[i];
286
+ if (node._tag === "text") {
287
+ values[i] = node.value;
288
+ }
289
+ else {
290
+ const renderable = ctx.values[node.index];
291
+ const index = i;
292
+ const effect = matchSettablePart(renderable, (value) => setValue(value, index), () => new CommentPartImpl(node.index, ({ value }) => setValue(value, index), null), (f) => Effect.zipRight(ctx.renderContext.queue.add(comment, f), ctx.refCounter.release(node.index)), () => ctx.expected++);
293
+ if (effect !== null) {
294
+ effects.push(effect);
295
+ }
296
+ }
297
+ }
298
+ return effects;
299
+ },
300
+ "text-part": (templatePart, node, ctx) => {
301
+ const part = TextPartImpl.browser(ctx.document, templatePart.index, node, ctx.renderContext);
302
+ ctx.expected++;
303
+ return handlePart(ctx.values[templatePart.index], (value) => Effect.zipRight(part.update(value), ctx.refCounter.release(templatePart.index)));
304
+ }
305
+ };
306
+ const SPACE_REGEXP = /\s+/g;
307
+ function splitClassNames(value) {
308
+ return value.split(SPACE_REGEXP).flatMap((a) => {
309
+ const trimmed = a.trim();
310
+ return trimmed.length > 0 ? [trimmed] : [];
311
+ });
312
+ }
313
+ function isNullOrUndefined(value) {
314
+ return value === null || value === undefined;
315
+ }
316
+ function diffClassNames(oldClassNames, newClassNames) {
317
+ const added = [];
318
+ const removed = [];
319
+ for (const className of oldClassNames) {
320
+ if (!newClassNames.has(className)) {
321
+ removed.push(className);
322
+ }
323
+ }
324
+ for (const className of newClassNames) {
325
+ if (!oldClassNames.has(className) && className.trim()) {
326
+ added.push(className);
327
+ }
328
+ }
329
+ return { added, removed };
330
+ }
18
331
  /**
19
332
  * Here for "standard" browser rendering, a TemplateInstance is effectively a live
20
333
  * view into the contents rendered by the Template.
21
334
  */
22
- export const renderTemplate = (document, ctx) => (templateStrings, values, providedRef) => Effect.gen(function* (_) {
23
- const elementRef = providedRef || (yield* _(ElementRef.make()));
24
- const events = Fx.map(elementRef, DomRenderEvent);
25
- const errors = Subject.make();
26
- const entry = getBrowserEntry(document, ctx, templateStrings);
27
- const content = document.importNode(entry.content, true); // Clone our template
28
- const parts = yield* _(buildParts(document, ctx, entry.template, content, elementRef, errors.onFailure, false)); // Build runtime-variant of parts with our content.
29
- // If there are parts we need to render them before constructing our Wire
30
- if (parts.length > 0) {
31
- const refCounter = yield* _(indexRefCounter(parts.length));
32
- // Do the work
33
- yield* _(renderValues(values, parts, refCounter, errors.onFailure));
34
- // Wait for initial work to be completed
35
- yield* _(refCounter.wait);
335
+ export const renderTemplate = (document, renderContext) => (templateStrings, values) => {
336
+ const entry = getBrowserEntry(document, renderContext, templateStrings);
337
+ if (values.length === 0) {
338
+ return Fx.sync(() => DomRenderEvent(persistent(document.importNode(entry.content, true))));
36
339
  }
37
- // Set the element when it is ready
38
- yield* _(ElementRef.set(elementRef, persistent(content)));
39
- // Return the Template instance
40
- return TemplateInstance(Fx.merge([events, errors]), elementRef);
41
- });
42
- export function renderValues(values, parts, refCounter, onCause, makeHydrateContext) {
340
+ return Fx.make((sink) => {
341
+ return Effect.gen(function* (_) {
342
+ const content = document.importNode(entry.content, true);
343
+ const context = yield* _(Effect.context());
344
+ const refCounter = yield* _(indexRefCounter2());
345
+ const ctx = {
346
+ context,
347
+ document,
348
+ eventSource: makeEventSource(),
349
+ expected: 0,
350
+ refCounter,
351
+ renderContext,
352
+ onCause: sink.onFailure,
353
+ values
354
+ };
355
+ // Connect our interpolated values to our template parts
356
+ const effects = [];
357
+ for (const [part, path] of entry.template.parts) {
358
+ const eff = RenderPartMap[part._tag](part, findPath(content, path), ctx);
359
+ if (eff !== null) {
360
+ effects.push(...(Array.isArray(eff) ? eff : [eff]));
361
+ }
362
+ }
363
+ // Fork any effects necessary
364
+ if (effects.length > 0) {
365
+ yield* _(Effect.forkAll(effects));
366
+ }
367
+ // If there's anything to wait on and it's not already done, wait for an initial value
368
+ // for all asynchronous sources.
369
+ if (ctx.expected > 0 && (yield* _(refCounter.expect(ctx.expected)))) {
370
+ yield* _(refCounter.wait);
371
+ }
372
+ // Create a persistent wire from our content
373
+ const wire = persistent(content);
374
+ // Set the element when it is ready
375
+ yield* _(ctx.eventSource.setup(wire, Context.get(context, Scope)));
376
+ // Emity our DomRenderEvent
377
+ yield* _(sink.onSuccess(DomRenderEvent(wire)));
378
+ // Ensure our templates last forever in the DOM environment
379
+ // so event listeners are kept attached to the current Scope.
380
+ yield* _(Effect.never);
381
+ });
382
+ });
383
+ };
384
+ export function renderValues(values, parts, refCounter, ctx, makeHydrateContext) {
43
385
  return Effect.all(parts.map((part, index) => {
44
386
  switch (part._tag) {
45
387
  case "sparse/attribute":
@@ -48,7 +390,7 @@ export function renderValues(values, parts, refCounter, onCause, makeHydrateCont
48
390
  return renderSparsePart(values, part, refCounter);
49
391
  }
50
392
  default:
51
- return renderPart(values, part, refCounter, onCause, makeHydrateContext ? () => makeHydrateContext(index) : undefined);
393
+ return renderPart(values, part, refCounter, ctx, makeHydrateContext ? () => makeHydrateContext(index) : undefined);
52
394
  }
53
395
  }));
54
396
  }
@@ -56,37 +398,47 @@ export function renderSparsePart(values, part, refCounter) {
56
398
  const indexes = part.parts.flatMap((p) => p._tag === "static/text" ? [] : [p.index]);
57
399
  return Effect.forkScoped(Fx.observe(unwrapSparsePartRenderables(part.parts.map((p) => p._tag === "static/text" ? Fx.succeed(p.value) : values[p.index]), part), (value) => Effect.tap(part.update(value), () => Effect.forEach(indexes, (a) => refCounter.release(a)))));
58
400
  }
59
- export function renderPart(values, part, refCounter, onCause, hydrateCtx) {
401
+ export function renderPart(values, part, refCounter, ctx, hydrateCtx) {
60
402
  const partIndex = part.index;
61
403
  const renderable = values[partIndex];
404
+ if (renderable === null || renderable === undefined)
405
+ return refCounter.release(partIndex);
62
406
  if (isDirective(renderable)) {
63
- return renderable(part).pipe(Effect.tap(() => refCounter.release(partIndex)), Effect.forkScoped);
407
+ return renderable(part).pipe(Effect.flatMap(() => refCounter.release(partIndex)), Effect.forkScoped);
64
408
  }
65
409
  else if (part._tag === "ref") {
66
410
  return refCounter.release(partIndex);
67
411
  }
68
412
  else if (part._tag === "event") {
69
- return Effect.tap(part.update(getEventHandler(values[partIndex], onCause)), () => refCounter.release(partIndex));
413
+ const handler = getEventHandler(renderable, ctx, part.onCause);
414
+ if (handler) {
415
+ part.addEventListener(handler);
416
+ }
417
+ return refCounter.release(partIndex);
70
418
  }
71
419
  else if (part._tag === "node" && hydrateCtx) {
72
- if (renderable === null || renderable === undefined)
73
- return refCounter.release(partIndex);
74
- return handlePart(values[partIndex], (value) => Effect.tap(part.update(value), () => refCounter.release(partIndex))).pipe(HydrateContext.provide(hydrateCtx()), Effect.forkScoped);
420
+ return handlePart(renderable, (value) => Effect.flatMap(part.update(value), () => refCounter.release(partIndex))).pipe(HydrateContext.provide(hydrateCtx()), Effect.forkScoped);
421
+ }
422
+ else if (part._tag === "properties") {
423
+ return handlePropertiesPart(renderable, part, refCounter);
75
424
  }
76
425
  else {
77
- const renderable = values[partIndex];
78
- if (renderable === null || renderable === undefined)
79
- return refCounter.release(partIndex);
80
- return handlePart(values[partIndex], (value) => Effect.tap(part.update(value), () => refCounter.release(partIndex)));
426
+ return handlePart(renderable, (value) => Effect.flatMap(part.update(value), () => refCounter.release(partIndex)));
81
427
  }
82
428
  }
83
- function getEventHandler(renderable, onCause) {
429
+ function handlePropertiesPart(renderable, part, refCounter) {
430
+ if (renderable && typeof renderable === "object") {
431
+ return handlePart(Fx.struct(Object.fromEntries(Object.entries(renderable).map(([k, v]) => [k, unwrapRenderable(v)]))), (value) => Effect.tap(part.update(value), () => refCounter.release(part.index)));
432
+ }
433
+ return Effect.succeed(void 0);
434
+ }
435
+ function getEventHandler(renderable, ctx, onCause) {
84
436
  if (renderable && typeof renderable === "object") {
85
437
  if (EventHandler.EventHandlerTypeId in renderable) {
86
- return EventHandler.make((ev) => Effect.catchAllCause(renderable.handler(ev), onCause), renderable.options);
438
+ return EventHandler.make((ev) => Effect.provide(Effect.catchAllCause(renderable.handler(ev), onCause), ctx), renderable.options);
87
439
  }
88
440
  else if (Effect.EffectTypeId in renderable) {
89
- return EventHandler.make(() => Effect.catchAllCause(renderable, onCause));
441
+ return EventHandler.make(() => Effect.provide(Effect.catchAllCause(renderable, onCause), ctx));
90
442
  }
91
443
  }
92
444
  return null;
@@ -100,7 +452,7 @@ function handlePart(renderable, update) {
100
452
  else if (Array.isArray(renderable)) {
101
453
  return renderable.length === 0
102
454
  ? update(null)
103
- : Effect.forkScoped(Fx.observe(Fx.combine(renderable.map(unwrapRenderable)), update));
455
+ : Effect.forkScoped(Fx.observe(Fx.tuple(renderable.map(unwrapRenderable)), update));
104
456
  }
105
457
  else if (TypeId in renderable) {
106
458
  return Effect.forkScoped(Fx.observe(renderable, update));
@@ -122,7 +474,7 @@ function unwrapRenderable(renderable) {
122
474
  if (renderable === null || renderable === undefined)
123
475
  return Fx.succeed(null);
124
476
  else if (Array.isArray(renderable)) {
125
- return renderable.length === 0 ? Fx.succeed(null) : Fx.combine(renderable.map(unwrapRenderable));
477
+ return renderable.length === 0 ? Fx.succeed(null) : Fx.tuple(renderable.map(unwrapRenderable));
126
478
  }
127
479
  else if (TypeId in renderable) {
128
480
  return renderable;
@@ -138,7 +490,7 @@ function unwrapRenderable(renderable) {
138
490
  }
139
491
  }
140
492
  function unwrapSparsePartRenderables(renderables, part) {
141
- return Fx.combine(
493
+ return Fx.tuple(
142
494
  // @ts-ignore type too deep
143
495
  renderables.map((renderable, i) => {
144
496
  const p = part.parts[i];
@@ -196,29 +548,31 @@ export function getBrowserEntry(document, ctx, templateStrings) {
196
548
  return cached;
197
549
  }
198
550
  }
199
- export function buildParts(document, ctx, template, content, ref, onCause, isHydrating) {
200
- return Effect.all(template.parts.map(([part, path]) => buildPartWithNode(document, ctx, part, findPath(content, path), ref, onCause, isHydrating)));
551
+ export function buildParts(document, ctx, template, content, eventSource, onCause, isHydrating) {
552
+ return template.parts.map(([part, path]) => buildPartWithNode(document, ctx, part, findPath(content, path), eventSource, onCause, isHydrating));
201
553
  }
202
- function buildPartWithNode(document, ctx, part, node, ref, onCause, isHydrating) {
554
+ function buildPartWithNode(document, ctx, part, node, eventSource, onCause, isHydrating) {
203
555
  switch (part._tag) {
204
556
  case "attr":
205
- return Effect.succeed(AttributePartImpl.browser(part.index, node, part.name, ctx));
557
+ return AttributePartImpl.browser(part.index, node, part.name, ctx);
206
558
  case "boolean-part":
207
- return Effect.succeed(BooleanPartImpl.browser(part.index, node, part.name, ctx));
559
+ return BooleanPartImpl.browser(part.index, node, part.name, ctx);
208
560
  case "className-part":
209
- return Effect.succeed(ClassNamePartImpl.browser(part.index, node, ctx));
561
+ return ClassNamePartImpl.browser(part.index, node, ctx);
210
562
  case "comment-part":
211
- return Effect.succeed(CommentPartImpl.browser(part.index, node, ctx));
563
+ return CommentPartImpl.browser(part.index, node, ctx);
212
564
  case "data":
213
- return Effect.succeed(DataPartImpl.browser(part.index, node, ctx));
565
+ return DataPartImpl.browser(part.index, node, ctx);
214
566
  case "event":
215
- return EventPartImpl.browser(part.name, part.index, ref, node, onCause);
567
+ return new EventPartImpl(part.name, part.index, ElementSource.fromElement(node), onCause, (handler) => eventSource.addEventListener(node, part.name, handler));
216
568
  case "node":
217
- return Effect.succeed(makeRenderNodePart(part.index, node, ctx, document, isHydrating));
569
+ return makeRenderNodePart(part.index, node, ctx, document, isHydrating);
218
570
  case "property":
219
- return Effect.succeed(PropertyPartImpl.browser(part.index, node, part.name, ctx));
571
+ return PropertyPartImpl.browser(part.index, node, part.name, ctx);
572
+ case "properties":
573
+ return PropertiesPartImpl.browser(part.index, node, ctx);
220
574
  case "ref":
221
- return Effect.succeed(new RefPartImpl(ref.query(node), part.index));
575
+ return new RefPartImpl(ElementSource.fromElement(node), part.index);
222
576
  case "sparse-attr": {
223
577
  const parts = Array(part.nodes.length);
224
578
  const sparse = SparseAttributePartImpl.browser(part.name, parts, node, ctx);
@@ -232,7 +586,7 @@ function buildPartWithNode(document, ctx, part, node, ref, onCause, isHydrating)
232
586
  parts.push(new AttributePartImpl(node.name, node.index, ({ value }) => sparse.update(replace(sparse.value, i, value || "")), sparse.value[i]));
233
587
  }
234
588
  }
235
- return Effect.succeed(sparse);
589
+ return sparse;
236
590
  }
237
591
  case "sparse-class-name": {
238
592
  const parts = [];
@@ -249,7 +603,7 @@ function buildPartWithNode(document, ctx, part, node, ref, onCause, isHydrating)
249
603
  parts.push(new ClassNamePartImpl(node.index, ({ value }) => sparse.update(replace(sparse.value, i, value || "")), []));
250
604
  }
251
605
  }
252
- return Effect.succeed(sparse);
606
+ return sparse;
253
607
  }
254
608
  case "sparse-comment": {
255
609
  const parts = Array(part.nodes.length);
@@ -264,10 +618,10 @@ function buildPartWithNode(document, ctx, part, node, ref, onCause, isHydrating)
264
618
  parts.push(new CommentPartImpl(node.index, ({ value }) => sparse.update(replace(sparse.value, i, value || "")), sparse.value[i]));
265
619
  }
266
620
  }
267
- return Effect.succeed(sparse);
621
+ return sparse;
268
622
  }
269
623
  case "text-part":
270
- return Effect.succeed(TextPartImpl.browser(document, part.index, node, ctx));
624
+ return TextPartImpl.browser(document, part.index, node, ctx);
271
625
  }
272
626
  }
273
627
  export function buildTemplate(document, { nodes }) {
@@ -293,6 +647,33 @@ function buildNode(document, node, isSvgContext) {
293
647
  case "comment-part":
294
648
  case "node":
295
649
  return document.createComment(`hole${node.index}`);
650
+ case "doctype":
651
+ return document.implementation.createDocumentType(node.name, docTypeNameToPublicId(node.name), docTypeNameToSystemId(node.name));
652
+ }
653
+ }
654
+ function docTypeNameToPublicId(name) {
655
+ switch (name) {
656
+ case "html":
657
+ return "-//W3C//DTD HTML 4.01//EN";
658
+ case "svg":
659
+ return "-//W3C//DTD SVG 1.1//EN";
660
+ case "math":
661
+ return "-//W3C//DTD MathML 2.0//EN";
662
+ default:
663
+ return "";
664
+ }
665
+ }
666
+ function docTypeNameToSystemId(name) {
667
+ switch (name) {
668
+ // HTML5
669
+ case "html":
670
+ return "http://www.w3.org/TR/html4/strict.dtd";
671
+ case "svg":
672
+ return "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";
673
+ case "math":
674
+ return "http://www.w3.org/Math/DTD/mathml2/mathml2.dtd";
675
+ default:
676
+ return "";
296
677
  }
297
678
  }
298
679
  const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
@@ -326,4 +707,41 @@ function buildTextChild(document, node) {
326
707
  }
327
708
  return document.createComment(`hole${node.index}`);
328
709
  }
710
+ function createAttribute(document, element, name) {
711
+ return element.getAttributeNode(name) ?? document.createAttribute(name);
712
+ }
713
+ function matchSettablePart(renderable, setValue, makePart, schedule, expect) {
714
+ return matchRenderable(renderable, {
715
+ Fx: (fx) => {
716
+ expect();
717
+ return Fx.observe(fx, (a) => schedule(() => setValue(a)));
718
+ },
719
+ Effect: (effect) => {
720
+ expect();
721
+ return Effect.flatMap(effect, (a) => schedule(() => setValue(a)));
722
+ },
723
+ Directive: (directive) => {
724
+ expect();
725
+ return directive(makePart());
726
+ },
727
+ Otherwise: (otherwise) => {
728
+ setValue(otherwise);
729
+ return null;
730
+ }
731
+ });
732
+ }
733
+ function matchRenderable(renderable, matches) {
734
+ if (Fx.isFx(renderable)) {
735
+ return matches.Fx(renderable);
736
+ }
737
+ else if (Effect.isEffect(renderable)) {
738
+ return matches.Effect(renderable);
739
+ }
740
+ else if (isDirective(renderable)) {
741
+ return matches.Directive(renderable);
742
+ }
743
+ else {
744
+ return matches.Otherwise(renderable);
745
+ }
746
+ }
329
747
  //# sourceMappingURL=render.js.map