@typed/template 0.9.6 → 0.11.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 +15 -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 +20 -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 +20 -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 +50 -12
- package/src/Html.ts +207 -98
- 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
package/src/RenderContext.ts
CHANGED
|
@@ -9,16 +9,11 @@ import { GlobalThis } from "@typed/dom/GlobalThis"
|
|
|
9
9
|
import { Window } from "@typed/dom/Window"
|
|
10
10
|
import type { Environment } from "@typed/environment"
|
|
11
11
|
import { CurrentEnvironment } from "@typed/environment"
|
|
12
|
-
import * as Idle from "@typed/fx/Idle"
|
|
13
12
|
import type { Rendered } from "@typed/wire"
|
|
14
|
-
import * as Effect from "effect/Effect"
|
|
15
13
|
import * as Layer from "effect/Layer"
|
|
16
14
|
import * as Option from "effect/Option"
|
|
17
|
-
import * as Scope from "effect/Scope"
|
|
18
15
|
import type { Entry } from "./Entry.js"
|
|
19
16
|
|
|
20
|
-
// TODO: We should probably have a more explicit environment type between DOM/HTML rendering
|
|
21
|
-
|
|
22
17
|
/**
|
|
23
18
|
* The context in which templates are rendered within
|
|
24
19
|
* @since 1.0.0
|
|
@@ -38,11 +33,6 @@ export interface RenderContext {
|
|
|
38
33
|
* Cache for individual templates.
|
|
39
34
|
*/
|
|
40
35
|
readonly templateCache: WeakMap<TemplateStringsArray, Entry>
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Queue for work to be batched
|
|
44
|
-
*/
|
|
45
|
-
readonly queue: RenderQueue
|
|
46
36
|
}
|
|
47
37
|
|
|
48
38
|
/**
|
|
@@ -50,44 +40,26 @@ export interface RenderContext {
|
|
|
50
40
|
* @since 1.0.0
|
|
51
41
|
*/
|
|
52
42
|
export const RenderContext: Context.Tagged<RenderContext, RenderContext> = Context.Tagged<RenderContext>(
|
|
53
|
-
"
|
|
43
|
+
"@typed/template/RenderContext"
|
|
54
44
|
)
|
|
55
45
|
|
|
56
46
|
/**
|
|
57
47
|
* @since 1.0.0
|
|
58
48
|
*/
|
|
59
|
-
export
|
|
60
|
-
readonly add: (part: unknown, task: () => void) => Effect.Effect<void, never, Scope.Scope>
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* @since 1.0.0
|
|
65
|
-
*/
|
|
66
|
-
export type RenderContextOptions = IdleRequestOptions & {
|
|
49
|
+
export type RenderContextOptions = {
|
|
67
50
|
readonly environment: Environment
|
|
68
|
-
readonly scope: Scope.Scope
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* @since 1.0.0
|
|
73
|
-
*/
|
|
74
|
-
export function make({ ...options }: Omit<RenderContextOptions, "scope">, skipRenderScheduling?: boolean) {
|
|
75
|
-
return Effect.scopeWith((scope) => Effect.succeed(unsafeMake({ ...options, scope }, skipRenderScheduling)))
|
|
76
51
|
}
|
|
77
52
|
|
|
78
53
|
/**
|
|
79
54
|
* @since 1.0.0
|
|
80
55
|
*/
|
|
81
|
-
export function
|
|
82
|
-
environment
|
|
83
|
-
|
|
84
|
-
...options
|
|
85
|
-
}: RenderContextOptions, skipRenderScheduling?: boolean): RenderContext {
|
|
56
|
+
export function make({
|
|
57
|
+
environment
|
|
58
|
+
}: RenderContextOptions): RenderContext {
|
|
86
59
|
return {
|
|
87
60
|
environment,
|
|
88
61
|
renderCache: new WeakMap(),
|
|
89
|
-
templateCache: new WeakMap()
|
|
90
|
-
queue: new RenderQueueImpl(scope, options, skipRenderScheduling ?? false)
|
|
62
|
+
templateCache: new WeakMap()
|
|
91
63
|
}
|
|
92
64
|
}
|
|
93
65
|
|
|
@@ -108,9 +80,9 @@ export function getTemplateCache(
|
|
|
108
80
|
return Option.fromNullable(templateCache.get(key))
|
|
109
81
|
}
|
|
110
82
|
|
|
111
|
-
const buildWithCurrentEnvironment = (environment: Environment
|
|
83
|
+
const buildWithCurrentEnvironment = (environment: Environment) =>
|
|
112
84
|
Layer.mergeAll(
|
|
113
|
-
RenderContext.
|
|
85
|
+
RenderContext.layer(make({ environment })),
|
|
114
86
|
CurrentEnvironment.layer(environment)
|
|
115
87
|
)
|
|
116
88
|
|
|
@@ -119,14 +91,14 @@ const buildWithCurrentEnvironment = (environment: Environment, skipRenderSchedul
|
|
|
119
91
|
*/
|
|
120
92
|
export const dom: (
|
|
121
93
|
window: Window & GlobalThis,
|
|
122
|
-
options?: DomServicesElementParams
|
|
123
|
-
) => Layer.Layer<RenderContext | CurrentEnvironment | DomServices> = (
|
|
94
|
+
options?: DomServicesElementParams
|
|
95
|
+
) => Layer.Layer<RenderContext | CurrentEnvironment | DomServices> = (
|
|
96
|
+
window,
|
|
97
|
+
options
|
|
98
|
+
) =>
|
|
124
99
|
Layer.provideMerge(
|
|
125
100
|
Layer.mergeAll(
|
|
126
|
-
buildWithCurrentEnvironment(
|
|
127
|
-
"dom",
|
|
128
|
-
options?.skipRenderScheduling
|
|
129
|
-
),
|
|
101
|
+
buildWithCurrentEnvironment("dom"),
|
|
130
102
|
domServices(options)
|
|
131
103
|
),
|
|
132
104
|
Layer.mergeAll(Window.layer(window), GlobalThis.layer(window))
|
|
@@ -147,103 +119,3 @@ export {
|
|
|
147
119
|
*/
|
|
148
120
|
static_ as static
|
|
149
121
|
}
|
|
150
|
-
|
|
151
|
-
class RenderQueueImpl implements RenderQueue {
|
|
152
|
-
queue = new Map<unknown, () => void>()
|
|
153
|
-
scheduled = false
|
|
154
|
-
run: Effect.Effect<void, never, Scope.Scope>
|
|
155
|
-
|
|
156
|
-
constructor(
|
|
157
|
-
readonly scope: Scope.Scope,
|
|
158
|
-
readonly options?: IdleRequestOptions,
|
|
159
|
-
readonly skipRenderScheduling: boolean = false
|
|
160
|
-
) {
|
|
161
|
-
this.add.bind(this)
|
|
162
|
-
|
|
163
|
-
this.run = typeof requestAnimationFrame === "undefined" ? this.runIdle : this.runAnimationFrame
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
add(part: unknown, task: () => void) {
|
|
167
|
-
if (this.skipRenderScheduling) return Effect.sync(task)
|
|
168
|
-
|
|
169
|
-
return Effect.suspend(() => {
|
|
170
|
-
this.queue.set(part, task)
|
|
171
|
-
|
|
172
|
-
return Effect.zipRight(
|
|
173
|
-
Effect.addFinalizer(() =>
|
|
174
|
-
Effect.sync(() => {
|
|
175
|
-
const currentTask = this.queue.get(part)
|
|
176
|
-
|
|
177
|
-
// If the current task is still the same we'll delete it from the queue
|
|
178
|
-
if (currentTask === task) {
|
|
179
|
-
this.queue.delete(part)
|
|
180
|
-
}
|
|
181
|
-
})
|
|
182
|
-
),
|
|
183
|
-
this.scheduleNextRun
|
|
184
|
-
)
|
|
185
|
-
})
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
scheduleNextRun = Effect.suspend(() => {
|
|
189
|
-
if (this.queue.size === 0 || this.scheduled) return Effect.unit
|
|
190
|
-
|
|
191
|
-
this.scheduled = true
|
|
192
|
-
|
|
193
|
-
return this.run.pipe(
|
|
194
|
-
Scope.extend(this.scope),
|
|
195
|
-
Effect.forkIn(this.scope)
|
|
196
|
-
)
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
runIdle: Effect.Effect<void, never, Scope.Scope> = Effect.suspend(() => {
|
|
200
|
-
return Effect.flatMap(
|
|
201
|
-
Idle.whenIdle(this.options),
|
|
202
|
-
(deadline) =>
|
|
203
|
-
Effect.suspend(() => {
|
|
204
|
-
const iterator = this.queue.entries()
|
|
205
|
-
|
|
206
|
-
while (Idle.shouldContinue(deadline) && this.runTask(iterator)) {
|
|
207
|
-
// Continue
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// If we have more work to do, schedule another run
|
|
211
|
-
if (this.queue.size > 0) {
|
|
212
|
-
return this.runIdle
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
this.scheduled = false
|
|
216
|
-
|
|
217
|
-
return Effect.unit
|
|
218
|
-
})
|
|
219
|
-
)
|
|
220
|
-
})
|
|
221
|
-
|
|
222
|
-
runAnimationFrame: Effect.Effect<void, never, Scope.Scope> = Effect.zipRight(
|
|
223
|
-
Effect.async<void>((cb) => {
|
|
224
|
-
const id = requestAnimationFrame(() => cb(Effect.unit))
|
|
225
|
-
return Effect.sync(() => cancelAnimationFrame(id))
|
|
226
|
-
}),
|
|
227
|
-
Effect.sync(() => {
|
|
228
|
-
const iterator = this.queue.entries()
|
|
229
|
-
|
|
230
|
-
while (this.runTask(iterator)) {
|
|
231
|
-
// Continue
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
this.scheduled = false
|
|
235
|
-
})
|
|
236
|
-
)
|
|
237
|
-
|
|
238
|
-
private runTask = (iterator: Iterator<[unknown, () => void]>) => {
|
|
239
|
-
const result = iterator.next()
|
|
240
|
-
|
|
241
|
-
if (result.done) return false
|
|
242
|
-
else {
|
|
243
|
-
const [part, task] = result.value
|
|
244
|
-
this.queue.delete(part)
|
|
245
|
-
task()
|
|
246
|
-
return true
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
package/src/RenderEvent.ts
CHANGED
|
@@ -35,16 +35,18 @@ export function DomRenderEvent(rendered: Rendered): DomRenderEvent {
|
|
|
35
35
|
export type HtmlRenderEvent = {
|
|
36
36
|
readonly _tag: "html"
|
|
37
37
|
readonly html: string
|
|
38
|
+
readonly done: boolean
|
|
38
39
|
readonly valueOf: () => string
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
43
|
* @since 1.0.0
|
|
43
44
|
*/
|
|
44
|
-
export function HtmlRenderEvent(html: string): HtmlRenderEvent {
|
|
45
|
+
export function HtmlRenderEvent(html: string, done: boolean): HtmlRenderEvent {
|
|
45
46
|
return {
|
|
46
47
|
_tag: "html",
|
|
47
48
|
html,
|
|
49
|
+
done,
|
|
48
50
|
valueOf: () => html
|
|
49
51
|
}
|
|
50
52
|
}
|
|
@@ -56,6 +58,13 @@ export function isRenderEvent(value: unknown): value is RenderEvent {
|
|
|
56
58
|
return isTaggedObject(value) && (value._tag === "html" || value._tag === "dom")
|
|
57
59
|
}
|
|
58
60
|
|
|
61
|
+
/**
|
|
62
|
+
* @since 1.0.0
|
|
63
|
+
*/
|
|
64
|
+
export function isHtmlRenderEvent(value: unknown): value is HtmlRenderEvent {
|
|
65
|
+
return isTaggedObject(value) && value._tag === "html"
|
|
66
|
+
}
|
|
67
|
+
|
|
59
68
|
function isTaggedObject(
|
|
60
69
|
value: unknown
|
|
61
70
|
): value is Record<string, unknown> & { readonly _tag: unknown } {
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The context in which templates are rendered within
|
|
3
|
+
* @since 1.0.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as Context from "@typed/context"
|
|
7
|
+
import * as Idle from "@typed/fx/Idle"
|
|
8
|
+
import * as Effect from "effect/Effect"
|
|
9
|
+
import * as FiberRef from "effect/FiberRef"
|
|
10
|
+
import { dual } from "effect/Function"
|
|
11
|
+
import type * as Layer from "effect/Layer"
|
|
12
|
+
import * as Scope from "effect/Scope"
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @since 1.0.0
|
|
16
|
+
*/
|
|
17
|
+
export const DEFAULT_PRIORITY = 10
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The context in which templates are rendered within
|
|
21
|
+
* @since 1.0.0
|
|
22
|
+
*/
|
|
23
|
+
export const RenderQueue: Context.Tagged<RenderQueue, RenderQueue> = Context.Tagged<RenderQueue>(
|
|
24
|
+
"@typed/template/RenderQueue"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @since 1.0.0
|
|
29
|
+
*/
|
|
30
|
+
export const currentPriority: FiberRef.FiberRef<number> = FiberRef.unsafeMake(DEFAULT_PRIORITY)
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @since 1.0.0
|
|
34
|
+
*/
|
|
35
|
+
export interface RenderQueue {
|
|
36
|
+
readonly add: (part: unknown, task: () => void, priority: number) => Effect.Effect<void, never, Scope.Scope>
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @since 1.0.0
|
|
41
|
+
*/
|
|
42
|
+
export interface IdleRenderQueueOptions extends IdleRequestOptions {
|
|
43
|
+
readonly scope: Scope.Scope
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @since 1.0.0
|
|
48
|
+
*/
|
|
49
|
+
export const unsafeMakeIdleRenderQueue = ({ scope, ...options }: IdleRenderQueueOptions): RenderQueue =>
|
|
50
|
+
new IdleImpl(scope, options)
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @since 1.0.0
|
|
54
|
+
*/
|
|
55
|
+
export const unsafeMakeRafRenderQueue = (scope: Scope.Scope): RenderQueue => new RafImpl(scope)
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @since 1.0.0
|
|
59
|
+
*/
|
|
60
|
+
export const unsafeMakeMicrotaskRenderQueue = (scope: Scope.Scope): RenderQueue => new MicroTaskImpl(scope)
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @since 1.0.0
|
|
64
|
+
*/
|
|
65
|
+
export const unsafeMakeSyncRenderQueue = (): RenderQueue => new SyncImpl()
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @since 1.0.0
|
|
69
|
+
*/
|
|
70
|
+
export const idle = (options?: IdleRequestOptions): Layer.Layer<RenderQueue> =>
|
|
71
|
+
RenderQueue.scoped(
|
|
72
|
+
Effect.scopeWith((scope) => Effect.succeed(unsafeMakeIdleRenderQueue({ scope, ...options })))
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @since 1.0.0
|
|
77
|
+
*/
|
|
78
|
+
export const raf: Layer.Layer<RenderQueue> = RenderQueue.scoped(
|
|
79
|
+
Effect.scopeWith((scope) => Effect.succeed(unsafeMakeRafRenderQueue(scope)))
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @since 1.0.0
|
|
84
|
+
*/
|
|
85
|
+
export const microtask: Layer.Layer<RenderQueue> = RenderQueue.scoped(
|
|
86
|
+
Effect.scopeWith((scope) => Effect.succeed(unsafeMakeMicrotaskRenderQueue(scope)))
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @since 1.0.0
|
|
91
|
+
*/
|
|
92
|
+
export const sync: Layer.Layer<RenderQueue> = RenderQueue.layer(Effect.sync(unsafeMakeSyncRenderQueue))
|
|
93
|
+
|
|
94
|
+
const MICRO_TASK_END = DEFAULT_PRIORITY - 1
|
|
95
|
+
const RAF_END = DEFAULT_PRIORITY + 9
|
|
96
|
+
const IDLE_START = RAF_END + 1
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @since 1.0.0
|
|
100
|
+
*/
|
|
101
|
+
export const mixed = (options?: IdleRequestOptions): Layer.Layer<RenderQueue> =>
|
|
102
|
+
RenderQueue.scoped(Effect.gen(function*() {
|
|
103
|
+
const scope = yield* Effect.scope
|
|
104
|
+
const queues: Array<readonly [priorityRange: readonly [number, number], RenderQueue]> = [
|
|
105
|
+
[[-1, -1], new SyncImpl()],
|
|
106
|
+
[[0, MICRO_TASK_END], new MicroTaskImpl(scope)],
|
|
107
|
+
[[DEFAULT_PRIORITY, RAF_END], new RafImpl(scope)],
|
|
108
|
+
[[IDLE_START, Number.MAX_SAFE_INTEGER], new IdleImpl(scope, options)]
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
return new MixedImpl(queues)
|
|
112
|
+
}))
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @since 1.0.0
|
|
116
|
+
*/
|
|
117
|
+
export const Priority = {
|
|
118
|
+
Sync: -1,
|
|
119
|
+
/**
|
|
120
|
+
* @example
|
|
121
|
+
* RenderPriority.MicroTask(0-9)
|
|
122
|
+
*/
|
|
123
|
+
MicroTask: (priority: number) => Math.min(Math.max(0, priority), MICRO_TASK_END),
|
|
124
|
+
/**
|
|
125
|
+
* @example
|
|
126
|
+
* RenderPriority.Raf(0-9)
|
|
127
|
+
*/
|
|
128
|
+
Raf: (priority: number) => Math.min(Math.max(DEFAULT_PRIORITY, DEFAULT_PRIORITY + priority), RAF_END),
|
|
129
|
+
/**
|
|
130
|
+
* @example
|
|
131
|
+
* RenderPriority.Idle(0-9)
|
|
132
|
+
*/
|
|
133
|
+
Idle: (priority: number) => Math.max(IDLE_START, IDLE_START + priority)
|
|
134
|
+
} as const
|
|
135
|
+
|
|
136
|
+
class PriorityQueue {
|
|
137
|
+
tasks: Map<unknown, () => void> = new Map()
|
|
138
|
+
priority: Map<unknown, number> = new Map<unknown, number>()
|
|
139
|
+
priorities: Map<number, Set<unknown>> = new Map()
|
|
140
|
+
|
|
141
|
+
add(part: unknown, task: () => void, priority: number) {
|
|
142
|
+
this.tasks.set(part, task)
|
|
143
|
+
this.setPriority(part, priority)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
get(part: unknown) {
|
|
147
|
+
return this.tasks.get(part)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
delete(part: unknown) {
|
|
151
|
+
this.tasks.delete(part)
|
|
152
|
+
const priority = this.priority.get(part)
|
|
153
|
+
if (priority === undefined) {
|
|
154
|
+
return false
|
|
155
|
+
} else {
|
|
156
|
+
this.priorities.get(priority)?.delete(part)
|
|
157
|
+
this.priority.delete(part)
|
|
158
|
+
return true
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
get isEmpty() {
|
|
163
|
+
return this.priorities.size === 0
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
*entries() {
|
|
167
|
+
for (const priority of Array.from(this.priorities.keys()).sort((a, b) => a - b)) {
|
|
168
|
+
const parts = this.priorities.get(priority)!
|
|
169
|
+
this.priorities.delete(priority)
|
|
170
|
+
yield this.getTasks(parts)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
private getTasks(parts: Set<unknown>) {
|
|
175
|
+
return Array.from(parts.values()).flatMap((part) => {
|
|
176
|
+
const task = this.tasks.get(part)
|
|
177
|
+
if (task === undefined) return []
|
|
178
|
+
this.tasks.delete(part)
|
|
179
|
+
this.priority.delete(part)
|
|
180
|
+
return [task]
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private setPriority(task: unknown, priority: number) {
|
|
185
|
+
const current = this.priority.get(task)
|
|
186
|
+
if (current === undefined) {
|
|
187
|
+
this.priority.set(task, priority)
|
|
188
|
+
this.addTaskToPriority(task, priority)
|
|
189
|
+
return priority
|
|
190
|
+
} else if (priority < current) {
|
|
191
|
+
this.priorities.get(current)?.delete(task)
|
|
192
|
+
this.priority.set(task, priority)
|
|
193
|
+
this.addTaskToPriority(task, priority)
|
|
194
|
+
return priority
|
|
195
|
+
} else {
|
|
196
|
+
return current
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private addTaskToPriority(task: unknown, priority: number) {
|
|
201
|
+
let set = this.priorities.get(priority)
|
|
202
|
+
if (set === undefined) {
|
|
203
|
+
set = new Set()
|
|
204
|
+
this.priorities.set(priority, set)
|
|
205
|
+
}
|
|
206
|
+
set.add(task)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
abstract class BaseImpl implements RenderQueue {
|
|
211
|
+
queue = new PriorityQueue()
|
|
212
|
+
scheduled = false
|
|
213
|
+
|
|
214
|
+
constructor(readonly scope: Scope.Scope) {
|
|
215
|
+
this.add.bind(this)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
add(part: unknown, task: () => void, priority: number) {
|
|
219
|
+
return Effect.suspend(() => {
|
|
220
|
+
this.queue.add(part, task, priority)
|
|
221
|
+
|
|
222
|
+
return Effect.zipRight(
|
|
223
|
+
Effect.addFinalizer(() =>
|
|
224
|
+
Effect.sync(() => {
|
|
225
|
+
const currentTask = this.queue.get(part)
|
|
226
|
+
// If the current task is still the same we'll delete it from the queue
|
|
227
|
+
if (currentTask === task) {
|
|
228
|
+
this.queue.delete(part)
|
|
229
|
+
}
|
|
230
|
+
})
|
|
231
|
+
),
|
|
232
|
+
this.scheduleNextRun
|
|
233
|
+
)
|
|
234
|
+
})
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
scheduleNextRun = Effect.suspend(() => {
|
|
238
|
+
if (this.queue.isEmpty || this.scheduled) return Effect.void
|
|
239
|
+
|
|
240
|
+
this.scheduled = true
|
|
241
|
+
|
|
242
|
+
return this.run.pipe(
|
|
243
|
+
Scope.extend(this.scope),
|
|
244
|
+
Effect.forkIn(this.scope)
|
|
245
|
+
)
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
abstract run: Effect.Effect<void, never, Scope.Scope>
|
|
249
|
+
|
|
250
|
+
protected runTasks = (iterator: Iterator<Array<() => void>>) => {
|
|
251
|
+
const result = iterator.next()
|
|
252
|
+
|
|
253
|
+
if (result.done) return false
|
|
254
|
+
else {
|
|
255
|
+
for (const task of result.value) {
|
|
256
|
+
this.tryRunTask(task)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return true
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
protected tryRunTask = (task: () => void) => {
|
|
264
|
+
try {
|
|
265
|
+
task()
|
|
266
|
+
} catch (error) {
|
|
267
|
+
// TODO: We should probably be able to report this back to a template
|
|
268
|
+
console.error(error)
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
class IdleImpl extends BaseImpl implements RenderQueue {
|
|
274
|
+
constructor(
|
|
275
|
+
scope: Scope.Scope,
|
|
276
|
+
readonly options?: IdleRequestOptions
|
|
277
|
+
) {
|
|
278
|
+
super(scope)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
run: Effect.Effect<void, never, Scope.Scope> = Effect.suspend(() => {
|
|
282
|
+
return Effect.flatMap(
|
|
283
|
+
Idle.whenIdle(this.options),
|
|
284
|
+
(deadline) =>
|
|
285
|
+
Effect.suspend(() => {
|
|
286
|
+
const iterator = this.queue.entries()
|
|
287
|
+
|
|
288
|
+
while (Idle.shouldContinue(deadline) && this.runTasks(iterator)) {
|
|
289
|
+
// Continue
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// If we have more work to do, schedule another run
|
|
293
|
+
if (!this.queue.isEmpty) {
|
|
294
|
+
return this.run
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
this.scheduled = false
|
|
298
|
+
|
|
299
|
+
return Effect.void
|
|
300
|
+
})
|
|
301
|
+
)
|
|
302
|
+
})
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
class RafImpl extends BaseImpl implements RenderQueue {
|
|
306
|
+
private _set: ((callback: FrameRequestCallback) => number) | typeof setTimeout
|
|
307
|
+
private _clear: (handle: number) => void
|
|
308
|
+
|
|
309
|
+
constructor(
|
|
310
|
+
readonly scope: Scope.Scope
|
|
311
|
+
) {
|
|
312
|
+
super(scope)
|
|
313
|
+
|
|
314
|
+
const [set, clear] = typeof globalThis.requestAnimationFrame === "function"
|
|
315
|
+
? [requestAnimationFrame.bind(globalThis), cancelAnimationFrame.bind(globalThis)]
|
|
316
|
+
: [setTimeout, clearTimeout]
|
|
317
|
+
this._set = set
|
|
318
|
+
this._clear = clear
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
run: Effect.Effect<void> = Effect.async((cb) => {
|
|
322
|
+
const id = this._set(() => {
|
|
323
|
+
this.runAllTasks()
|
|
324
|
+
return cb(Effect.void)
|
|
325
|
+
})
|
|
326
|
+
return Effect.sync(() => {
|
|
327
|
+
this.scheduled = false
|
|
328
|
+
this._clear(id)
|
|
329
|
+
})
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
private runAllTasks = () => {
|
|
333
|
+
const iterator = this.queue.entries()
|
|
334
|
+
while (this.runTasks(iterator)) {
|
|
335
|
+
// Continue
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
this.scheduled = false
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const noOp = () => void 0
|
|
343
|
+
|
|
344
|
+
class MicroTaskImpl extends BaseImpl implements RenderQueue {
|
|
345
|
+
private _set: { (callback: VoidFunction): void; (callback: () => void): void } | typeof setTimeout
|
|
346
|
+
private _clear:
|
|
347
|
+
| { (id: number | undefined): void; (timeoutId: string | number | NodeJS.Timeout | undefined): void }
|
|
348
|
+
| (() => undefined)
|
|
349
|
+
|
|
350
|
+
constructor(
|
|
351
|
+
readonly scope: Scope.Scope
|
|
352
|
+
) {
|
|
353
|
+
super(scope)
|
|
354
|
+
|
|
355
|
+
const [set, clear] = typeof globalThis.queueMicrotask === "function"
|
|
356
|
+
? [globalThis.queueMicrotask.bind(globalThis), noOp]
|
|
357
|
+
: [setTimeout, clearTimeout]
|
|
358
|
+
this._set = set
|
|
359
|
+
this._clear = clear
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
run: Effect.Effect<void> = Effect.async((cb) => {
|
|
363
|
+
const id = this._set(() => {
|
|
364
|
+
this.runAllTasks()
|
|
365
|
+
return cb(Effect.void)
|
|
366
|
+
})
|
|
367
|
+
return Effect.sync(() => {
|
|
368
|
+
this.scheduled = false
|
|
369
|
+
id && this._clear(id)
|
|
370
|
+
})
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
private runAllTasks = () => {
|
|
374
|
+
const iterator = this.queue.entries()
|
|
375
|
+
while (this.runTasks(iterator)) {
|
|
376
|
+
// Continue
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
this.scheduled = false
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
class SyncImpl implements RenderQueue {
|
|
384
|
+
constructor() {
|
|
385
|
+
this.add.bind(this)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
add(_: unknown, task: () => void) {
|
|
389
|
+
return Effect.sync(task)
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
class MixedImpl implements RenderQueue {
|
|
394
|
+
private _priorityCache = new Map<number, RenderQueue>()
|
|
395
|
+
|
|
396
|
+
constructor(
|
|
397
|
+
readonly queues: Array<readonly [priorityRange: readonly [number, number], RenderQueue]>
|
|
398
|
+
) {
|
|
399
|
+
this.queues.sort(([a], [b]) => a[0] - b[0])
|
|
400
|
+
|
|
401
|
+
this.add.bind(this)
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
add(part: unknown, task: () => void, priority: number) {
|
|
405
|
+
let queue = this.getQueueForPriority(priority)
|
|
406
|
+
|
|
407
|
+
if (queue === undefined) {
|
|
408
|
+
queue = this.queues[this.queues.length - 1][1]
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return queue.add(part, task, priority)
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
private getQueueForPriority(priority: number) {
|
|
415
|
+
if (this._priorityCache.has(priority)) {
|
|
416
|
+
return this._priorityCache.get(priority)!
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const q = this.queues.find(([range]) => priority >= range[0] && priority <= range[1])
|
|
420
|
+
|
|
421
|
+
if (q) {
|
|
422
|
+
this._priorityCache.set(priority, q[1])
|
|
423
|
+
return q[1]
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return q
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* @since 1.0.0
|
|
432
|
+
*/
|
|
433
|
+
export const withCurrentPriority = <A, E, R>(f: (priority: number) => Effect.Effect<A, E, R>) =>
|
|
434
|
+
Effect.flatMap(FiberRef.get(currentPriority), f)
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* @since 1.0.0
|
|
438
|
+
*/
|
|
439
|
+
export const usingCurrentPriority: {
|
|
440
|
+
(priority: number): <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
|
|
441
|
+
<A, E, R>(effect: Effect.Effect<A, E, R>, priority: number): Effect.Effect<A, E, R>
|
|
442
|
+
} = dual(
|
|
443
|
+
2,
|
|
444
|
+
<A, E, R>(effect: Effect.Effect<A, E, R>, priority: number) => Effect.locally(effect, currentPriority, priority)
|
|
445
|
+
)
|
package/src/RenderTemplate.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type { Scope } from "effect/Scope"
|
|
|
8
8
|
import type { Placeholder } from "./Placeholder.js"
|
|
9
9
|
import type { Renderable } from "./Renderable.js"
|
|
10
10
|
import type { RenderEvent } from "./RenderEvent.js"
|
|
11
|
+
import type { RenderQueue } from "./RenderQueue.js"
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* @since 1.0.0
|
|
@@ -16,7 +17,7 @@ export interface RenderTemplate {
|
|
|
16
17
|
<Values extends ReadonlyArray<Renderable<any, any>>>(
|
|
17
18
|
templateStrings: TemplateStringsArray,
|
|
18
19
|
values: Values
|
|
19
|
-
): Fx.Fx<RenderEvent, Placeholder.Error<Values[number]>, Scope | Placeholder.Context<Values[number]>>
|
|
20
|
+
): Fx.Fx<RenderEvent, Placeholder.Error<Values[number]>, Scope | RenderQueue | Placeholder.Context<Values[number]>>
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
/**
|
|
@@ -34,6 +35,10 @@ export const RenderTemplate: Context.Tagged<RenderTemplate, RenderTemplate> = Co
|
|
|
34
35
|
export function html<const Values extends ReadonlyArray<Renderable<any, any>>>(
|
|
35
36
|
template: TemplateStringsArray,
|
|
36
37
|
...values: Values
|
|
37
|
-
): Fx.Fx<
|
|
38
|
+
): Fx.Fx<
|
|
39
|
+
RenderEvent,
|
|
40
|
+
Placeholder.Error<Values[number]>,
|
|
41
|
+
RenderTemplate | RenderQueue | Scope | Placeholder.Context<Values[number]>
|
|
42
|
+
> {
|
|
38
43
|
return Fx.fromFxEffect(RenderTemplate.with((render) => render(template, values)))
|
|
39
44
|
}
|