@typed/template 0.9.6 → 0.10.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.
- package/RenderQueue/package.json +6 -0
- package/dist/cjs/Directive.js +1 -1
- package/dist/cjs/Directive.js.map +1 -1
- package/dist/cjs/ElementRef.js +1 -1
- package/dist/cjs/ElementRef.js.map +1 -1
- package/dist/cjs/ElementSource.js +1 -1
- package/dist/cjs/ElementSource.js.map +1 -1
- package/dist/cjs/EventHandler.js +11 -4
- package/dist/cjs/EventHandler.js.map +1 -1
- package/dist/cjs/Html.js +84 -44
- package/dist/cjs/Html.js.map +1 -1
- package/dist/cjs/HtmlChunk.js +67 -21
- package/dist/cjs/HtmlChunk.js.map +1 -1
- package/dist/cjs/Hydrate.js +6 -6
- package/dist/cjs/Hydrate.js.map +1 -1
- package/dist/cjs/Many.js +4 -4
- package/dist/cjs/Many.js.map +1 -1
- package/dist/cjs/Meta.js +10 -3
- package/dist/cjs/Meta.js.map +1 -1
- package/dist/cjs/Parser.js +1 -1
- package/dist/cjs/Placeholder.js +5 -9
- package/dist/cjs/Placeholder.js.map +1 -1
- package/dist/cjs/Platform.js +7 -5
- package/dist/cjs/Platform.js.map +1 -1
- package/dist/cjs/Render.js +8 -7
- package/dist/cjs/Render.js.map +1 -1
- package/dist/cjs/RenderContext.js +8 -92
- package/dist/cjs/RenderContext.js.map +1 -1
- package/dist/cjs/RenderEvent.js +9 -1
- package/dist/cjs/RenderEvent.js.map +1 -1
- package/dist/cjs/RenderQueue.js +341 -0
- package/dist/cjs/RenderQueue.js.map +1 -0
- package/dist/cjs/RenderTemplate.js +1 -1
- package/dist/cjs/RenderTemplate.js.map +1 -1
- package/dist/cjs/Template.js +12 -0
- package/dist/cjs/Template.js.map +1 -1
- package/dist/cjs/Test.js +64 -33
- package/dist/cjs/Test.js.map +1 -1
- package/dist/cjs/Vitest.js +12 -20
- package/dist/cjs/Vitest.js.map +1 -1
- package/dist/cjs/index.js +6 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/internal/EventSource.js +16 -9
- package/dist/cjs/internal/EventSource.js.map +1 -1
- package/dist/cjs/internal/HydrateContext.js.map +1 -1
- package/dist/cjs/internal/browser.js +11 -10
- package/dist/cjs/internal/browser.js.map +1 -1
- package/dist/cjs/internal/character-entities.js +2141 -0
- package/dist/cjs/internal/character-entities.js.map +1 -0
- package/dist/cjs/internal/errors.js +19 -2
- package/dist/cjs/internal/errors.js.map +1 -1
- package/dist/cjs/internal/indexRefCounter.js +36 -63
- package/dist/cjs/internal/indexRefCounter.js.map +1 -1
- package/dist/cjs/internal/parser.js +18 -17
- package/dist/cjs/internal/parser.js.map +1 -1
- package/dist/cjs/internal/parser2.js +382 -0
- package/dist/cjs/internal/parser2.js.map +1 -0
- package/dist/cjs/internal/server-parts.js +124 -0
- package/dist/cjs/internal/server-parts.js.map +1 -0
- package/dist/cjs/internal/server.js +15 -185
- package/dist/cjs/internal/server.js.map +1 -1
- package/dist/cjs/internal/utils.js +73 -9
- package/dist/cjs/internal/utils.js.map +1 -1
- package/dist/cjs/internal/v2/SyncPart.js +6 -0
- package/dist/cjs/internal/v2/SyncPart.js.map +1 -0
- package/dist/cjs/internal/v2/helpers.js +15 -0
- package/dist/cjs/internal/v2/helpers.js.map +1 -0
- package/dist/cjs/internal/v2/hydrate.js +202 -0
- package/dist/cjs/internal/v2/hydrate.js.map +1 -0
- package/dist/cjs/internal/v2/hydration-template.js +269 -0
- package/dist/cjs/internal/v2/hydration-template.js.map +1 -0
- package/dist/cjs/internal/v2/parts.js +169 -0
- package/dist/cjs/internal/v2/parts.js.map +1 -0
- package/dist/cjs/internal/v2/render-entry.js +110 -0
- package/dist/cjs/internal/v2/render-entry.js.map +1 -0
- package/dist/cjs/internal/v2/render-sync-parts.js +318 -0
- package/dist/cjs/internal/v2/render-sync-parts.js.map +1 -0
- package/dist/cjs/internal/v2/render.js +417 -0
- package/dist/cjs/internal/v2/render.js.map +1 -0
- package/dist/cjs/internal/v2/sync-parts.js +115 -0
- package/dist/cjs/internal/v2/sync-parts.js.map +1 -0
- package/dist/dts/ElementRef.d.ts +1 -1
- package/dist/dts/ElementRef.d.ts.map +1 -1
- package/dist/dts/ElementSource.d.ts +1 -1
- package/dist/dts/ElementSource.d.ts.map +1 -1
- package/dist/dts/EventHandler.d.ts +12 -8
- package/dist/dts/EventHandler.d.ts.map +1 -1
- package/dist/dts/Html.d.ts +6 -5
- package/dist/dts/Html.d.ts.map +1 -1
- package/dist/dts/HtmlChunk.d.ts.map +1 -1
- package/dist/dts/Hydrate.d.ts +1 -3
- package/dist/dts/Hydrate.d.ts.map +1 -1
- package/dist/dts/Many.d.ts +9 -11
- package/dist/dts/Many.d.ts.map +1 -1
- package/dist/dts/Meta.d.ts +5 -1
- package/dist/dts/Meta.d.ts.map +1 -1
- package/dist/dts/Parser.d.ts +1 -1
- package/dist/dts/Parser.d.ts.map +1 -1
- package/dist/dts/Part.d.ts +20 -56
- package/dist/dts/Part.d.ts.map +1 -1
- package/dist/dts/Placeholder.d.ts +6 -10
- package/dist/dts/Placeholder.d.ts.map +1 -1
- package/dist/dts/Platform.d.ts +2 -4
- package/dist/dts/Platform.d.ts.map +1 -1
- package/dist/dts/Render.d.ts +4 -8
- package/dist/dts/Render.d.ts.map +1 -1
- package/dist/dts/RenderContext.d.ts +3 -22
- package/dist/dts/RenderContext.d.ts.map +1 -1
- package/dist/dts/RenderEvent.d.ts +6 -1
- package/dist/dts/RenderEvent.d.ts.map +1 -1
- package/dist/dts/RenderQueue.d.ts +103 -0
- package/dist/dts/RenderQueue.d.ts.map +1 -0
- package/dist/dts/RenderTemplate.d.ts +3 -2
- package/dist/dts/RenderTemplate.d.ts.map +1 -1
- package/dist/dts/Renderable.d.ts +1 -1
- package/dist/dts/Template.d.ts +14 -1
- package/dist/dts/Template.d.ts.map +1 -1
- package/dist/dts/Test.d.ts +14 -1
- package/dist/dts/Test.d.ts.map +1 -1
- package/dist/dts/Vitest.d.ts +11 -8
- package/dist/dts/Vitest.d.ts.map +1 -1
- package/dist/dts/index.d.ts +4 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/internal/EventSource.d.ts +2 -1
- package/dist/dts/internal/EventSource.d.ts.map +1 -1
- package/dist/dts/internal/browser.d.ts +3 -3
- package/dist/dts/internal/browser.d.ts.map +1 -1
- package/dist/dts/internal/character-entities.d.ts +2133 -0
- package/dist/dts/internal/character-entities.d.ts.map +1 -0
- package/dist/dts/internal/errors.d.ts +9 -1
- package/dist/dts/internal/errors.d.ts.map +1 -1
- package/dist/dts/internal/indexRefCounter.d.ts +0 -4
- package/dist/dts/internal/indexRefCounter.d.ts.map +1 -1
- package/dist/dts/internal/parser.d.ts +13 -0
- package/dist/dts/internal/parser.d.ts.map +1 -1
- package/dist/dts/internal/parser2.d.ts +12 -0
- package/dist/dts/internal/parser2.d.ts.map +1 -0
- package/dist/dts/internal/server-parts.d.ts +223 -0
- package/dist/dts/internal/server-parts.d.ts.map +1 -0
- package/dist/dts/internal/server.d.ts +2 -28
- package/dist/dts/internal/server.d.ts.map +1 -1
- package/dist/dts/internal/utils.d.ts +4 -1
- package/dist/dts/internal/utils.d.ts.map +1 -1
- package/dist/dts/internal/v2/SyncPart.d.ts +87 -0
- package/dist/dts/internal/v2/SyncPart.d.ts.map +1 -0
- package/dist/dts/internal/v2/helpers.d.ts +3 -0
- package/dist/dts/internal/v2/helpers.d.ts.map +1 -0
- package/dist/dts/internal/v2/hydrate.d.ts +7 -0
- package/dist/dts/internal/v2/hydrate.d.ts.map +1 -0
- package/dist/dts/internal/v2/hydration-template.d.ts +54 -0
- package/dist/dts/internal/v2/hydration-template.d.ts.map +1 -0
- package/dist/dts/internal/v2/parts.d.ts +245 -0
- package/dist/dts/internal/v2/parts.d.ts.map +1 -0
- package/dist/dts/internal/v2/render-entry.d.ts +6 -0
- package/dist/dts/internal/v2/render-entry.d.ts.map +1 -0
- package/dist/dts/internal/v2/render-sync-parts.d.ts +22 -0
- package/dist/dts/internal/v2/render-sync-parts.d.ts.map +1 -0
- package/dist/dts/internal/v2/render.d.ts +62 -0
- package/dist/dts/internal/v2/render.d.ts.map +1 -0
- package/dist/dts/internal/v2/sync-parts.d.ts +129 -0
- package/dist/dts/internal/v2/sync-parts.d.ts.map +1 -0
- package/dist/esm/ElementRef.js.map +1 -1
- package/dist/esm/EventHandler.js +14 -4
- package/dist/esm/EventHandler.js.map +1 -1
- package/dist/esm/Html.js +91 -50
- package/dist/esm/Html.js.map +1 -1
- package/dist/esm/HtmlChunk.js +75 -24
- package/dist/esm/HtmlChunk.js.map +1 -1
- package/dist/esm/Hydrate.js +5 -5
- package/dist/esm/Hydrate.js.map +1 -1
- package/dist/esm/Many.js +3 -3
- package/dist/esm/Many.js.map +1 -1
- package/dist/esm/Meta.js +7 -1
- package/dist/esm/Meta.js.map +1 -1
- package/dist/esm/Parser.js +1 -1
- package/dist/esm/Parser.js.map +1 -1
- package/dist/esm/Placeholder.js +4 -8
- package/dist/esm/Placeholder.js.map +1 -1
- package/dist/esm/Platform.js +3 -1
- package/dist/esm/Platform.js.map +1 -1
- package/dist/esm/Render.js +6 -5
- package/dist/esm/Render.js.map +1 -1
- package/dist/esm/RenderContext.js +5 -85
- package/dist/esm/RenderContext.js.map +1 -1
- package/dist/esm/RenderEvent.js +8 -1
- package/dist/esm/RenderEvent.js.map +1 -1
- package/dist/esm/RenderQueue.js +336 -0
- package/dist/esm/RenderQueue.js.map +1 -0
- package/dist/esm/RenderTemplate.js.map +1 -1
- package/dist/esm/Template.js +12 -0
- package/dist/esm/Template.js.map +1 -1
- package/dist/esm/Test.js +71 -30
- package/dist/esm/Test.js.map +1 -1
- package/dist/esm/Vitest.js +11 -8
- package/dist/esm/Vitest.js.map +1 -1
- package/dist/esm/index.js +4 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/internal/EventSource.js +12 -7
- package/dist/esm/internal/EventSource.js.map +1 -1
- package/dist/esm/internal/HydrateContext.js.map +1 -1
- package/dist/esm/internal/browser.js +10 -9
- package/dist/esm/internal/browser.js.map +1 -1
- package/dist/esm/internal/character-entities.js +2134 -0
- package/dist/esm/internal/character-entities.js.map +1 -0
- package/dist/esm/internal/errors.js +22 -2
- package/dist/esm/internal/errors.js.map +1 -1
- package/dist/esm/internal/indexRefCounter.js +36 -61
- package/dist/esm/internal/indexRefCounter.js.map +1 -1
- package/dist/esm/internal/parser.js +18 -18
- package/dist/esm/internal/parser.js.map +1 -1
- package/dist/esm/internal/parser2.js +393 -0
- package/dist/esm/internal/parser2.js.map +1 -0
- package/dist/esm/internal/server-parts.js +109 -0
- package/dist/esm/internal/server-parts.js.map +1 -0
- package/dist/esm/internal/server.js +12 -161
- package/dist/esm/internal/server.js.map +1 -1
- package/dist/esm/internal/utils.js +71 -7
- package/dist/esm/internal/utils.js.map +1 -1
- package/dist/esm/internal/v2/SyncPart.js +5 -0
- package/dist/esm/internal/v2/SyncPart.js.map +1 -0
- package/dist/esm/internal/v2/helpers.js +12 -0
- package/dist/esm/internal/v2/helpers.js.map +1 -0
- package/dist/esm/internal/v2/hydrate.js +195 -0
- package/dist/esm/internal/v2/hydrate.js.map +1 -0
- package/dist/esm/internal/v2/hydration-template.js +265 -0
- package/dist/esm/internal/v2/hydration-template.js.map +1 -0
- package/dist/esm/internal/v2/parts.js +150 -0
- package/dist/esm/internal/v2/parts.js.map +1 -0
- package/dist/esm/internal/v2/render-entry.js +102 -0
- package/dist/esm/internal/v2/render-entry.js.map +1 -0
- package/dist/esm/internal/v2/render-sync-parts.js +265 -0
- package/dist/esm/internal/v2/render-sync-parts.js.map +1 -0
- package/dist/esm/internal/v2/render.js +353 -0
- package/dist/esm/internal/v2/render.js.map +1 -0
- package/dist/esm/internal/v2/sync-parts.js +102 -0
- package/dist/esm/internal/v2/sync-parts.js.map +1 -0
- package/package.json +20 -13
- package/src/ElementRef.ts +1 -1
- package/src/ElementSource.ts +1 -1
- package/src/EventHandler.ts +29 -11
- package/src/Html.ts +199 -90
- package/src/HtmlChunk.ts +77 -30
- package/src/Hydrate.ts +20 -14
- package/src/Many.ts +17 -14
- package/src/Meta.ts +8 -1
- package/src/Parser.ts +1 -1
- package/src/Part.ts +22 -66
- package/src/Placeholder.ts +17 -15
- package/src/Platform.ts +5 -5
- package/src/Render.ts +23 -26
- package/src/RenderContext.ts +14 -142
- package/src/RenderEvent.ts +10 -1
- package/src/RenderQueue.ts +445 -0
- package/src/RenderTemplate.ts +7 -2
- package/src/Renderable.ts +1 -1
- package/src/Template.ts +15 -1
- package/src/Test.ts +122 -38
- package/src/Vitest.ts +20 -10
- package/src/index.ts +4 -0
- package/src/internal/EventSource.ts +14 -8
- package/src/internal/HydrateContext.ts +3 -4
- package/src/internal/browser.ts +26 -21
- package/src/internal/character-entities.ts +2136 -0
- package/src/internal/errors.ts +30 -3
- package/src/internal/indexRefCounter.ts +38 -70
- package/src/internal/parser.ts +19 -19
- package/src/internal/parser2.ts +468 -0
- package/src/internal/server-parts.ts +161 -0
- package/src/internal/server.ts +16 -272
- package/src/internal/utils.ts +83 -7
- package/src/internal/v2/SyncPart.ts +112 -0
- package/src/internal/v2/helpers.ts +13 -0
- package/src/internal/v2/hydrate.ts +289 -0
- package/src/internal/v2/hydration-template.ts +308 -0
- package/src/internal/v2/parts.ts +254 -0
- package/src/internal/v2/render-entry.ts +131 -0
- package/src/internal/v2/render-sync-parts.ts +440 -0
- package/src/internal/v2/render.ts +588 -0
- package/src/internal/v2/sync-parts.ts +133 -0
- package/dist/cjs/internal/hydrate.js +0 -274
- package/dist/cjs/internal/hydrate.js.map +0 -1
- package/dist/cjs/internal/parts.js +0 -451
- package/dist/cjs/internal/parts.js.map +0 -1
- package/dist/cjs/internal/render.js +0 -704
- package/dist/cjs/internal/render.js.map +0 -1
- package/dist/dts/internal/hydrate.d.ts +0 -33
- package/dist/dts/internal/hydrate.d.ts.map +0 -1
- package/dist/dts/internal/parts.d.ts +0 -314
- package/dist/dts/internal/parts.d.ts.map +0 -1
- package/dist/dts/internal/render.d.ts +0 -16
- package/dist/dts/internal/render.d.ts.map +0 -1
- package/dist/esm/internal/hydrate.js +0 -239
- package/dist/esm/internal/hydrate.js.map +0 -1
- package/dist/esm/internal/parts.js +0 -373
- package/dist/esm/internal/parts.js.map +0 -1
- package/dist/esm/internal/render.js +0 -689
- package/dist/esm/internal/render.js.map +0 -1
- package/src/internal/hydrate.ts +0 -366
- package/src/internal/parts.ts +0 -609
- package/src/internal/render.ts +0 -971
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
import * as Context from "@typed/context"
|
|
2
|
+
import * as Fx from "@typed/fx"
|
|
3
|
+
import type { Rendered } from "@typed/wire"
|
|
4
|
+
import { persistent } from "@typed/wire"
|
|
5
|
+
import type * as Cause from "effect/Cause"
|
|
6
|
+
import type { Chunk } from "effect/Chunk"
|
|
7
|
+
import * as Effect from "effect/Effect"
|
|
8
|
+
import * as ExecutionStrategy from "effect/ExecutionStrategy"
|
|
9
|
+
import { flow } from "effect/Function"
|
|
10
|
+
import * as Scope from "effect/Scope"
|
|
11
|
+
import { type Directive, isDirective } from "../../Directive.js"
|
|
12
|
+
import * as ElementRef from "../../ElementRef.js"
|
|
13
|
+
import * as ElementSource from "../../ElementSource.js"
|
|
14
|
+
import * as EventHandler from "../../EventHandler.js"
|
|
15
|
+
import type { Placeholder } from "../../Placeholder.js"
|
|
16
|
+
import type { ToRendered } from "../../Render.js"
|
|
17
|
+
import type { Renderable } from "../../Renderable.js"
|
|
18
|
+
import type { RenderContext } from "../../RenderContext.js"
|
|
19
|
+
import { DomRenderEvent, type RenderEvent } from "../../RenderEvent.js"
|
|
20
|
+
import { DEFAULT_PRIORITY, RenderQueue } from "../../RenderQueue.js"
|
|
21
|
+
import type { RenderTemplate } from "../../RenderTemplate.js"
|
|
22
|
+
import type * as Template from "../../Template.js"
|
|
23
|
+
import type { EventSource } from "../EventSource.js"
|
|
24
|
+
import { makeEventSource } from "../EventSource.js"
|
|
25
|
+
import type { IndexRefCounter } from "../indexRefCounter.js"
|
|
26
|
+
import { makeRefCounter } from "../indexRefCounter.js"
|
|
27
|
+
import { findHoleComment, findPath, keyToPartType } from "../utils.js"
|
|
28
|
+
import { isNullOrUndefined } from "./helpers.js"
|
|
29
|
+
import { EventPartImpl, RefPartImpl, syncPartToPart } from "./parts.js"
|
|
30
|
+
import { getBrowserEntry } from "./render-entry.js"
|
|
31
|
+
import * as SyncPartsInternal from "./render-sync-parts.js"
|
|
32
|
+
import type { SyncPart } from "./SyncPart.js"
|
|
33
|
+
|
|
34
|
+
export type TemplateContext = {
|
|
35
|
+
/**
|
|
36
|
+
* @internal
|
|
37
|
+
*/
|
|
38
|
+
expected: number
|
|
39
|
+
/**
|
|
40
|
+
* @internal
|
|
41
|
+
*/
|
|
42
|
+
spreadIndex: number
|
|
43
|
+
|
|
44
|
+
readonly content: DocumentFragment
|
|
45
|
+
readonly context: Context.Context<Scope.Scope>
|
|
46
|
+
readonly document: Document
|
|
47
|
+
readonly eventSource: EventSource
|
|
48
|
+
readonly parentScope: Scope.Scope
|
|
49
|
+
readonly queue: RenderQueue
|
|
50
|
+
readonly refCounter: IndexRefCounter
|
|
51
|
+
readonly renderContext: RenderContext
|
|
52
|
+
readonly scope: Scope.CloseableScope
|
|
53
|
+
readonly values: ReadonlyArray<Renderable<any, any>>
|
|
54
|
+
readonly onCause: (cause: Cause.Cause<any>) => Effect.Effect<unknown>
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const renderTemplate: (
|
|
58
|
+
document: Document,
|
|
59
|
+
renderContext: RenderContext
|
|
60
|
+
) => RenderTemplate = (document, renderContext) =>
|
|
61
|
+
<Values extends ReadonlyArray<Renderable<any, any>>>(
|
|
62
|
+
templateStrings: TemplateStringsArray,
|
|
63
|
+
values: Values
|
|
64
|
+
) => {
|
|
65
|
+
const entry = getBrowserEntry(document, renderContext, templateStrings)
|
|
66
|
+
if (entry.template.parts.length === 0) {
|
|
67
|
+
return Fx.succeed(DomRenderEvent(persistent(document, document.importNode(entry.content, true))))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return Fx.make<
|
|
71
|
+
RenderEvent,
|
|
72
|
+
Placeholder.Error<Values[number]>,
|
|
73
|
+
Scope.Scope | RenderQueue | Placeholder.Context<Values[number]>
|
|
74
|
+
>((sink) =>
|
|
75
|
+
Effect.catchAllCause(
|
|
76
|
+
Effect.gen(function*() {
|
|
77
|
+
// Create a context for rendering our template
|
|
78
|
+
const ctx = yield* makeTemplateContext<Values>(
|
|
79
|
+
entry.content,
|
|
80
|
+
document,
|
|
81
|
+
renderContext,
|
|
82
|
+
values,
|
|
83
|
+
sink.onFailure
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
// Setup all parts
|
|
87
|
+
const effects = setupParts(entry.template.parts, ctx)
|
|
88
|
+
if (effects.length > 0) {
|
|
89
|
+
yield* Effect.forEach(effects, flow(Effect.catchAllCause(ctx.onCause), Effect.forkIn(ctx.scope)))
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// If there's anything to wait on and it's not already done, wait for an initial value
|
|
93
|
+
// for all asynchronous sources.
|
|
94
|
+
if (ctx.expected > 0 && (yield* ctx.refCounter.expect(ctx.expected))) {
|
|
95
|
+
yield* ctx.refCounter.wait
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Create a persistent wire from our content
|
|
99
|
+
const wire = persistent(document, ctx.content)
|
|
100
|
+
|
|
101
|
+
// Setup our event listeners for our wire.
|
|
102
|
+
// We use the parentScope to allow event listeners to exist
|
|
103
|
+
// beyond the lifetime of the current Fiber, but no further than its parent template.
|
|
104
|
+
yield* ctx.eventSource.setup(wire, ctx.parentScope)
|
|
105
|
+
|
|
106
|
+
// Emit our DomRenderEvent
|
|
107
|
+
yield* sink.onSuccess(DomRenderEvent(wire)).pipe(
|
|
108
|
+
// Ensure our templates last forever in the DOM environment
|
|
109
|
+
// so event listeners are kept attached to the current Scope.
|
|
110
|
+
Effect.zipRight(Effect.never),
|
|
111
|
+
// Close our scope whenever the current Fiber is interrupted
|
|
112
|
+
Effect.onExit((exit) => Scope.close(ctx.scope, exit))
|
|
113
|
+
)
|
|
114
|
+
}),
|
|
115
|
+
sink.onFailure
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function makeTemplateContext<Values extends ReadonlyArray<Renderable<any, any>>>(
|
|
121
|
+
entry: DocumentFragment,
|
|
122
|
+
document: Document,
|
|
123
|
+
renderContext: RenderContext,
|
|
124
|
+
values: ReadonlyArray<Renderable<any, any>>,
|
|
125
|
+
onCause: (cause: Cause.Cause<Placeholder.Error<Values[number]>>) => Effect.Effect<unknown>
|
|
126
|
+
): Effect.Effect<TemplateContext, never, Scope.Scope | RenderQueue | Placeholder.Context<Values[number]>> {
|
|
127
|
+
return Effect.gen(function*() {
|
|
128
|
+
const refCounter = yield* makeRefCounter
|
|
129
|
+
const context = yield* Effect.context<Placeholder.Context<Values[number]> | Scope.Scope | RenderQueue>()
|
|
130
|
+
const queue = Context.get(context, RenderQueue)
|
|
131
|
+
const parentScope = Context.get(context, Scope.Scope)
|
|
132
|
+
const eventSource = makeEventSource()
|
|
133
|
+
const content = document.importNode(entry, true)
|
|
134
|
+
const scope = yield* Scope.fork(parentScope, ExecutionStrategy.sequential)
|
|
135
|
+
const templateContext: TemplateContext = {
|
|
136
|
+
context: Context.add(context, Scope.Scope, scope),
|
|
137
|
+
expected: 0,
|
|
138
|
+
content,
|
|
139
|
+
document,
|
|
140
|
+
eventSource,
|
|
141
|
+
parentScope,
|
|
142
|
+
queue,
|
|
143
|
+
refCounter,
|
|
144
|
+
renderContext,
|
|
145
|
+
scope,
|
|
146
|
+
values,
|
|
147
|
+
spreadIndex: values.length,
|
|
148
|
+
onCause
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return templateContext
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function setupParts(parts: Template.Template["parts"], ctx: TemplateContext) {
|
|
156
|
+
const effects: Array<Effect.Effect<void, any, any>> = []
|
|
157
|
+
|
|
158
|
+
for (const [part, path] of parts) {
|
|
159
|
+
const effect = setupPart(part, path, ctx)
|
|
160
|
+
if (effect) {
|
|
161
|
+
effects.push(effect)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return effects
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function setupPart(
|
|
169
|
+
part: Template.PartNode | Template.SparsePartNode,
|
|
170
|
+
path: Chunk<number>,
|
|
171
|
+
ctx: TemplateContext
|
|
172
|
+
) {
|
|
173
|
+
switch (part._tag) {
|
|
174
|
+
case "attr":
|
|
175
|
+
return setupAttrPart(part, findPath(ctx.content, path) as HTMLElement | SVGElement, ctx, ctx.values[part.index])
|
|
176
|
+
case "boolean-part":
|
|
177
|
+
return setupBooleanPart(
|
|
178
|
+
part,
|
|
179
|
+
findPath(ctx.content, path) as HTMLElement | SVGElement,
|
|
180
|
+
ctx,
|
|
181
|
+
ctx.values[part.index]
|
|
182
|
+
)
|
|
183
|
+
case "className-part":
|
|
184
|
+
return setupClassNamePart(
|
|
185
|
+
part,
|
|
186
|
+
findPath(ctx.content, path) as HTMLElement | SVGElement,
|
|
187
|
+
ctx,
|
|
188
|
+
ctx.values[part.index]
|
|
189
|
+
)
|
|
190
|
+
case "comment-part":
|
|
191
|
+
return setupCommentPart(part, findPath(ctx.content, path) as Comment, ctx)
|
|
192
|
+
case "data":
|
|
193
|
+
return setupDataPart(part, findPath(ctx.content, path) as HTMLElement | SVGElement, ctx, ctx.values[part.index])
|
|
194
|
+
case "event":
|
|
195
|
+
return setupEventPart(part, findPath(ctx.content, path) as HTMLElement | SVGElement, ctx, ctx.values[part.index])
|
|
196
|
+
case "node": {
|
|
197
|
+
const parent = findPath(ctx.content, path) as Element
|
|
198
|
+
const comment = findHoleComment(parent, part.index)
|
|
199
|
+
return setupNodePart(part, comment, ctx, null, [])
|
|
200
|
+
}
|
|
201
|
+
case "properties":
|
|
202
|
+
return setupPropertiesPart(part, findPath(ctx.content, path) as HTMLElement | SVGElement, ctx)
|
|
203
|
+
case "property":
|
|
204
|
+
return setupPropertyPart(
|
|
205
|
+
part,
|
|
206
|
+
findPath(ctx.content, path) as HTMLElement | SVGElement,
|
|
207
|
+
ctx,
|
|
208
|
+
ctx.values[part.index]
|
|
209
|
+
)
|
|
210
|
+
case "ref":
|
|
211
|
+
return setupRefPart(part, findPath(ctx.content, path) as HTMLElement | SVGElement, ctx)
|
|
212
|
+
case "sparse-attr":
|
|
213
|
+
return setupSparseAttrPart(part, findPath(ctx.content, path) as HTMLElement | SVGElement, ctx)
|
|
214
|
+
case "sparse-class-name":
|
|
215
|
+
return setupSparseClassNamePart(part, findPath(ctx.content, path) as HTMLElement | SVGElement, ctx)
|
|
216
|
+
case "sparse-comment":
|
|
217
|
+
return setupSparseCommentPart(part, findPath(ctx.content, path) as Comment, ctx)
|
|
218
|
+
case "text-part": {
|
|
219
|
+
const parent = findPath(ctx.content, path) as Element
|
|
220
|
+
const comment = findHoleComment(parent, part.index)
|
|
221
|
+
return setupTextPart(part, comment, ctx)
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function setupAttrPart(
|
|
227
|
+
{ index, name }: Pick<Template.AttrPartNode, "index" | "name">,
|
|
228
|
+
element: HTMLElement | SVGElement,
|
|
229
|
+
ctx: TemplateContext,
|
|
230
|
+
renderable: Renderable<any, any>
|
|
231
|
+
) {
|
|
232
|
+
const attr = element.getAttributeNode(name) ?? ctx.document.createAttribute(name)
|
|
233
|
+
const part = SyncPartsInternal.makeAttributePart(index, element, attr)
|
|
234
|
+
return matchSyncPart(renderable, ctx, part)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function setupBooleanPart(
|
|
238
|
+
{ index, name }: Pick<Template.BooleanPartNode, "index" | "name">,
|
|
239
|
+
element: HTMLElement | SVGElement,
|
|
240
|
+
ctx: TemplateContext,
|
|
241
|
+
renderable: Renderable<any, any>
|
|
242
|
+
) {
|
|
243
|
+
const part = SyncPartsInternal.makeBooleanAttributePart(name, index, element)
|
|
244
|
+
return matchSyncPart(renderable, ctx, part)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export function setupClassNamePart(
|
|
248
|
+
{ index }: Pick<Template.ClassNamePartNode, "index">,
|
|
249
|
+
element: HTMLElement | SVGElement,
|
|
250
|
+
ctx: TemplateContext,
|
|
251
|
+
renderable: Renderable<any, any>
|
|
252
|
+
) {
|
|
253
|
+
const part = SyncPartsInternal.makeClassNamePart(index, element)
|
|
254
|
+
return matchSyncPart(renderable, ctx, part)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function setupCommentPart(
|
|
258
|
+
{ index }: Pick<Template.CommentPartNode, "index">,
|
|
259
|
+
comment: Comment,
|
|
260
|
+
ctx: TemplateContext
|
|
261
|
+
) {
|
|
262
|
+
const part = SyncPartsInternal.makeCommentPart(index, comment)
|
|
263
|
+
const renderable = ctx.values[index]
|
|
264
|
+
return matchSyncPart(renderable, ctx, part)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function setupDataPart(
|
|
268
|
+
{ index }: Pick<Template.DataPartNode, "index">,
|
|
269
|
+
element: HTMLElement | SVGElement,
|
|
270
|
+
ctx: TemplateContext,
|
|
271
|
+
renderable: Renderable<any, any>
|
|
272
|
+
) {
|
|
273
|
+
const part = SyncPartsInternal.makeDataPart(index, element)
|
|
274
|
+
return matchSyncPart(renderable, ctx, part)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export function setupEventPart(
|
|
278
|
+
{ index, name }: Pick<Template.EventPartNode, "index" | "name">,
|
|
279
|
+
element: HTMLElement | SVGElement,
|
|
280
|
+
ctx: TemplateContext,
|
|
281
|
+
renderable: Renderable<any, any>
|
|
282
|
+
) {
|
|
283
|
+
if (isNullOrUndefined(renderable)) return null
|
|
284
|
+
|
|
285
|
+
if (isDirective(renderable)) {
|
|
286
|
+
return renderable(
|
|
287
|
+
new EventPartImpl(
|
|
288
|
+
name,
|
|
289
|
+
index,
|
|
290
|
+
ElementSource.fromElement(element),
|
|
291
|
+
ctx.onCause,
|
|
292
|
+
(handler) => ctx.eventSource.addEventListener(element, name, handler)
|
|
293
|
+
)
|
|
294
|
+
)
|
|
295
|
+
} else {
|
|
296
|
+
const handler = getEventHandler(renderable, ctx.context, ctx.onCause)
|
|
297
|
+
if (handler === null) return null
|
|
298
|
+
ctx.eventSource.addEventListener(element, name, handler)
|
|
299
|
+
return null
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export function getEventHandler<E, R>(
|
|
304
|
+
renderable: any,
|
|
305
|
+
ctx: Context.Context<any> | Context.Context<never>,
|
|
306
|
+
onCause: (cause: Cause.Cause<E>) => Effect.Effect<unknown>
|
|
307
|
+
): EventHandler.EventHandler<never, never> | null {
|
|
308
|
+
if (renderable && typeof renderable === "object") {
|
|
309
|
+
if (EventHandler.EventHandlerTypeId in renderable) {
|
|
310
|
+
return EventHandler.make(
|
|
311
|
+
(ev) =>
|
|
312
|
+
Effect.provide(
|
|
313
|
+
Effect.catchAllCause((renderable as EventHandler.EventHandler<Event, E, R>).handler(ev), onCause),
|
|
314
|
+
ctx as any
|
|
315
|
+
),
|
|
316
|
+
(renderable as EventHandler.EventHandler<Event, E, R>).options
|
|
317
|
+
)
|
|
318
|
+
} else if (Effect.EffectTypeId in renderable) {
|
|
319
|
+
return EventHandler.make(() => Effect.provide(Effect.catchAllCause(renderable, onCause), ctx))
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return null
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export function setupNodePart(
|
|
327
|
+
{ index }: Template.NodePart,
|
|
328
|
+
comment: Comment,
|
|
329
|
+
ctx: TemplateContext,
|
|
330
|
+
text: Text | null,
|
|
331
|
+
nodes: Array<Node>
|
|
332
|
+
) {
|
|
333
|
+
const part = SyncPartsInternal.makeNodePart(index, comment, ctx.document, text, nodes)
|
|
334
|
+
const renderable = ctx.values[index]
|
|
335
|
+
return matchSyncPart(renderable, ctx, part)
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export function setupPropertyPart(
|
|
339
|
+
{ index, name }: Pick<Template.PropertyPartNode, "index" | "name">,
|
|
340
|
+
element: HTMLElement | SVGElement,
|
|
341
|
+
ctx: TemplateContext,
|
|
342
|
+
renderable: Renderable<any, any>
|
|
343
|
+
) {
|
|
344
|
+
const part = SyncPartsInternal.makePropertyPart(name, index, element)
|
|
345
|
+
return matchSyncPart(renderable, ctx, part)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export function setupRefPart(
|
|
349
|
+
{ index }: Pick<Template.RefPartNode, "index">,
|
|
350
|
+
element: HTMLElement | SVGElement,
|
|
351
|
+
ctx: TemplateContext
|
|
352
|
+
) {
|
|
353
|
+
const renderable = ctx.values[index]
|
|
354
|
+
|
|
355
|
+
if (isNullOrUndefined(renderable)) return null
|
|
356
|
+
else if (isDirective(renderable)) {
|
|
357
|
+
return renderable(
|
|
358
|
+
new RefPartImpl(ElementSource.fromElement(element), index)
|
|
359
|
+
)
|
|
360
|
+
} else if (ElementRef.isElementRef(renderable)) {
|
|
361
|
+
// TODO: We need to enable only setting these values once the Template has been rendered into the DOM
|
|
362
|
+
return ElementRef.set(renderable, element)
|
|
363
|
+
} else {
|
|
364
|
+
return null
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export function setupPropertiesPart(
|
|
369
|
+
{ index }: Pick<Template.PropertiesPartNode, "index">,
|
|
370
|
+
element: HTMLElement | SVGElement,
|
|
371
|
+
ctx: TemplateContext
|
|
372
|
+
) {
|
|
373
|
+
const renderable = ctx.values[index]
|
|
374
|
+
if (renderable && typeof renderable === "object") {
|
|
375
|
+
const effects: Array<Effect.Effect<void, any, any>> = []
|
|
376
|
+
const addEffect = (effect: Effect.Effect<void, any, any> | null | undefined) => {
|
|
377
|
+
if (isNullOrUndefined(effect)) return
|
|
378
|
+
effects.push(effect)
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
for (const [key, value] of Object.entries(renderable as Record<string, any>)) {
|
|
382
|
+
const [type, name] = keyToPartType(key)
|
|
383
|
+
const index = ++ctx.spreadIndex
|
|
384
|
+
switch (type) {
|
|
385
|
+
case "attr":
|
|
386
|
+
addEffect(setupAttrPart({ index, name }, element, ctx, value))
|
|
387
|
+
break
|
|
388
|
+
case "boolean":
|
|
389
|
+
addEffect(setupBooleanPart({ index, name }, element, ctx, value))
|
|
390
|
+
break
|
|
391
|
+
case "class":
|
|
392
|
+
addEffect(setupClassNamePart({ index }, element, ctx, value))
|
|
393
|
+
break
|
|
394
|
+
case "data":
|
|
395
|
+
addEffect(setupDataPart({ index }, element, ctx, value))
|
|
396
|
+
break
|
|
397
|
+
case "event":
|
|
398
|
+
addEffect(setupEventPart({ index, name }, element, ctx, value))
|
|
399
|
+
break
|
|
400
|
+
case "property":
|
|
401
|
+
addEffect(setupPropertyPart({ index, name }, element, ctx, value))
|
|
402
|
+
break
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return Effect.all(effects, { concurrency: "unbounded" })
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return null
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
export function setupSparseAttrPart(
|
|
413
|
+
{ name, nodes }: Template.SparseAttrNode,
|
|
414
|
+
element: HTMLElement | SVGElement,
|
|
415
|
+
ctx: TemplateContext
|
|
416
|
+
) {
|
|
417
|
+
ctx.expected++
|
|
418
|
+
const attr = element.getAttributeNode(name) ?? ctx.document.createAttribute(name)
|
|
419
|
+
const index = nodes.find((n): n is Template.AttrPartNode => n._tag === "attr")!.index
|
|
420
|
+
return SyncPartsInternal.handleSparseAttribute(
|
|
421
|
+
element,
|
|
422
|
+
attr,
|
|
423
|
+
nodes,
|
|
424
|
+
ctx.values,
|
|
425
|
+
(f) => Effect.zipRight(ctx.queue.add(attr, f, DEFAULT_PRIORITY), ctx.refCounter.release(index))
|
|
426
|
+
)
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
export function setupSparseClassNamePart(
|
|
430
|
+
{ nodes }: Template.SparseClassNameNode,
|
|
431
|
+
element: HTMLElement | SVGElement,
|
|
432
|
+
ctx: TemplateContext
|
|
433
|
+
) {
|
|
434
|
+
ctx.expected++
|
|
435
|
+
const index = nodes.find((n): n is Template.ClassNamePartNode => n._tag === "className-part")!.index
|
|
436
|
+
return SyncPartsInternal.handleSparseClassName(
|
|
437
|
+
element,
|
|
438
|
+
nodes,
|
|
439
|
+
ctx.values,
|
|
440
|
+
(f) => Effect.zipRight(ctx.queue.add(element.classList, f, DEFAULT_PRIORITY), ctx.refCounter.release(index))
|
|
441
|
+
)
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export function setupSparseCommentPart(
|
|
445
|
+
{ nodes }: Template.SparseCommentNode,
|
|
446
|
+
comment: Comment,
|
|
447
|
+
ctx: TemplateContext
|
|
448
|
+
) {
|
|
449
|
+
ctx.expected++
|
|
450
|
+
const index = nodes.find((n): n is Template.CommentPartNode => n._tag === "comment-part")!.index
|
|
451
|
+
return SyncPartsInternal.handleSparseComment(
|
|
452
|
+
comment,
|
|
453
|
+
nodes,
|
|
454
|
+
ctx.values,
|
|
455
|
+
(f) => Effect.zipRight(ctx.queue.add(comment, f, DEFAULT_PRIORITY), ctx.refCounter.release(index))
|
|
456
|
+
)
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
export function setupTextPart({ index }: Template.TextPartNode, comment: Comment, ctx: TemplateContext) {
|
|
460
|
+
const text = comment.previousSibling
|
|
461
|
+
? SyncPartsInternal.getPreviousTextSibling(comment.previousSibling) ?? createText(ctx.document, comment)
|
|
462
|
+
: createText(ctx.document, comment)
|
|
463
|
+
const part = SyncPartsInternal.makeTextPart(index, text)
|
|
464
|
+
const renderable = ctx.values[index]
|
|
465
|
+
return matchSyncPart(renderable, ctx, part)
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function createText(document: Document, comment: Comment) {
|
|
469
|
+
const text = document.createTextNode("")
|
|
470
|
+
comment.parentNode!.insertBefore(text, comment)
|
|
471
|
+
return text
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
export function matchSyncPart(
|
|
475
|
+
renderable: Renderable<any, any>,
|
|
476
|
+
ctx: TemplateContext,
|
|
477
|
+
syncPart: SyncPart
|
|
478
|
+
) {
|
|
479
|
+
return matchRenderable(renderable, ctx, {
|
|
480
|
+
Fx: (fx) =>
|
|
481
|
+
fx.run(Fx.Sink.make(
|
|
482
|
+
ctx.onCause,
|
|
483
|
+
(value) => runSyncUpdate(syncPart, value, ctx)
|
|
484
|
+
)),
|
|
485
|
+
Effect: (effect) => Effect.flatMap(effect, (value) => runSyncUpdate(syncPart, value, ctx)),
|
|
486
|
+
Directive: (directive) => directive(syncPartToPart(syncPart, ({ value }) => runSyncUpdate(syncPart, value, ctx))),
|
|
487
|
+
Otherwise: (value) => {
|
|
488
|
+
syncPart.update(value as never)
|
|
489
|
+
return null
|
|
490
|
+
}
|
|
491
|
+
})
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function unwrapRenderable<E, R>(renderable: unknown): Fx.Fx<any, E, R> {
|
|
495
|
+
switch (typeof renderable) {
|
|
496
|
+
case "undefined":
|
|
497
|
+
case "object": {
|
|
498
|
+
if (renderable === null || renderable === undefined) return Fx.succeed(null)
|
|
499
|
+
else if (Array.isArray(renderable)) {
|
|
500
|
+
return renderable.length === 0
|
|
501
|
+
? Fx.succeed(null)
|
|
502
|
+
// TODO: We need to ensure the ordering of these values in server environments
|
|
503
|
+
: Fx.map(Fx.tuple(renderable.map(unwrapRenderable)), (xs) => xs.flat()) as any
|
|
504
|
+
} else if (Fx.TypeId in renderable) {
|
|
505
|
+
return renderable as any
|
|
506
|
+
} else if (Effect.EffectTypeId in renderable) {
|
|
507
|
+
return Fx.fromFxEffect(Effect.map(renderable as any, unwrapRenderable<E, R>))
|
|
508
|
+
} else return Fx.succeed(renderable as any)
|
|
509
|
+
}
|
|
510
|
+
default:
|
|
511
|
+
return Fx.succeed(renderable)
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
export function runSyncUpdate(
|
|
516
|
+
syncPart: SyncPart,
|
|
517
|
+
value: any,
|
|
518
|
+
ctx: TemplateContext
|
|
519
|
+
) {
|
|
520
|
+
return Effect.zipRight(
|
|
521
|
+
ctx.queue.add(syncPart, () => syncPart.update(value as never), DEFAULT_PRIORITY),
|
|
522
|
+
ctx.refCounter.release(syncPart.index)
|
|
523
|
+
)
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export function matchRenderable(
|
|
527
|
+
renderable: Renderable<any, any>,
|
|
528
|
+
ctx: TemplateContext,
|
|
529
|
+
matches: {
|
|
530
|
+
Fx: (fx: Fx.Fx<any, any, any>) => Effect.Effect<void, any, any>
|
|
531
|
+
Effect: (effect: Effect.Effect<any, any, any>) => Effect.Effect<void, any, any>
|
|
532
|
+
Directive: (directive: Directive<any, any>) => Effect.Effect<void, any, any>
|
|
533
|
+
Otherwise: (_: any) => Effect.Effect<void, any, any> | null
|
|
534
|
+
}
|
|
535
|
+
): Effect.Effect<void, any, any> | null {
|
|
536
|
+
if (Fx.isFx(renderable)) {
|
|
537
|
+
ctx.expected++
|
|
538
|
+
return matches.Fx(renderable)
|
|
539
|
+
} else if (Effect.isEffect(renderable)) {
|
|
540
|
+
ctx.expected++
|
|
541
|
+
return matches.Effect(renderable)
|
|
542
|
+
} else if (isDirective<any, any>(renderable)) {
|
|
543
|
+
ctx.expected++
|
|
544
|
+
return matches.Directive(renderable)
|
|
545
|
+
} else if (Array.isArray(renderable)) {
|
|
546
|
+
return matches.Fx(unwrapRenderable(renderable))
|
|
547
|
+
} else {
|
|
548
|
+
return matches.Otherwise(renderable)
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
export function attachRoot<T extends RenderEvent | null>(
|
|
553
|
+
cache: RenderContext["renderCache"],
|
|
554
|
+
where: HTMLElement,
|
|
555
|
+
what: RenderEvent | null // TODO: Should we support HTML RenderEvents here too?,
|
|
556
|
+
): Effect.Effect<ToRendered<T>> {
|
|
557
|
+
return Effect.sync(() => {
|
|
558
|
+
const wire = what?.valueOf() as ToRendered<T>
|
|
559
|
+
const previous = cache.get(where)
|
|
560
|
+
|
|
561
|
+
if (wire !== previous) {
|
|
562
|
+
if (previous && !wire) removeChildren(where, previous)
|
|
563
|
+
|
|
564
|
+
cache.set(where, wire || null)
|
|
565
|
+
|
|
566
|
+
if (wire) replaceChildren(where, wire)
|
|
567
|
+
|
|
568
|
+
return wire as ToRendered<T>
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
return previous as ToRendered<T>
|
|
572
|
+
})
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
export function removeChildren(where: HTMLElement, previous: Rendered) {
|
|
576
|
+
for (const node of getNodes(previous)) {
|
|
577
|
+
where.removeChild(node)
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
export function replaceChildren(where: HTMLElement, wire: Rendered) {
|
|
582
|
+
where.replaceChildren(...getNodes(wire))
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
export function getNodes(rendered: Rendered): Array<globalThis.Node> {
|
|
586
|
+
const value = rendered.valueOf() as globalThis.Node | Array<globalThis.Node>
|
|
587
|
+
return Array.isArray(value) ? value : [value]
|
|
588
|
+
}
|