@typed/template 0.2.0 → 0.3.1

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 (227) 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/chunks.js +4 -1
  42. package/dist/cjs/internal/chunks.js.map +1 -1
  43. package/dist/cjs/internal/errors.js +4 -0
  44. package/dist/cjs/internal/errors.js.map +1 -1
  45. package/dist/cjs/internal/hydrate.js +113 -80
  46. package/dist/cjs/internal/hydrate.js.map +1 -1
  47. package/dist/cjs/internal/indexRefCounter.js +49 -2
  48. package/dist/cjs/internal/indexRefCounter.js.map +1 -1
  49. package/dist/cjs/internal/parser.js +72 -21
  50. package/dist/cjs/internal/parser.js.map +1 -1
  51. package/dist/cjs/internal/parts.js +128 -28
  52. package/dist/cjs/internal/parts.js.map +1 -1
  53. package/dist/cjs/internal/render.js +460 -161
  54. package/dist/cjs/internal/render.js.map +1 -1
  55. package/dist/cjs/internal/server.js +5 -2
  56. package/dist/cjs/internal/server.js.map +1 -1
  57. package/dist/dts/Directive.d.ts.map +1 -1
  58. package/dist/dts/ElementRef.d.ts +4 -2
  59. package/dist/dts/ElementRef.d.ts.map +1 -1
  60. package/dist/dts/ElementSource.d.ts +10 -5
  61. package/dist/dts/ElementSource.d.ts.map +1 -1
  62. package/dist/dts/EventHandler.d.ts.map +1 -1
  63. package/dist/dts/Html.d.ts +1 -1
  64. package/dist/dts/Html.d.ts.map +1 -1
  65. package/dist/dts/HtmlChunk.d.ts.map +1 -1
  66. package/dist/dts/Many.d.ts +13 -11
  67. package/dist/dts/Many.d.ts.map +1 -1
  68. package/dist/dts/Parser.d.ts +3 -6
  69. package/dist/dts/Parser.d.ts.map +1 -1
  70. package/dist/dts/Part.d.ts +13 -3
  71. package/dist/dts/Part.d.ts.map +1 -1
  72. package/dist/dts/Placeholder.d.ts +2 -2
  73. package/dist/dts/Placeholder.d.ts.map +1 -1
  74. package/dist/dts/Render.d.ts +2 -1
  75. package/dist/dts/Render.d.ts.map +1 -1
  76. package/dist/dts/RenderContext.d.ts +5 -4
  77. package/dist/dts/RenderContext.d.ts.map +1 -1
  78. package/dist/dts/RenderTemplate.d.ts +2 -16
  79. package/dist/dts/RenderTemplate.d.ts.map +1 -1
  80. package/dist/dts/Renderable.d.ts +2 -2
  81. package/dist/dts/Renderable.d.ts.map +1 -1
  82. package/dist/dts/Template.d.ts +21 -3
  83. package/dist/dts/Template.d.ts.map +1 -1
  84. package/dist/dts/TemplateInstance.d.ts +3 -2
  85. package/dist/dts/TemplateInstance.d.ts.map +1 -1
  86. package/dist/dts/Test.d.ts +4 -6
  87. package/dist/dts/Test.d.ts.map +1 -1
  88. package/dist/dts/index.d.ts +0 -4
  89. package/dist/dts/index.d.ts.map +1 -1
  90. package/dist/dts/internal/EventSource.d.ts +12 -0
  91. package/dist/dts/internal/EventSource.d.ts.map +1 -0
  92. package/dist/dts/internal/browser.d.ts.map +1 -1
  93. package/dist/dts/internal/chunks.d.ts +1 -0
  94. package/dist/dts/internal/chunks.d.ts.map +1 -1
  95. package/dist/dts/internal/errors.d.ts +1 -0
  96. package/dist/dts/internal/errors.d.ts.map +1 -1
  97. package/dist/dts/internal/hydrate.d.ts +9 -16
  98. package/dist/dts/internal/hydrate.d.ts.map +1 -1
  99. package/dist/dts/internal/indexRefCounter.d.ts +5 -0
  100. package/dist/dts/internal/indexRefCounter.d.ts.map +1 -1
  101. package/dist/dts/internal/module-augmentation.d.ts +0 -4
  102. package/dist/dts/internal/module-augmentation.d.ts.map +1 -1
  103. package/dist/dts/internal/parser.d.ts +8 -0
  104. package/dist/dts/internal/parser.d.ts.map +1 -1
  105. package/dist/dts/internal/parts.d.ts +66 -56
  106. package/dist/dts/internal/parts.d.ts.map +1 -1
  107. package/dist/dts/internal/render.d.ts +1 -15
  108. package/dist/dts/internal/render.d.ts.map +1 -1
  109. package/dist/dts/internal/server.d.ts.map +1 -1
  110. package/dist/esm/Directive.js +1 -1
  111. package/dist/esm/Directive.js.map +1 -1
  112. package/dist/esm/ElementRef.js +12 -7
  113. package/dist/esm/ElementRef.js.map +1 -1
  114. package/dist/esm/ElementSource.js +16 -13
  115. package/dist/esm/ElementSource.js.map +1 -1
  116. package/dist/esm/EventHandler.js +1 -1
  117. package/dist/esm/EventHandler.js.map +1 -1
  118. package/dist/esm/Html.js +29 -24
  119. package/dist/esm/Html.js.map +1 -1
  120. package/dist/esm/HtmlChunk.js +6 -1
  121. package/dist/esm/HtmlChunk.js.map +1 -1
  122. package/dist/esm/Hydrate.js +1 -1
  123. package/dist/esm/Hydrate.js.map +1 -1
  124. package/dist/esm/Many.js +14 -10
  125. package/dist/esm/Many.js.map +1 -1
  126. package/dist/esm/Parser.js +6 -335
  127. package/dist/esm/Parser.js.map +1 -1
  128. package/dist/esm/Placeholder.js +2 -2
  129. package/dist/esm/Placeholder.js.map +1 -1
  130. package/dist/esm/Platform.js +2 -2
  131. package/dist/esm/Platform.js.map +1 -1
  132. package/dist/esm/Render.js +1 -1
  133. package/dist/esm/Render.js.map +1 -1
  134. package/dist/esm/RenderContext.js +38 -17
  135. package/dist/esm/RenderContext.js.map +1 -1
  136. package/dist/esm/RenderTemplate.js +2 -12
  137. package/dist/esm/RenderTemplate.js.map +1 -1
  138. package/dist/esm/Template.js +24 -0
  139. package/dist/esm/Template.js.map +1 -1
  140. package/dist/esm/TemplateInstance.js +2 -2
  141. package/dist/esm/TemplateInstance.js.map +1 -1
  142. package/dist/esm/Test.js +20 -7
  143. package/dist/esm/Test.js.map +1 -1
  144. package/dist/esm/index.js +0 -4
  145. package/dist/esm/index.js.map +1 -1
  146. package/dist/esm/internal/EventSource.js +91 -0
  147. package/dist/esm/internal/EventSource.js.map +1 -0
  148. package/dist/esm/internal/browser.js +12 -12
  149. package/dist/esm/internal/browser.js.map +1 -1
  150. package/dist/esm/internal/chunks.js +2 -0
  151. package/dist/esm/internal/chunks.js.map +1 -1
  152. package/dist/esm/internal/errors.js +3 -0
  153. package/dist/esm/internal/errors.js.map +1 -1
  154. package/dist/esm/internal/hydrate.js +113 -76
  155. package/dist/esm/internal/hydrate.js.map +1 -1
  156. package/dist/esm/internal/indexRefCounter.js +50 -2
  157. package/dist/esm/internal/indexRefCounter.js.map +1 -1
  158. package/dist/esm/internal/parser.js +98 -22
  159. package/dist/esm/internal/parser.js.map +1 -1
  160. package/dist/esm/internal/parts.js +110 -27
  161. package/dist/esm/internal/parts.js.map +1 -1
  162. package/dist/esm/internal/render.js +446 -157
  163. package/dist/esm/internal/render.js.map +1 -1
  164. package/dist/esm/internal/server.js +5 -4
  165. package/dist/esm/internal/server.js.map +1 -1
  166. package/package.json +10 -26
  167. package/src/Directive.ts +1 -1
  168. package/src/ElementRef.ts +18 -14
  169. package/src/ElementSource.ts +62 -47
  170. package/src/EventHandler.ts +1 -1
  171. package/src/Html.ts +58 -57
  172. package/src/HtmlChunk.ts +15 -1
  173. package/src/Hydrate.ts +1 -1
  174. package/src/Many.ts +53 -43
  175. package/src/Parser.ts +10 -453
  176. package/src/Part.ts +15 -3
  177. package/src/Placeholder.ts +4 -4
  178. package/src/Platform.ts +2 -2
  179. package/src/Render.ts +7 -2
  180. package/src/RenderContext.ts +47 -19
  181. package/src/RenderTemplate.ts +9 -54
  182. package/src/Renderable.ts +2 -1
  183. package/src/Template.ts +26 -0
  184. package/src/TemplateInstance.ts +9 -9
  185. package/src/Test.ts +40 -21
  186. package/src/index.ts +0 -4
  187. package/src/internal/EventSource.ts +153 -0
  188. package/src/internal/browser.ts +26 -25
  189. package/src/internal/chunks.ts +4 -0
  190. package/src/internal/errors.ts +4 -0
  191. package/src/internal/hydrate.ts +161 -107
  192. package/src/internal/indexRefCounter.ts +63 -2
  193. package/src/internal/module-augmentation.ts +0 -4
  194. package/src/internal/parser.ts +107 -25
  195. package/src/internal/parts.ts +158 -73
  196. package/src/internal/render.ts +638 -289
  197. package/src/internal/server.ts +5 -3
  198. package/Token/package.json +0 -6
  199. package/Tokenizer/package.json +0 -6
  200. package/dist/cjs/Token.js +0 -270
  201. package/dist/cjs/Token.js.map +0 -1
  202. package/dist/cjs/Tokenizer.js +0 -18
  203. package/dist/cjs/Tokenizer.js.map +0 -1
  204. package/dist/cjs/internal/readAttribute.js +0 -34
  205. package/dist/cjs/internal/readAttribute.js.map +0 -1
  206. package/dist/cjs/internal/tokenizer.js +0 -264
  207. package/dist/cjs/internal/tokenizer.js.map +0 -1
  208. package/dist/dts/Token.d.ts +0 -202
  209. package/dist/dts/Token.d.ts.map +0 -1
  210. package/dist/dts/Tokenizer.d.ts +0 -6
  211. package/dist/dts/Tokenizer.d.ts.map +0 -1
  212. package/dist/dts/internal/readAttribute.d.ts +0 -9
  213. package/dist/dts/internal/readAttribute.d.ts.map +0 -1
  214. package/dist/dts/internal/tokenizer.d.ts +0 -3
  215. package/dist/dts/internal/tokenizer.d.ts.map +0 -1
  216. package/dist/esm/Token.js +0 -264
  217. package/dist/esm/Token.js.map +0 -1
  218. package/dist/esm/Tokenizer.js +0 -9
  219. package/dist/esm/Tokenizer.js.map +0 -1
  220. package/dist/esm/internal/readAttribute.js +0 -24
  221. package/dist/esm/internal/readAttribute.js.map +0 -1
  222. package/dist/esm/internal/tokenizer.js +0 -296
  223. package/dist/esm/internal/tokenizer.js.map +0 -1
  224. package/src/Token.ts +0 -269
  225. package/src/Tokenizer.ts +0 -10
  226. package/src/internal/readAttribute.ts +0 -28
  227. package/src/internal/tokenizer.ts +0 -338
@@ -1,92 +1,405 @@
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";
6
- import { replace } from "effect/ReadonlyArray";
5
+ import * as Context from "effect/Context";
6
+ import { Scope } from "effect/Scope";
7
7
  import { isDirective } from "../Directive.js";
8
8
  import * as ElementRef from "../ElementRef.js";
9
+ import * as ElementSource from "../ElementSource.js";
9
10
  import * as EventHandler from "../EventHandler.js";
10
11
  import { DomRenderEvent } from "../RenderEvent.js";
11
- import { TemplateInstance } from "../TemplateInstance.js";
12
12
  import { makeRenderNodePart } from "./browser.js";
13
+ import { makeEventSource } from "./EventSource.js";
13
14
  import { HydrateContext } from "./HydrateContext.js";
14
- import { indexRefCounter } from "./indexRefCounter.js";
15
+ import { indexRefCounter2 } from "./indexRefCounter.js";
15
16
  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
- /**
19
- * Here for "standard" browser rendering, a TemplateInstance is effectively a live
20
- * view into the contents rendered by the Template.
21
- */
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);
36
- }
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) {
43
- return Effect.all(parts.map((part, index) => {
44
- switch (part._tag) {
45
- case "sparse/attribute":
46
- case "sparse/className":
47
- case "sparse/comment": {
48
- return renderSparsePart(values, part, refCounter);
17
+ import { AttributePartImpl, BooleanPartImpl, ClassNamePartImpl, CommentPartImpl, DataPartImpl, PropertyPartImpl, RefPartImpl, TextPartImpl } from "./parts.js";
18
+ import { findHoleComment, findPath } from "./utils.js";
19
+ const RenderPartMap = {
20
+ "attr": (templatePart, node, ctx) => {
21
+ const { document, refCounter, renderContext, values } = ctx;
22
+ const element = node;
23
+ const attr = createAttribute(document, element, templatePart.name);
24
+ const renderable = values[templatePart.index];
25
+ let isSet = true;
26
+ const setValue = (value) => {
27
+ if (isNullOrUndefined(value)) {
28
+ element.removeAttribute(templatePart.name);
29
+ isSet = false;
30
+ }
31
+ else {
32
+ attr.value = String(value);
33
+ if (isSet === false) {
34
+ element.setAttributeNode(attr);
35
+ isSet = true;
36
+ }
37
+ }
38
+ };
39
+ 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++);
40
+ },
41
+ "boolean-part": (templatePart, node, ctx) => {
42
+ const { refCounter, renderContext, values } = ctx;
43
+ const element = node;
44
+ const renderable = values[templatePart.index];
45
+ const setValue = (value) => {
46
+ element.toggleAttribute(templatePart.name, isNullOrUndefined(value) ? false : Boolean(value));
47
+ };
48
+ 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++);
49
+ },
50
+ "className-part": (templatePart, node, ctx) => {
51
+ const { refCounter, renderContext, values } = ctx;
52
+ const element = node;
53
+ const renderable = values[templatePart.index];
54
+ let classNames = new Set();
55
+ const setValue = (value) => {
56
+ if (isNullOrUndefined(value)) {
57
+ element.classList.remove(...classNames);
58
+ classNames.clear();
59
+ }
60
+ else {
61
+ const newClassNames = new Set(Array.isArray(value) ? value : [String(value)]);
62
+ const { added, removed } = diffClassNames(classNames, newClassNames);
63
+ if (removed.length > 0) {
64
+ element.classList.remove(...removed);
65
+ }
66
+ if (added.length > 0)
67
+ element.classList.add(...added);
68
+ classNames = newClassNames;
69
+ }
70
+ };
71
+ return matchSettablePart(renderable, setValue, () => ClassNamePartImpl.browser(templatePart.index, element, renderContext), (f) => Effect.zipRight(renderContext.queue.add(element, f), refCounter.release(templatePart.index)), () => ctx.expected++);
72
+ },
73
+ "comment-part": (templatePart, node, ctx) => {
74
+ const { refCounter, renderContext, values } = ctx;
75
+ const comment = findHoleComment(node, templatePart.index);
76
+ const renderable = values[templatePart.index];
77
+ const setValue = (value) => {
78
+ comment.textContent = isNullOrUndefined(value) ? "" : String(value);
79
+ };
80
+ return matchSettablePart(renderable, setValue, () => CommentPartImpl.browser(templatePart.index, comment, renderContext), (f) => Effect.zipRight(renderContext.queue.add(comment, f), refCounter.release(templatePart.index)), () => ctx.expected++);
81
+ },
82
+ "data": (templatePart, node, ctx) => {
83
+ const element = node;
84
+ const renderable = ctx.values[templatePart.index];
85
+ const previousKeys = new Set(Object.keys(element.dataset));
86
+ const setValue = (value) => {
87
+ if (isNullOrUndefined(value)) {
88
+ for (const key of previousKeys) {
89
+ delete element.dataset[key];
90
+ }
91
+ previousKeys.clear();
92
+ }
93
+ else {
94
+ for (const key of previousKeys) {
95
+ if (!(key in value)) {
96
+ delete element.dataset[key];
97
+ previousKeys.delete(key);
98
+ }
99
+ }
100
+ for (const key of Object.keys(value)) {
101
+ if (!previousKeys.has(key)) {
102
+ previousKeys.add(key);
103
+ }
104
+ element.dataset[key] = value[key] || "";
105
+ }
106
+ }
107
+ };
108
+ 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++);
109
+ },
110
+ "event": (templatePart, node, ctx) => {
111
+ const element = node;
112
+ const renderable = ctx.values[templatePart.index];
113
+ const handler = getEventHandler(renderable, ctx.context, ctx.onCause);
114
+ if (handler) {
115
+ ctx.eventSource.addEventListener(element, templatePart.name, handler);
116
+ }
117
+ return null;
118
+ },
119
+ "node": (templatePart, node, ctx) => {
120
+ const makeHydrateContext = ctx.makeHydrateContext;
121
+ const part = makeRenderNodePart(templatePart.index, node, ctx.renderContext, ctx.document, !!makeHydrateContext);
122
+ ctx.expected++;
123
+ const handle = handlePart(ctx.values[templatePart.index], (value) => Effect.zipRight(part.update(value), ctx.refCounter.release(templatePart.index)));
124
+ if (makeHydrateContext) {
125
+ return Effect.provideService(handle, HydrateContext, makeHydrateContext(templatePart.index));
126
+ }
127
+ else {
128
+ return handle;
129
+ }
130
+ },
131
+ "property": (templatePart, node, ctx) => {
132
+ const element = node;
133
+ const renderable = ctx.values[templatePart.index];
134
+ const setValue = (value) => {
135
+ if (isNullOrUndefined(value)) {
136
+ delete element[templatePart.name];
137
+ }
138
+ else {
139
+ ;
140
+ element[templatePart.name] = value;
141
+ }
142
+ };
143
+ 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++);
144
+ },
145
+ "properties": (templatePart, node, ctx) => {
146
+ const renderable = ctx.values[templatePart.index];
147
+ if (isNullOrUndefined(renderable))
148
+ return null;
149
+ else if (Fx.isFx(renderable) || Effect.isEffect(renderable)) {
150
+ throw new Error(`Properties Part must utilize an Record of renderable values.`);
151
+ }
152
+ else if (typeof renderable === "object" && !Array.isArray(renderable)) {
153
+ const element = node;
154
+ const toggleBoolean = (key, value) => {
155
+ element.toggleAttribute(key, isNullOrUndefined(value) ? false : Boolean(value));
156
+ };
157
+ const setAttribute = (key, value) => {
158
+ if (isNullOrUndefined(value)) {
159
+ element.removeAttribute(key);
160
+ }
161
+ else {
162
+ element.setAttribute(key, String(value));
163
+ }
164
+ };
165
+ const setProperty = (key, value) => {
166
+ if (isNullOrUndefined(value)) {
167
+ delete element[key];
168
+ }
169
+ else {
170
+ ;
171
+ element[key] = value;
172
+ }
173
+ };
174
+ const effects = [];
175
+ // We need indexes to track async values that won't conflict
176
+ // with any other Parts, we can start end of the current values.length
177
+ // As there should only ever be exactly 1 properties part.
178
+ let i = ctx.values.length;
179
+ loop: for (const [key, value] of Object.entries(renderable)) {
180
+ const index = ++i;
181
+ switch (key[0]) {
182
+ case "?": {
183
+ const name = key.slice(1);
184
+ 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++);
185
+ if (eff !== null) {
186
+ effects.push(eff);
187
+ }
188
+ continue loop;
189
+ }
190
+ case ".": {
191
+ const name = key.slice(1);
192
+ 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++);
193
+ if (eff !== null) {
194
+ effects.push(eff);
195
+ }
196
+ continue loop;
197
+ }
198
+ case "@": {
199
+ const name = key.slice(1);
200
+ const handler = getEventHandler(value, ctx.context, ctx.onCause);
201
+ if (handler) {
202
+ ctx.eventSource.addEventListener(element, name, handler);
203
+ }
204
+ continue loop;
205
+ }
206
+ case "o": {
207
+ if (key[1] === "n") {
208
+ const name = key.slice(2);
209
+ const handler = getEventHandler(value, ctx.context, ctx.onCause);
210
+ if (handler) {
211
+ ctx.eventSource.addEventListener(element, name, handler);
212
+ }
213
+ }
214
+ continue loop;
215
+ }
216
+ }
217
+ 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++);
218
+ if (eff !== null) {
219
+ effects.push(eff);
220
+ }
221
+ }
222
+ return effects;
223
+ }
224
+ else {
225
+ return null;
226
+ }
227
+ },
228
+ "ref": (templatePart, node, ctx) => {
229
+ const element = node;
230
+ const renderable = ctx.values[templatePart.index];
231
+ if (isDirective(renderable)) {
232
+ return renderable(new RefPartImpl(ElementSource.fromElement(element), templatePart.index));
233
+ }
234
+ else if (ElementRef.isElementRef(renderable)) {
235
+ return ElementRef.set(renderable, element);
236
+ }
237
+ return null;
238
+ },
239
+ "sparse-attr": (templatePart, node, ctx) => {
240
+ const values = Array.from({ length: templatePart.nodes.length }, () => "");
241
+ const element = node;
242
+ const attr = createAttribute(ctx.document, element, templatePart.name);
243
+ const setValue = (value, index) => Effect.suspend(() => {
244
+ values[index] = value || "";
245
+ return ctx.renderContext.queue.add(element, () => attr.value = values.join(""));
246
+ });
247
+ const effects = [];
248
+ for (let i = 0; i < templatePart.nodes.length; ++i) {
249
+ const node = templatePart.nodes[i];
250
+ if (node._tag === "text") {
251
+ values[i] = node.value;
252
+ }
253
+ else {
254
+ const renderable = ctx.values[node.index];
255
+ const index = i;
256
+ 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++);
257
+ if (effect !== null) {
258
+ effects.push(effect);
259
+ }
260
+ }
261
+ }
262
+ return effects;
263
+ },
264
+ "sparse-class-name": (templatePart, node, ctx) => {
265
+ const element = node;
266
+ const effects = templatePart.nodes.flatMap((node) => {
267
+ if (node._tag === "text") {
268
+ const split = splitClassNames(node.value);
269
+ if (split.length > 0)
270
+ element.classList.add(...split);
271
+ return [];
272
+ }
273
+ else {
274
+ const eff = RenderPartMap[node._tag](node, element, ctx);
275
+ if (eff === null)
276
+ return [];
277
+ return Array.isArray(eff) ? eff : [eff];
278
+ }
279
+ });
280
+ return effects;
281
+ },
282
+ "sparse-comment": (templatePart, node, ctx) => {
283
+ const values = Array.from({ length: templatePart.nodes.length }, () => "");
284
+ const comment = node;
285
+ const setValue = (value, index) => Effect.suspend(() => {
286
+ values[index] = value || "";
287
+ return ctx.renderContext.queue.add(comment, () => comment.textContent = values.join(""));
288
+ });
289
+ const effects = [];
290
+ for (let i = 0; i < templatePart.nodes.length; ++i) {
291
+ const node = templatePart.nodes[i];
292
+ if (node._tag === "text") {
293
+ values[i] = node.value;
294
+ }
295
+ else {
296
+ const renderable = ctx.values[node.index];
297
+ const index = i;
298
+ 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++);
299
+ if (effect !== null) {
300
+ effects.push(effect);
301
+ }
49
302
  }
50
- default:
51
- return renderPart(values, part, refCounter, onCause, makeHydrateContext ? () => makeHydrateContext(index) : undefined);
52
303
  }
53
- }));
304
+ return effects;
305
+ },
306
+ "text-part": (templatePart, node, ctx) => {
307
+ const part = TextPartImpl.browser(ctx.document, templatePart.index, node, ctx.renderContext);
308
+ ctx.expected++;
309
+ return handlePart(ctx.values[templatePart.index], (value) => Effect.zipRight(part.update(value), ctx.refCounter.release(templatePart.index)));
310
+ }
311
+ };
312
+ const SPACE_REGEXP = /\s+/g;
313
+ function splitClassNames(value) {
314
+ return value.split(SPACE_REGEXP).flatMap((a) => {
315
+ const trimmed = a.trim();
316
+ return trimmed.length > 0 ? [trimmed] : [];
317
+ });
54
318
  }
55
- export function renderSparsePart(values, part, refCounter) {
56
- const indexes = part.parts.flatMap((p) => p._tag === "static/text" ? [] : [p.index]);
57
- 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)))));
319
+ function isNullOrUndefined(value) {
320
+ return value === null || value === undefined;
58
321
  }
59
- export function renderPart(values, part, refCounter, onCause, hydrateCtx) {
60
- const partIndex = part.index;
61
- const renderable = values[partIndex];
62
- if (isDirective(renderable)) {
63
- return renderable(part).pipe(Effect.tap(() => refCounter.release(partIndex)), Effect.forkScoped);
64
- }
65
- else if (part._tag === "ref") {
66
- return refCounter.release(partIndex);
67
- }
68
- else if (part._tag === "event") {
69
- return Effect.tap(part.update(getEventHandler(values[partIndex], onCause)), () => refCounter.release(partIndex));
70
- }
71
- 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);
322
+ function diffClassNames(oldClassNames, newClassNames) {
323
+ const added = [];
324
+ const removed = [];
325
+ for (const className of oldClassNames) {
326
+ if (!newClassNames.has(className)) {
327
+ removed.push(className);
328
+ }
75
329
  }
76
- 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)));
330
+ for (const className of newClassNames) {
331
+ if (!oldClassNames.has(className) && className.trim()) {
332
+ added.push(className);
333
+ }
81
334
  }
335
+ return { added, removed };
82
336
  }
83
- function getEventHandler(renderable, onCause) {
337
+ /**
338
+ * @internal
339
+ */
340
+ export function renderPart2(part, content, path, ctx) {
341
+ return RenderPartMap[part._tag](part, findPath(content, path), ctx);
342
+ }
343
+ /**
344
+ * Here for "standard" browser rendering, a TemplateInstance is effectively a live
345
+ * view into the contents rendered by the Template.
346
+ */
347
+ export const renderTemplate = (document, renderContext) => (templateStrings, values) => {
348
+ const entry = getBrowserEntry(document, renderContext, templateStrings);
349
+ if (values.length === 0) {
350
+ return Fx.sync(() => DomRenderEvent(persistent(document.importNode(entry.content, true))));
351
+ }
352
+ return Fx.make((sink) => {
353
+ return Effect.gen(function* (_) {
354
+ const content = document.importNode(entry.content, true);
355
+ const context = yield* _(Effect.context());
356
+ const refCounter = yield* _(indexRefCounter2());
357
+ const ctx = {
358
+ context,
359
+ document,
360
+ eventSource: makeEventSource(),
361
+ expected: 0,
362
+ refCounter,
363
+ renderContext,
364
+ onCause: sink.onFailure,
365
+ values
366
+ };
367
+ // Connect our interpolated values to our template parts
368
+ const effects = [];
369
+ for (const [part, path] of entry.template.parts) {
370
+ const eff = renderPart2(part, content, path, ctx);
371
+ if (eff !== null) {
372
+ effects.push(...(Array.isArray(eff) ? eff : [eff]));
373
+ }
374
+ }
375
+ // Fork any effects necessary
376
+ if (effects.length > 0) {
377
+ yield* _(Effect.forkAll(effects));
378
+ }
379
+ // If there's anything to wait on and it's not already done, wait for an initial value
380
+ // for all asynchronous sources.
381
+ if (ctx.expected > 0 && (yield* _(refCounter.expect(ctx.expected)))) {
382
+ yield* _(refCounter.wait);
383
+ }
384
+ // Create a persistent wire from our content
385
+ const wire = persistent(content);
386
+ // Set the element when it is ready
387
+ yield* _(ctx.eventSource.setup(wire, Context.get(context, Scope)));
388
+ // Emit our DomRenderEvent
389
+ yield* _(sink.onSuccess(DomRenderEvent(wire)));
390
+ // Ensure our templates last forever in the DOM environment
391
+ // so event listeners are kept attached to the current Scope.
392
+ yield* _(Effect.never);
393
+ });
394
+ });
395
+ };
396
+ function getEventHandler(renderable, ctx, onCause) {
84
397
  if (renderable && typeof renderable === "object") {
85
398
  if (EventHandler.EventHandlerTypeId in renderable) {
86
- return EventHandler.make((ev) => Effect.catchAllCause(renderable.handler(ev), onCause), renderable.options);
399
+ return EventHandler.make((ev) => Effect.provide(Effect.catchAllCause(renderable.handler(ev), onCause), ctx), renderable.options);
87
400
  }
88
401
  else if (Effect.EffectTypeId in renderable) {
89
- return EventHandler.make(() => Effect.catchAllCause(renderable, onCause));
402
+ return EventHandler.make(() => Effect.provide(Effect.catchAllCause(renderable, onCause), ctx));
90
403
  }
91
404
  }
92
405
  return null;
@@ -100,7 +413,7 @@ function handlePart(renderable, update) {
100
413
  else if (Array.isArray(renderable)) {
101
414
  return renderable.length === 0
102
415
  ? update(null)
103
- : Effect.forkScoped(Fx.observe(Fx.combine(renderable.map(unwrapRenderable)), update));
416
+ : Effect.forkScoped(Fx.observe(Fx.tuple(renderable.map(unwrapRenderable)), update));
104
417
  }
105
418
  else if (TypeId in renderable) {
106
419
  return Effect.forkScoped(Fx.observe(renderable, update));
@@ -122,7 +435,7 @@ function unwrapRenderable(renderable) {
122
435
  if (renderable === null || renderable === undefined)
123
436
  return Fx.succeed(null);
124
437
  else if (Array.isArray(renderable)) {
125
- return renderable.length === 0 ? Fx.succeed(null) : Fx.combine(renderable.map(unwrapRenderable));
438
+ return renderable.length === 0 ? Fx.succeed(null) : Fx.tuple(renderable.map(unwrapRenderable));
126
439
  }
127
440
  else if (TypeId in renderable) {
128
441
  return renderable;
@@ -137,20 +450,6 @@ function unwrapRenderable(renderable) {
137
450
  return Fx.succeed(renderable);
138
451
  }
139
452
  }
140
- function unwrapSparsePartRenderables(renderables, part) {
141
- return Fx.combine(
142
- // @ts-ignore type too deep
143
- renderables.map((renderable, i) => {
144
- const p = part.parts[i];
145
- if (p._tag === "static/text") {
146
- return Fx.succeed(p.value);
147
- }
148
- if (isDirective(renderable)) {
149
- return Fx.fromEffect(Effect.map(renderable(p), () => p.value));
150
- }
151
- return Fx.mapEffect(unwrapRenderable(renderable), (u) => Effect.map(p.update(u), () => p.value));
152
- }));
153
- }
154
453
  export function attachRoot(cache, where, what // TODO: Should we support HTML RenderEvents here too?
155
454
  ) {
156
455
  return Effect.sync(() => {
@@ -196,80 +495,6 @@ export function getBrowserEntry(document, ctx, templateStrings) {
196
495
  return cached;
197
496
  }
198
497
  }
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)));
201
- }
202
- function buildPartWithNode(document, ctx, part, node, ref, onCause, isHydrating) {
203
- switch (part._tag) {
204
- case "attr":
205
- return Effect.succeed(AttributePartImpl.browser(part.index, node, part.name, ctx));
206
- case "boolean-part":
207
- return Effect.succeed(BooleanPartImpl.browser(part.index, node, part.name, ctx));
208
- case "className-part":
209
- return Effect.succeed(ClassNamePartImpl.browser(part.index, node, ctx));
210
- case "comment-part":
211
- return Effect.succeed(CommentPartImpl.browser(part.index, node, ctx));
212
- case "data":
213
- return Effect.succeed(DataPartImpl.browser(part.index, node, ctx));
214
- case "event":
215
- return EventPartImpl.browser(part.name, part.index, ref, node, onCause);
216
- case "node":
217
- return Effect.succeed(makeRenderNodePart(part.index, node, ctx, document, isHydrating));
218
- case "property":
219
- return Effect.succeed(PropertyPartImpl.browser(part.index, node, part.name, ctx));
220
- case "ref":
221
- return Effect.succeed(new RefPartImpl(ref.query(node), part.index));
222
- case "sparse-attr": {
223
- const parts = Array(part.nodes.length);
224
- const sparse = SparseAttributePartImpl.browser(part.name, parts, node, ctx);
225
- for (let i = 0; i < part.nodes.length; ++i) {
226
- const node = part.nodes[i];
227
- if (node._tag === "text") {
228
- parts.push(new StaticTextImpl(node.value));
229
- sparse.value[i] = node.value;
230
- }
231
- else {
232
- parts.push(new AttributePartImpl(node.name, node.index, ({ value }) => sparse.update(replace(sparse.value, i, value || "")), sparse.value[i]));
233
- }
234
- }
235
- return Effect.succeed(sparse);
236
- }
237
- case "sparse-class-name": {
238
- const parts = [];
239
- const values = []; // TODO: Do this for all other sparse attrs
240
- const sparse = SparseClassNamePartImpl.browser(parts, node, ctx, values);
241
- for (let i = 0; i < part.nodes.length; ++i) {
242
- const node = part.nodes[i];
243
- if (node._tag === "text") {
244
- parts.push(new StaticTextImpl(node.value));
245
- values.push(node.value);
246
- }
247
- else {
248
- values.push([]);
249
- parts.push(new ClassNamePartImpl(node.index, ({ value }) => sparse.update(replace(sparse.value, i, value || "")), []));
250
- }
251
- }
252
- return Effect.succeed(sparse);
253
- }
254
- case "sparse-comment": {
255
- const parts = Array(part.nodes.length);
256
- const sparse = SparseCommentPartImpl.browser(node, parts, ctx);
257
- for (let i = 0; i < part.nodes.length; ++i) {
258
- const node = part.nodes[i];
259
- if (node._tag === "text") {
260
- parts.push(new StaticTextImpl(node.value));
261
- sparse.value[i] = node.value;
262
- }
263
- else {
264
- parts.push(new CommentPartImpl(node.index, ({ value }) => sparse.update(replace(sparse.value, i, value || "")), sparse.value[i]));
265
- }
266
- }
267
- return Effect.succeed(sparse);
268
- }
269
- case "text-part":
270
- return Effect.succeed(TextPartImpl.browser(document, part.index, node, ctx));
271
- }
272
- }
273
498
  export function buildTemplate(document, { nodes }) {
274
499
  const fragment = document.createDocumentFragment();
275
500
  for (let i = 0; i < nodes.length; ++i) {
@@ -293,6 +518,33 @@ function buildNode(document, node, isSvgContext) {
293
518
  case "comment-part":
294
519
  case "node":
295
520
  return document.createComment(`hole${node.index}`);
521
+ case "doctype":
522
+ return document.implementation.createDocumentType(node.name, docTypeNameToPublicId(node.name), docTypeNameToSystemId(node.name));
523
+ }
524
+ }
525
+ function docTypeNameToPublicId(name) {
526
+ switch (name) {
527
+ case "html":
528
+ return "-//W3C//DTD HTML 4.01//EN";
529
+ case "svg":
530
+ return "-//W3C//DTD SVG 1.1//EN";
531
+ case "math":
532
+ return "-//W3C//DTD MathML 2.0//EN";
533
+ default:
534
+ return "";
535
+ }
536
+ }
537
+ function docTypeNameToSystemId(name) {
538
+ switch (name) {
539
+ // HTML5
540
+ case "html":
541
+ return "http://www.w3.org/TR/html4/strict.dtd";
542
+ case "svg":
543
+ return "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";
544
+ case "math":
545
+ return "http://www.w3.org/Math/DTD/mathml2/mathml2.dtd";
546
+ default:
547
+ return "";
296
548
  }
297
549
  }
298
550
  const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
@@ -326,4 +578,41 @@ function buildTextChild(document, node) {
326
578
  }
327
579
  return document.createComment(`hole${node.index}`);
328
580
  }
581
+ function createAttribute(document, element, name) {
582
+ return element.getAttributeNode(name) ?? document.createAttribute(name);
583
+ }
584
+ function matchSettablePart(renderable, setValue, makePart, schedule, expect) {
585
+ return matchRenderable(renderable, {
586
+ Fx: (fx) => {
587
+ expect();
588
+ return Fx.observe(fx, (a) => schedule(() => setValue(a)));
589
+ },
590
+ Effect: (effect) => {
591
+ expect();
592
+ return Effect.flatMap(effect, (a) => schedule(() => setValue(a)));
593
+ },
594
+ Directive: (directive) => {
595
+ expect();
596
+ return directive(makePart());
597
+ },
598
+ Otherwise: (otherwise) => {
599
+ setValue(otherwise);
600
+ return null;
601
+ }
602
+ });
603
+ }
604
+ function matchRenderable(renderable, matches) {
605
+ if (Fx.isFx(renderable)) {
606
+ return matches.Fx(renderable);
607
+ }
608
+ else if (Effect.isEffect(renderable)) {
609
+ return matches.Effect(renderable);
610
+ }
611
+ else if (isDirective(renderable)) {
612
+ return matches.Directive(renderable);
613
+ }
614
+ else {
615
+ return matches.Otherwise(renderable);
616
+ }
617
+ }
329
618
  //# sourceMappingURL=render.js.map