@typed/template 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Directive.js +1 -1
- package/dist/cjs/Directive.js.map +1 -1
- package/dist/cjs/ElementRef.js +23 -13
- package/dist/cjs/ElementRef.js.map +1 -1
- package/dist/cjs/ElementSource.js +16 -18
- package/dist/cjs/ElementSource.js.map +1 -1
- package/dist/cjs/EventHandler.js +1 -1
- package/dist/cjs/EventHandler.js.map +1 -1
- package/dist/cjs/Html.js +31 -32
- package/dist/cjs/Html.js.map +1 -1
- package/dist/cjs/HtmlChunk.js +4 -1
- package/dist/cjs/HtmlChunk.js.map +1 -1
- package/dist/cjs/Hydrate.js +1 -1
- package/dist/cjs/Hydrate.js.map +1 -1
- package/dist/cjs/Many.js +14 -13
- package/dist/cjs/Many.js.map +1 -1
- package/dist/cjs/Parser.js +11 -323
- package/dist/cjs/Parser.js.map +1 -1
- package/dist/cjs/Placeholder.js +3 -3
- package/dist/cjs/Placeholder.js.map +1 -1
- package/dist/cjs/Platform.js +4 -4
- package/dist/cjs/Platform.js.map +1 -1
- package/dist/cjs/Render.js +1 -1
- package/dist/cjs/Render.js.map +1 -1
- package/dist/cjs/RenderContext.js +48 -27
- package/dist/cjs/RenderContext.js.map +1 -1
- package/dist/cjs/RenderTemplate.js +2 -17
- package/dist/cjs/RenderTemplate.js.map +1 -1
- package/dist/cjs/Template.js +27 -1
- package/dist/cjs/Template.js.map +1 -1
- package/dist/cjs/TemplateInstance.js +2 -2
- package/dist/cjs/TemplateInstance.js.map +1 -1
- package/dist/cjs/Test.js +20 -7
- package/dist/cjs/Test.js.map +1 -1
- package/dist/cjs/index.js +0 -12
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/internal/EventSource.js +95 -0
- package/dist/cjs/internal/EventSource.js.map +1 -0
- package/dist/cjs/internal/browser.js +11 -11
- package/dist/cjs/internal/browser.js.map +1 -1
- package/dist/cjs/internal/hydrate.js +49 -50
- package/dist/cjs/internal/hydrate.js.map +1 -1
- package/dist/cjs/internal/indexRefCounter.js +49 -2
- package/dist/cjs/internal/indexRefCounter.js.map +1 -1
- package/dist/cjs/internal/parser.js +60 -17
- package/dist/cjs/internal/parser.js.map +1 -1
- package/dist/cjs/internal/parts.js +128 -28
- package/dist/cjs/internal/parts.js.map +1 -1
- package/dist/cjs/internal/render.js +486 -53
- package/dist/cjs/internal/render.js.map +1 -1
- package/dist/cjs/internal/server.js +5 -2
- package/dist/cjs/internal/server.js.map +1 -1
- package/dist/dts/Directive.d.ts.map +1 -1
- package/dist/dts/ElementRef.d.ts +4 -2
- package/dist/dts/ElementRef.d.ts.map +1 -1
- package/dist/dts/ElementSource.d.ts +10 -5
- package/dist/dts/ElementSource.d.ts.map +1 -1
- package/dist/dts/EventHandler.d.ts.map +1 -1
- package/dist/dts/Html.d.ts +1 -1
- package/dist/dts/Html.d.ts.map +1 -1
- package/dist/dts/HtmlChunk.d.ts.map +1 -1
- package/dist/dts/Many.d.ts +13 -11
- package/dist/dts/Many.d.ts.map +1 -1
- package/dist/dts/Parser.d.ts +3 -6
- package/dist/dts/Parser.d.ts.map +1 -1
- package/dist/dts/Part.d.ts +13 -3
- package/dist/dts/Part.d.ts.map +1 -1
- package/dist/dts/Placeholder.d.ts +2 -2
- package/dist/dts/Placeholder.d.ts.map +1 -1
- package/dist/dts/Render.d.ts +2 -1
- package/dist/dts/Render.d.ts.map +1 -1
- package/dist/dts/RenderContext.d.ts +5 -4
- package/dist/dts/RenderContext.d.ts.map +1 -1
- package/dist/dts/RenderTemplate.d.ts +2 -16
- package/dist/dts/RenderTemplate.d.ts.map +1 -1
- package/dist/dts/Renderable.d.ts +2 -2
- package/dist/dts/Renderable.d.ts.map +1 -1
- package/dist/dts/Template.d.ts +21 -3
- package/dist/dts/Template.d.ts.map +1 -1
- package/dist/dts/TemplateInstance.d.ts +3 -2
- package/dist/dts/TemplateInstance.d.ts.map +1 -1
- package/dist/dts/Test.d.ts +4 -6
- package/dist/dts/Test.d.ts.map +1 -1
- package/dist/dts/index.d.ts +0 -4
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/internal/EventSource.d.ts +12 -0
- package/dist/dts/internal/EventSource.d.ts.map +1 -0
- package/dist/dts/internal/browser.d.ts.map +1 -1
- package/dist/dts/internal/hydrate.d.ts +5 -5
- package/dist/dts/internal/hydrate.d.ts.map +1 -1
- package/dist/dts/internal/indexRefCounter.d.ts +5 -0
- package/dist/dts/internal/indexRefCounter.d.ts.map +1 -1
- package/dist/dts/internal/module-augmentation.d.ts +0 -4
- package/dist/dts/internal/module-augmentation.d.ts.map +1 -1
- package/dist/dts/internal/parser.d.ts +8 -0
- package/dist/dts/internal/parser.d.ts.map +1 -1
- package/dist/dts/internal/parts.d.ts +66 -56
- package/dist/dts/internal/parts.d.ts.map +1 -1
- package/dist/dts/internal/render.d.ts +7 -7
- package/dist/dts/internal/render.d.ts.map +1 -1
- package/dist/dts/internal/server.d.ts.map +1 -1
- package/dist/esm/Directive.js +1 -1
- package/dist/esm/Directive.js.map +1 -1
- package/dist/esm/ElementRef.js +12 -7
- package/dist/esm/ElementRef.js.map +1 -1
- package/dist/esm/ElementSource.js +16 -13
- package/dist/esm/ElementSource.js.map +1 -1
- package/dist/esm/EventHandler.js +1 -1
- package/dist/esm/EventHandler.js.map +1 -1
- package/dist/esm/Html.js +29 -24
- package/dist/esm/Html.js.map +1 -1
- package/dist/esm/HtmlChunk.js +6 -1
- package/dist/esm/HtmlChunk.js.map +1 -1
- package/dist/esm/Hydrate.js +1 -1
- package/dist/esm/Hydrate.js.map +1 -1
- package/dist/esm/Many.js +14 -10
- package/dist/esm/Many.js.map +1 -1
- package/dist/esm/Parser.js +6 -335
- package/dist/esm/Parser.js.map +1 -1
- package/dist/esm/Placeholder.js +2 -2
- package/dist/esm/Placeholder.js.map +1 -1
- package/dist/esm/Platform.js +2 -2
- package/dist/esm/Platform.js.map +1 -1
- package/dist/esm/Render.js +1 -1
- package/dist/esm/Render.js.map +1 -1
- package/dist/esm/RenderContext.js +38 -17
- package/dist/esm/RenderContext.js.map +1 -1
- package/dist/esm/RenderTemplate.js +2 -12
- package/dist/esm/RenderTemplate.js.map +1 -1
- package/dist/esm/Template.js +24 -0
- package/dist/esm/Template.js.map +1 -1
- package/dist/esm/TemplateInstance.js +2 -2
- package/dist/esm/TemplateInstance.js.map +1 -1
- package/dist/esm/Test.js +20 -7
- package/dist/esm/Test.js.map +1 -1
- package/dist/esm/index.js +0 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/internal/EventSource.js +91 -0
- package/dist/esm/internal/EventSource.js.map +1 -0
- package/dist/esm/internal/browser.js +12 -12
- package/dist/esm/internal/browser.js.map +1 -1
- package/dist/esm/internal/hydrate.js +45 -46
- package/dist/esm/internal/hydrate.js.map +1 -1
- package/dist/esm/internal/indexRefCounter.js +50 -2
- package/dist/esm/internal/indexRefCounter.js.map +1 -1
- package/dist/esm/internal/parser.js +84 -17
- package/dist/esm/internal/parser.js.map +1 -1
- package/dist/esm/internal/parts.js +110 -27
- package/dist/esm/internal/parts.js.map +1 -1
- package/dist/esm/internal/render.js +476 -58
- package/dist/esm/internal/render.js.map +1 -1
- package/dist/esm/internal/server.js +5 -4
- package/dist/esm/internal/server.js.map +1 -1
- package/package.json +10 -26
- package/src/Directive.ts +1 -1
- package/src/ElementRef.ts +18 -14
- package/src/ElementSource.ts +62 -47
- package/src/EventHandler.ts +1 -1
- package/src/Html.ts +58 -57
- package/src/HtmlChunk.ts +15 -1
- package/src/Hydrate.ts +1 -1
- package/src/Many.ts +53 -43
- package/src/Parser.ts +10 -453
- package/src/Part.ts +15 -3
- package/src/Placeholder.ts +4 -4
- package/src/Platform.ts +2 -2
- package/src/Render.ts +7 -2
- package/src/RenderContext.ts +47 -19
- package/src/RenderTemplate.ts +9 -54
- package/src/Renderable.ts +2 -1
- package/src/Template.ts +26 -0
- package/src/TemplateInstance.ts +9 -9
- package/src/Test.ts +40 -21
- package/src/index.ts +0 -4
- package/src/internal/EventSource.ts +153 -0
- package/src/internal/browser.ts +26 -25
- package/src/internal/hydrate.ts +68 -61
- package/src/internal/indexRefCounter.ts +63 -2
- package/src/internal/module-augmentation.ts +0 -4
- package/src/internal/parser.ts +92 -19
- package/src/internal/parts.ts +158 -73
- package/src/internal/render.ts +701 -89
- package/src/internal/server.ts +5 -3
- package/Token/package.json +0 -6
- package/Tokenizer/package.json +0 -6
- package/dist/cjs/Token.js +0 -270
- package/dist/cjs/Token.js.map +0 -1
- package/dist/cjs/Tokenizer.js +0 -18
- package/dist/cjs/Tokenizer.js.map +0 -1
- package/dist/cjs/internal/readAttribute.js +0 -34
- package/dist/cjs/internal/readAttribute.js.map +0 -1
- package/dist/cjs/internal/tokenizer.js +0 -264
- package/dist/cjs/internal/tokenizer.js.map +0 -1
- package/dist/dts/Token.d.ts +0 -202
- package/dist/dts/Token.d.ts.map +0 -1
- package/dist/dts/Tokenizer.d.ts +0 -6
- package/dist/dts/Tokenizer.d.ts.map +0 -1
- package/dist/dts/internal/readAttribute.d.ts +0 -9
- package/dist/dts/internal/readAttribute.d.ts.map +0 -1
- package/dist/dts/internal/tokenizer.d.ts +0 -3
- package/dist/dts/internal/tokenizer.d.ts.map +0 -1
- package/dist/esm/Token.js +0 -264
- package/dist/esm/Token.js.map +0 -1
- package/dist/esm/Tokenizer.js +0 -9
- package/dist/esm/Tokenizer.js.map +0 -1
- package/dist/esm/internal/readAttribute.js +0 -24
- package/dist/esm/internal/readAttribute.js.map +0 -1
- package/dist/esm/internal/tokenizer.js +0 -296
- package/dist/esm/internal/tokenizer.js.map +0 -1
- package/src/Token.ts +0 -269
- package/src/Tokenizer.ts +0 -10
- package/src/internal/readAttribute.ts +0 -28
- package/src/internal/tokenizer.ts +0 -338
package/src/RenderTemplate.ts
CHANGED
|
@@ -4,30 +4,22 @@
|
|
|
4
4
|
|
|
5
5
|
import * as Context from "@typed/context"
|
|
6
6
|
import * as Fx from "@typed/fx/Fx"
|
|
7
|
-
import type { Rendered } from "@typed/wire"
|
|
8
|
-
import type * as Effect from "effect/Effect"
|
|
9
7
|
import type { Scope } from "effect/Scope"
|
|
10
|
-
import type { ElementRef } from "./ElementRef.js"
|
|
11
8
|
import type { Placeholder } from "./Placeholder.js"
|
|
12
9
|
import type { Renderable } from "./Renderable.js"
|
|
13
10
|
import type { RenderEvent } from "./RenderEvent.js"
|
|
14
|
-
import type { TemplateInstance } from "./TemplateInstance.js"
|
|
15
11
|
|
|
16
12
|
/**
|
|
17
13
|
* @since 1.0.0
|
|
18
14
|
*/
|
|
19
15
|
export interface RenderTemplate {
|
|
20
|
-
<Values extends ReadonlyArray<Renderable<any, any
|
|
16
|
+
<Values extends ReadonlyArray<Renderable<any, any>>>(
|
|
21
17
|
templateStrings: TemplateStringsArray,
|
|
22
|
-
values: Values
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
TemplateInstance<
|
|
28
|
-
Placeholder.Error<Values[number]>,
|
|
29
|
-
T
|
|
30
|
-
>
|
|
18
|
+
values: Values
|
|
19
|
+
): Fx.Fx<
|
|
20
|
+
Scope | Placeholder.Context<Values[number]>,
|
|
21
|
+
Placeholder.Error<Values[number]>,
|
|
22
|
+
RenderEvent
|
|
31
23
|
>
|
|
32
24
|
}
|
|
33
25
|
|
|
@@ -38,51 +30,14 @@ export const RenderTemplate: Context.Tagged<RenderTemplate, RenderTemplate> = Co
|
|
|
38
30
|
RenderTemplate,
|
|
39
31
|
RenderTemplate
|
|
40
32
|
>(
|
|
41
|
-
"
|
|
33
|
+
"@typed/template/RenderTemplate"
|
|
42
34
|
)
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* @since 1.0.0
|
|
46
|
-
*/
|
|
47
|
-
export interface TemplateFx<R, E, T extends Rendered = Rendered> extends
|
|
48
|
-
Fx.Fx<
|
|
49
|
-
RenderTemplate | Scope | R,
|
|
50
|
-
E,
|
|
51
|
-
RenderEvent
|
|
52
|
-
>
|
|
53
|
-
{
|
|
54
|
-
readonly instance: Effect.Effect<
|
|
55
|
-
RenderTemplate | Scope | R,
|
|
56
|
-
never,
|
|
57
|
-
TemplateInstance<
|
|
58
|
-
E,
|
|
59
|
-
T
|
|
60
|
-
>
|
|
61
|
-
>
|
|
62
|
-
}
|
|
63
|
-
|
|
64
35
|
/**
|
|
65
36
|
* @since 1.0.0
|
|
66
37
|
*/
|
|
67
38
|
export function html<const Values extends ReadonlyArray<Renderable<any, any>>>(
|
|
68
39
|
template: TemplateStringsArray,
|
|
69
40
|
...values: Values
|
|
70
|
-
):
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return Object.assign(Fx.fromFxEffect(instance), { instance })
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* @since 1.0.0
|
|
78
|
-
*/
|
|
79
|
-
export function as<T extends Rendered = Rendered>(ref: ElementRef<T>) {
|
|
80
|
-
return function html<const Values extends ReadonlyArray<Renderable<any, any>>>(
|
|
81
|
-
template: TemplateStringsArray,
|
|
82
|
-
...values: Values
|
|
83
|
-
): TemplateFx<Placeholder.Context<Values[number]>, Placeholder.Error<Values[number]>, T> {
|
|
84
|
-
const instance = RenderTemplate.withEffect((render) => render(template, values, ref))
|
|
85
|
-
|
|
86
|
-
return Object.assign(Fx.fromFxEffect(instance), { instance })
|
|
87
|
-
}
|
|
41
|
+
): Fx.Fx<RenderTemplate | Scope | Placeholder.Context<Values[number]>, Placeholder.Error<Values[number]>, RenderEvent> {
|
|
42
|
+
return Fx.fromFxEffect(RenderTemplate.with((render) => render(template, values)))
|
|
88
43
|
}
|
package/src/Renderable.ts
CHANGED
|
@@ -12,7 +12,8 @@ import type { RenderEvent } from "./RenderEvent.js"
|
|
|
12
12
|
*/
|
|
13
13
|
export type Renderable<R = never, E = never> =
|
|
14
14
|
| Renderable.Value
|
|
15
|
-
|
|
|
15
|
+
| Placeholder<R, E, any>
|
|
16
|
+
| { readonly [key: string]: Renderable<R, E> | Placeholder<R, E, any> | unknown } // TODO: Should we manage data attributes this way?
|
|
16
17
|
| Placeholder<R, E, any>
|
|
17
18
|
| Effect<R, E, any>
|
|
18
19
|
| Fx<R, E, any>
|
package/src/Template.ts
CHANGED
|
@@ -32,6 +32,7 @@ export type Node =
|
|
|
32
32
|
| TextNode
|
|
33
33
|
| NodePart
|
|
34
34
|
| Comment
|
|
35
|
+
| DocType
|
|
35
36
|
|
|
36
37
|
/**
|
|
37
38
|
* @since 1.0.0
|
|
@@ -44,6 +45,7 @@ export type PartNode =
|
|
|
44
45
|
| EventPartNode
|
|
45
46
|
| NodePart
|
|
46
47
|
| PropertyPartNode
|
|
48
|
+
| PropertiesPartNode
|
|
47
49
|
| RefPartNode
|
|
48
50
|
| TextPartNode
|
|
49
51
|
| CommentPartNode
|
|
@@ -96,6 +98,18 @@ export class TextOnlyElement {
|
|
|
96
98
|
) {}
|
|
97
99
|
}
|
|
98
100
|
|
|
101
|
+
/**
|
|
102
|
+
* @since 1.0.0
|
|
103
|
+
*/
|
|
104
|
+
export class DocType {
|
|
105
|
+
readonly _tag = "doctype"
|
|
106
|
+
constructor(
|
|
107
|
+
readonly name: string,
|
|
108
|
+
readonly publicType?: string,
|
|
109
|
+
readonly systemId?: string
|
|
110
|
+
) {}
|
|
111
|
+
}
|
|
112
|
+
|
|
99
113
|
/**
|
|
100
114
|
* @since 1.0.0
|
|
101
115
|
*/
|
|
@@ -110,6 +124,7 @@ export type Attribute =
|
|
|
110
124
|
| DataPartNode
|
|
111
125
|
| EventPartNode
|
|
112
126
|
| PropertyPartNode
|
|
127
|
+
| PropertiesPartNode
|
|
113
128
|
| RefPartNode
|
|
114
129
|
|
|
115
130
|
/**
|
|
@@ -219,6 +234,17 @@ export class PropertyPartNode {
|
|
|
219
234
|
) {}
|
|
220
235
|
}
|
|
221
236
|
|
|
237
|
+
/**
|
|
238
|
+
* @since 1.0.0
|
|
239
|
+
*/
|
|
240
|
+
export class PropertiesPartNode {
|
|
241
|
+
readonly _tag = "properties" as const
|
|
242
|
+
|
|
243
|
+
constructor(
|
|
244
|
+
readonly index: number
|
|
245
|
+
) {}
|
|
246
|
+
}
|
|
247
|
+
|
|
222
248
|
/**
|
|
223
249
|
* @since 1.0.0
|
|
224
250
|
*/
|
package/src/TemplateInstance.ts
CHANGED
|
@@ -8,14 +8,14 @@ import type * as Versioned from "@typed/fx/Versioned"
|
|
|
8
8
|
import type { Rendered } from "@typed/wire"
|
|
9
9
|
import type { NoSuchElementException } from "effect/Cause"
|
|
10
10
|
import type * as Effect from "effect/Effect"
|
|
11
|
+
import type { Scope } from "effect/Scope"
|
|
11
12
|
import { type ElementRef, ElementRefTypeId } from "./ElementRef.js"
|
|
12
|
-
import type { Placeholder } from "./Placeholder.js"
|
|
13
13
|
import type { RenderEvent } from "./RenderEvent.js"
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* @since 1.0.0
|
|
17
17
|
*/
|
|
18
|
-
export const TemplateInstanceTypeId = Symbol.for("
|
|
18
|
+
export const TemplateInstanceTypeId = Symbol.for("@typed/template/TemplateInstance")
|
|
19
19
|
/**
|
|
20
20
|
* @since 1.0.0
|
|
21
21
|
*/
|
|
@@ -25,7 +25,7 @@ export type TemplateInstanceTypeId = typeof TemplateInstanceTypeId
|
|
|
25
25
|
* @since 1.0.0
|
|
26
26
|
*/
|
|
27
27
|
export interface TemplateInstance<E, T extends Rendered = Rendered>
|
|
28
|
-
extends Versioned.Versioned<never, never,
|
|
28
|
+
extends Versioned.Versioned<never, never, Scope, E, RenderEvent, never, E | NoSuchElementException, T>
|
|
29
29
|
{
|
|
30
30
|
readonly [TemplateInstanceTypeId]: TemplateInstanceTypeId
|
|
31
31
|
|
|
@@ -42,16 +42,16 @@ export interface TemplateInstance<E, T extends Rendered = Rendered>
|
|
|
42
42
|
* @since 1.0.0
|
|
43
43
|
*/
|
|
44
44
|
export function TemplateInstance<T extends Rendered = Rendered, E = never>(
|
|
45
|
-
events: Fx.Fx<
|
|
45
|
+
events: Fx.Fx<Scope, E, RenderEvent>,
|
|
46
46
|
ref: ElementRef<T>
|
|
47
47
|
): TemplateInstance<E, T> {
|
|
48
48
|
return new TemplateInstanceImpl(events, ref) as any
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
// @ts-expect-error
|
|
51
|
+
// @ts-expect-error does not implement Placeholder
|
|
52
52
|
class TemplateInstanceImpl<E, T extends Rendered>
|
|
53
|
-
extends FxEffectBase<
|
|
54
|
-
implements
|
|
53
|
+
extends FxEffectBase<Scope, E, RenderEvent, never, E | NoSuchElementException, T>
|
|
54
|
+
implements TemplateInstance<E, T>
|
|
55
55
|
{
|
|
56
56
|
readonly [TemplateInstanceTypeId]: TemplateInstanceTypeId = TemplateInstanceTypeId
|
|
57
57
|
query: TemplateInstance<E, T>["query"]
|
|
@@ -61,7 +61,7 @@ class TemplateInstanceImpl<E, T extends Rendered>
|
|
|
61
61
|
version: Effect.Effect<never, never, number>
|
|
62
62
|
|
|
63
63
|
constructor(
|
|
64
|
-
readonly i0: Fx.Fx<
|
|
64
|
+
readonly i0: Fx.Fx<Scope, E, RenderEvent>,
|
|
65
65
|
readonly i1: ElementRef<T>
|
|
66
66
|
) {
|
|
67
67
|
super()
|
|
@@ -73,7 +73,7 @@ class TemplateInstanceImpl<E, T extends Rendered>
|
|
|
73
73
|
this.version = this.i1[ElementRefTypeId].version
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
toFx(): Fx.Fx<
|
|
76
|
+
toFx(): Fx.Fx<Scope, E, RenderEvent> {
|
|
77
77
|
return this.i0
|
|
78
78
|
}
|
|
79
79
|
|
package/src/Test.ts
CHANGED
|
@@ -6,17 +6,15 @@ import type { DomServices, DomServicesElementParams } from "@typed/dom/DomServic
|
|
|
6
6
|
import type { GlobalThis } from "@typed/dom/GlobalThis"
|
|
7
7
|
import type { Window } from "@typed/dom/Window"
|
|
8
8
|
import type { CurrentEnvironment } from "@typed/environment"
|
|
9
|
-
import type { Computed } from "@typed/fx/Computed"
|
|
10
|
-
import type { Filtered } from "@typed/fx/Filtered"
|
|
11
9
|
import * as Fx from "@typed/fx/Fx"
|
|
12
10
|
import * as RefArray from "@typed/fx/RefArray"
|
|
11
|
+
import * as RefSubject from "@typed/fx/RefSubject"
|
|
13
12
|
import * as Sink from "@typed/fx/Sink"
|
|
14
13
|
import * as Cause from "effect/Cause"
|
|
15
14
|
import * as Effect from "effect/Effect"
|
|
16
15
|
import * as Either from "effect/Either"
|
|
17
16
|
import * as Fiber from "effect/Fiber"
|
|
18
17
|
import type * as Scope from "effect/Scope"
|
|
19
|
-
import * as happyDOM from "happy-dom"
|
|
20
18
|
import type IHappyDOMOptions from "happy-dom/lib/window/IHappyDOMOptions.js"
|
|
21
19
|
import * as ElementRef from "./ElementRef.js"
|
|
22
20
|
import { ROOT_CSS_SELECTOR } from "./ElementSource.js"
|
|
@@ -36,11 +34,11 @@ import type { RenderTemplate } from "./RenderTemplate.js"
|
|
|
36
34
|
* @since 1.0.0
|
|
37
35
|
*/
|
|
38
36
|
export interface TestRender<E> {
|
|
39
|
-
readonly window: Window & GlobalThis
|
|
37
|
+
readonly window: Window & GlobalThis
|
|
40
38
|
readonly document: Document
|
|
41
39
|
readonly elementRef: ElementRef.ElementRef
|
|
42
|
-
readonly errors: Computed<never, never, ReadonlyArray<E>>
|
|
43
|
-
readonly lastError: Filtered<never, never, E>
|
|
40
|
+
readonly errors: RefSubject.Computed<never, never, ReadonlyArray<E>>
|
|
41
|
+
readonly lastError: RefSubject.Filtered<never, never, E>
|
|
44
42
|
readonly interrupt: Effect.Effect<never, never, void>
|
|
45
43
|
readonly makeEvent: (type: string, eventInitDict?: EventInit) => Event
|
|
46
44
|
readonly makeCustomEvent: <A>(type: string, eventInitDict?: CustomEventInit<A>) => CustomEvent<A>
|
|
@@ -62,24 +60,25 @@ export function testRender<R, E>(
|
|
|
62
60
|
TestRender<E>
|
|
63
61
|
> {
|
|
64
62
|
return Effect.gen(function*(_) {
|
|
65
|
-
const window =
|
|
63
|
+
const window = yield* _(getOrMakeWindow(options))
|
|
66
64
|
const elementRef = yield* _(ElementRef.make())
|
|
67
|
-
const errors = yield* _(
|
|
65
|
+
const errors = yield* _(RefSubject.make<never, never, ReadonlyArray<E>>(Effect.succeed([])))
|
|
68
66
|
const fiber = yield* _(
|
|
69
67
|
fx,
|
|
70
68
|
render,
|
|
71
|
-
|
|
72
|
-
(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
69
|
+
(x) =>
|
|
70
|
+
x.run(Sink.make(
|
|
71
|
+
(cause) =>
|
|
72
|
+
Cause.failureOrCause(cause).pipe(
|
|
73
|
+
Either.match({
|
|
74
|
+
onLeft: (error) => RefArray.append(errors, error),
|
|
75
|
+
onRight: (cause) => errors.onFailure(cause)
|
|
76
|
+
})
|
|
77
|
+
),
|
|
78
|
+
(rendered) => ElementRef.set(elementRef, rendered)
|
|
79
|
+
)),
|
|
81
80
|
Effect.forkScoped,
|
|
82
|
-
Effect.provide(RenderContext.
|
|
81
|
+
Effect.provide(RenderContext.dom(window, { skipRenderScheduling: true }))
|
|
83
82
|
)
|
|
84
83
|
|
|
85
84
|
const test: TestRender<E> = {
|
|
@@ -99,6 +98,9 @@ export function testRender<R, E>(
|
|
|
99
98
|
yield* _(adjustTime(1))
|
|
100
99
|
yield* _(adjustTime(1))
|
|
101
100
|
|
|
101
|
+
// Await the first render
|
|
102
|
+
yield* _(Fx.first(elementRef), Effect.race(Effect.delay(Effect.dieMessage(`Rendering taking too long`), 1000)))
|
|
103
|
+
|
|
102
104
|
return test
|
|
103
105
|
})
|
|
104
106
|
}
|
|
@@ -146,6 +148,23 @@ export function click<E>(
|
|
|
146
148
|
|
|
147
149
|
// internals
|
|
148
150
|
|
|
149
|
-
function
|
|
150
|
-
|
|
151
|
+
function getOrMakeWindow(options?: IHappyDOMOptions) {
|
|
152
|
+
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
153
|
+
return Effect.gen(function*(_) {
|
|
154
|
+
window.document.head.innerHTML = ""
|
|
155
|
+
window.document.body.innerHTML = ""
|
|
156
|
+
yield* _(Effect.addFinalizer(() =>
|
|
157
|
+
Effect.sync(() => {
|
|
158
|
+
window.document.head.innerHTML = ""
|
|
159
|
+
window.document.body.innerHTML = ""
|
|
160
|
+
})
|
|
161
|
+
))
|
|
162
|
+
|
|
163
|
+
return window
|
|
164
|
+
})
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return Effect.promise(() =>
|
|
168
|
+
import("happy-dom").then((happyDOM) => new happyDOM.Window(options) as any as Window & GlobalThis)
|
|
169
|
+
)
|
|
151
170
|
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { Rendered } from "@typed/wire"
|
|
2
|
+
import { Effect, Scope } from "effect"
|
|
3
|
+
import type * as Fiber from "effect/Fiber"
|
|
4
|
+
import * as Runtime from "effect/Runtime"
|
|
5
|
+
import { getElements } from "../ElementSource"
|
|
6
|
+
import type { EventHandler } from "../EventHandler"
|
|
7
|
+
|
|
8
|
+
type EventName = string
|
|
9
|
+
|
|
10
|
+
type Handler<Ev extends Event> = EventHandler<never, never, Ev>
|
|
11
|
+
|
|
12
|
+
export interface EventSource {
|
|
13
|
+
readonly addEventListener: <Ev extends Event>(
|
|
14
|
+
element: Element,
|
|
15
|
+
event: EventName,
|
|
16
|
+
handler: Handler<Ev>
|
|
17
|
+
) => void
|
|
18
|
+
|
|
19
|
+
readonly setup: (rendered: Rendered, scope: Scope.Scope) => Effect.Effect<never, never, void>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type Entry = readonly [Element, Handler<any>]
|
|
23
|
+
type Run = <E, A>(effect: Effect.Effect<never, E, A>) => Fiber.RuntimeFiber<E, A>
|
|
24
|
+
|
|
25
|
+
const disposable = (f: () => void): Disposable => ({
|
|
26
|
+
[Symbol.dispose]: f
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
export function makeEventSource(): EventSource {
|
|
30
|
+
const bubbleListeners = new Map<
|
|
31
|
+
EventName,
|
|
32
|
+
Set<Entry>
|
|
33
|
+
>()
|
|
34
|
+
const captureListeners = new Map<
|
|
35
|
+
EventName,
|
|
36
|
+
Set<Entry>
|
|
37
|
+
>()
|
|
38
|
+
|
|
39
|
+
function addListener(
|
|
40
|
+
listeners: Map<
|
|
41
|
+
EventName,
|
|
42
|
+
Set<Entry>
|
|
43
|
+
>,
|
|
44
|
+
event: EventName,
|
|
45
|
+
entry: Entry
|
|
46
|
+
): void {
|
|
47
|
+
const set = listeners.get(event)
|
|
48
|
+
if (set === undefined) {
|
|
49
|
+
const set = new Set<Entry>()
|
|
50
|
+
set.add(entry)
|
|
51
|
+
listeners.set(event, set)
|
|
52
|
+
} else {
|
|
53
|
+
set.add(entry)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function addEventListener<Ev extends Event>(
|
|
58
|
+
element: Element,
|
|
59
|
+
event: EventName,
|
|
60
|
+
handler: Handler<Ev>
|
|
61
|
+
): void {
|
|
62
|
+
if (handler.options?.capture === true) {
|
|
63
|
+
return addListener(captureListeners, event, [element, handler])
|
|
64
|
+
} else {
|
|
65
|
+
return addListener(bubbleListeners, event, [element, handler])
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function setupBubbleListeners(element: Element, run: Run) {
|
|
70
|
+
const disposables: Array<Disposable> = []
|
|
71
|
+
|
|
72
|
+
for (const [event, handlers] of bubbleListeners) {
|
|
73
|
+
const listener = (ev: Event) =>
|
|
74
|
+
run(
|
|
75
|
+
Effect.forEach(handlers, ([el, handler]) =>
|
|
76
|
+
ev.target === el || el.contains(ev.target as Node) ? handler.handler(ev) : Effect.unit)
|
|
77
|
+
)
|
|
78
|
+
element.addEventListener(event, listener, getDerivedAddEventListenerOptions(handlers))
|
|
79
|
+
disposables.push(disposable(() => element.removeEventListener(event, listener)))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return disposables
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function setupCaptureListeners(element: Element, run: Run) {
|
|
86
|
+
const disposables: Array<Disposable> = []
|
|
87
|
+
|
|
88
|
+
for (const [event, handlers] of captureListeners) {
|
|
89
|
+
const listener = (ev: Event) =>
|
|
90
|
+
run(
|
|
91
|
+
Effect.forEach(handlers, ([el, handler]) =>
|
|
92
|
+
ev.target === el || el.contains(ev.target as Node)
|
|
93
|
+
? handler.handler(proxyCurrentTargetForCaptureEvents(ev, el))
|
|
94
|
+
: Effect.unit)
|
|
95
|
+
)
|
|
96
|
+
element.addEventListener(event, listener, getDerivedAddEventListenerOptions(handlers))
|
|
97
|
+
disposables.push(disposable(() => element.removeEventListener(event, listener)))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return disposables
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function setup(rendered: Rendered, scope: Scope.Scope) {
|
|
104
|
+
const hasBubbleListeners = bubbleListeners.size > 0
|
|
105
|
+
const hasCaptureListeners = captureListeners.size > 0
|
|
106
|
+
|
|
107
|
+
if (!hasBubbleListeners && !hasCaptureListeners) {
|
|
108
|
+
return Effect.unit
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return Effect.flatMap(Effect.runtime<never>(), (runtime) => {
|
|
112
|
+
const elements = getElements(rendered)
|
|
113
|
+
const disposables: Array<Disposable> = []
|
|
114
|
+
const runFork = Runtime.runFork(runtime)
|
|
115
|
+
const run: Run = <E, A>(effect: Effect.Effect<never, E, A>) =>
|
|
116
|
+
runFork(Effect.fromFiberEffect(Effect.forkIn(effect, scope)))
|
|
117
|
+
|
|
118
|
+
for (const element of elements) {
|
|
119
|
+
if (hasBubbleListeners) {
|
|
120
|
+
disposables.push(...setupBubbleListeners(element, run))
|
|
121
|
+
}
|
|
122
|
+
if (hasCaptureListeners) {
|
|
123
|
+
disposables.push(...setupCaptureListeners(element, run))
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return Scope.addFinalizer(scope, Effect.sync(() => disposables.forEach((d) => d[Symbol.dispose]())))
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
addEventListener,
|
|
133
|
+
setup
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const EVENT_PROPERTY_TO_REPLACE = "currentTarget"
|
|
138
|
+
|
|
139
|
+
function proxyCurrentTargetForCaptureEvents<E extends Event>(event: E, currentTarget: Element): E {
|
|
140
|
+
return new Proxy(event, {
|
|
141
|
+
get(target: E, property: string | symbol) {
|
|
142
|
+
return property === EVENT_PROPERTY_TO_REPLACE ? currentTarget : target[property as keyof E]
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function getDerivedAddEventListenerOptions(entries: Set<Entry>): AddEventListenerOptions {
|
|
148
|
+
const hs = Array.from(entries)
|
|
149
|
+
return {
|
|
150
|
+
once: hs.some((h) => h[1].options?.once === true),
|
|
151
|
+
passive: hs.every((h) => h[1].options?.passive === true)
|
|
152
|
+
}
|
|
153
|
+
}
|
package/src/internal/browser.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { diffable, isComment } from "@typed/wire"
|
|
2
2
|
import udomdiff from "udomdiff"
|
|
3
3
|
import type { RenderContext } from "../RenderContext.js"
|
|
4
|
-
import type { RenderEvent } from "../RenderEvent.js"
|
|
5
4
|
import { isRenderEvent } from "../RenderEvent.js"
|
|
6
5
|
import { NodePartImpl } from "./parts.js"
|
|
7
6
|
import { findHoleComment, isCommentWithValue } from "./utils.js"
|
|
@@ -19,16 +18,20 @@ export function makeRenderNodePart(
|
|
|
19
18
|
|
|
20
19
|
return new NodePartImpl(index, ({ part, value }) => {
|
|
21
20
|
return ctx.queue.add(part, () => {
|
|
22
|
-
matchValue(
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
matchValue(
|
|
22
|
+
value,
|
|
23
|
+
(content) => {
|
|
24
|
+
if (text === undefined) {
|
|
25
|
+
text = document.createTextNode("")
|
|
26
|
+
}
|
|
27
|
+
text.textContent = content
|
|
28
|
+
|
|
29
|
+
nodes = diffChildren(comment, nodes, [text], document)
|
|
30
|
+
},
|
|
31
|
+
(updatedNodes) => {
|
|
32
|
+
nodes = diffChildren(comment, nodes, updatedNodes, document)
|
|
25
33
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
nodes = diffChildren(comment, nodes, [text], document)
|
|
29
|
-
}, (updatedNodes) => {
|
|
30
|
-
nodes = diffChildren(comment, nodes, updatedNodes, document)
|
|
31
|
-
})
|
|
34
|
+
)
|
|
32
35
|
})
|
|
33
36
|
}, nodes)
|
|
34
37
|
}
|
|
@@ -83,22 +86,12 @@ export function diffChildren(
|
|
|
83
86
|
comment.parentNode!,
|
|
84
87
|
// Document Fragments cannot be removed, so we filter them out
|
|
85
88
|
currentNodes.filter((x) => x.nodeType !== x.DOCUMENT_FRAGMENT_NODE),
|
|
86
|
-
nextNodes
|
|
89
|
+
nextNodes,
|
|
87
90
|
diffable(document),
|
|
88
91
|
comment
|
|
89
92
|
)
|
|
90
93
|
}
|
|
91
94
|
|
|
92
|
-
function flattenRenderEvent(x: Node | RenderEvent): Array<Node> {
|
|
93
|
-
if (isRenderEvent(x)) {
|
|
94
|
-
const value = x.valueOf()
|
|
95
|
-
|
|
96
|
-
return Array.isArray(value) ? value : [value]
|
|
97
|
-
} else {
|
|
98
|
-
return [x]
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
95
|
function matchValue<A, B>(value: unknown, onText: (text: string) => A, onNodes: (nodes: Array<Node>) => B): A | B {
|
|
103
96
|
switch (typeof value) {
|
|
104
97
|
// primitives are handled as text content
|
|
@@ -117,16 +110,24 @@ function matchValue<A, B>(value: unknown, onText: (text: string) => A, onNodes:
|
|
|
117
110
|
if (value.length === 0) return onNodes([])
|
|
118
111
|
// or diffed, if these contains nodes or "wires"
|
|
119
112
|
else if (value.some((x) => typeof x === "object")) {
|
|
120
|
-
return onNodes(
|
|
121
|
-
value.flatMap((x) => (x === null ? [] : [isRenderEvent(x) ? x.valueOf() : x]))
|
|
122
|
-
)
|
|
113
|
+
return onNodes(value.flatMap(renderEventToArray))
|
|
123
114
|
} // in all other cases the content is stringified as is
|
|
124
115
|
else return onText(String(value))
|
|
125
116
|
} else {
|
|
126
|
-
return onNodes(
|
|
117
|
+
return onNodes(renderEventToArray(value))
|
|
127
118
|
}
|
|
128
119
|
}
|
|
129
120
|
case "function":
|
|
130
121
|
return onNodes([])
|
|
131
122
|
}
|
|
132
123
|
}
|
|
124
|
+
|
|
125
|
+
function renderEventToArray(x: unknown): Array<Node> {
|
|
126
|
+
if (x === null || x === undefined) return []
|
|
127
|
+
if (isRenderEvent(x)) {
|
|
128
|
+
const value = x.valueOf()
|
|
129
|
+
return Array.isArray(value) ? value : [value]
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return [x as Node]
|
|
133
|
+
}
|