@typed/template 0.1.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/LICENSE +21 -0
- package/README.md +5 -0
- package/dist/cjs/Directive.js +76 -0
- package/dist/cjs/Directive.js.map +1 -0
- package/dist/cjs/ElementRef.js +83 -0
- package/dist/cjs/ElementRef.js.map +1 -0
- package/dist/cjs/ElementSource.js +244 -0
- package/dist/cjs/ElementSource.js.map +1 -0
- package/dist/cjs/Entry.js +6 -0
- package/dist/cjs/Entry.js.map +1 -0
- package/dist/cjs/EventHandler.js +65 -0
- package/dist/cjs/EventHandler.js.map +1 -0
- package/dist/cjs/Html.js +169 -0
- package/dist/cjs/Html.js.map +1 -0
- package/dist/cjs/HtmlChunk.js +257 -0
- package/dist/cjs/HtmlChunk.js.map +1 -0
- package/dist/cjs/Hydrate.js +49 -0
- package/dist/cjs/Hydrate.js.map +1 -0
- package/dist/cjs/Many.js +45 -0
- package/dist/cjs/Many.js.map +1 -0
- package/dist/cjs/Meta.js +37 -0
- package/dist/cjs/Meta.js.map +1 -0
- package/dist/cjs/Parser.js +331 -0
- package/dist/cjs/Parser.js.map +1 -0
- package/dist/cjs/Part.js +6 -0
- package/dist/cjs/Part.js.map +1 -0
- package/dist/cjs/Placeholder.js +38 -0
- package/dist/cjs/Placeholder.js.map +1 -0
- package/dist/cjs/Platform.js +64 -0
- package/dist/cjs/Platform.js.map +1 -0
- package/dist/cjs/Render.js +39 -0
- package/dist/cjs/Render.js.map +1 -0
- package/dist/cjs/RenderContext.js +130 -0
- package/dist/cjs/RenderContext.js.map +1 -0
- package/dist/cjs/RenderEvent.js +44 -0
- package/dist/cjs/RenderEvent.js.map +1 -0
- package/dist/cjs/RenderTemplate.js +41 -0
- package/dist/cjs/RenderTemplate.js.map +1 -0
- package/dist/cjs/Renderable.js +6 -0
- package/dist/cjs/Renderable.js.map +1 -0
- package/dist/cjs/Template.js +266 -0
- package/dist/cjs/Template.js.map +1 -0
- package/dist/cjs/TemplateInstance.js +51 -0
- package/dist/cjs/TemplateInstance.js.map +1 -0
- package/dist/cjs/Test.js +90 -0
- package/dist/cjs/Test.js.map +1 -0
- package/dist/cjs/Token.js +270 -0
- package/dist/cjs/Token.js.map +1 -0
- package/dist/cjs/Tokenizer.js +18 -0
- package/dist/cjs/Tokenizer.js.map +1 -0
- package/dist/cjs/Vitest.js +44 -0
- package/dist/cjs/Vitest.js.map +1 -0
- package/dist/cjs/index.js +147 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/internal/HydrateContext.js +13 -0
- package/dist/cjs/internal/HydrateContext.js.map +1 -0
- package/dist/cjs/internal/browser.js +109 -0
- package/dist/cjs/internal/browser.js.map +1 -0
- package/dist/cjs/internal/chunks.js +54 -0
- package/dist/cjs/internal/chunks.js.map +1 -0
- package/dist/cjs/internal/errors.js +23 -0
- package/dist/cjs/internal/errors.js.map +1 -0
- package/dist/cjs/internal/hydrate.js +197 -0
- package/dist/cjs/internal/hydrate.js.map +1 -0
- package/dist/cjs/internal/indexRefCounter.js +32 -0
- package/dist/cjs/internal/indexRefCounter.js.map +1 -0
- package/dist/cjs/internal/module-augmentation.js +6 -0
- package/dist/cjs/internal/module-augmentation.js.map +1 -0
- package/dist/cjs/internal/parser.js +492 -0
- package/dist/cjs/internal/parser.js.map +1 -0
- package/dist/cjs/internal/parts.js +350 -0
- package/dist/cjs/internal/parts.js.map +1 -0
- package/dist/cjs/internal/readAttribute.js +34 -0
- package/dist/cjs/internal/readAttribute.js.map +1 -0
- package/dist/cjs/internal/render.js +332 -0
- package/dist/cjs/internal/render.js.map +1 -0
- package/dist/cjs/internal/server.js +219 -0
- package/dist/cjs/internal/server.js.map +1 -0
- package/dist/cjs/internal/tokenizer.js +264 -0
- package/dist/cjs/internal/tokenizer.js.map +1 -0
- package/dist/cjs/internal/utils.js +68 -0
- package/dist/cjs/internal/utils.js.map +1 -0
- package/dist/dts/Directive.d.ts +70 -0
- package/dist/dts/Directive.d.ts.map +1 -0
- package/dist/dts/ElementRef.d.ts +40 -0
- package/dist/dts/ElementRef.d.ts.map +1 -0
- package/dist/dts/ElementSource.d.ts +72 -0
- package/dist/dts/ElementSource.d.ts.map +1 -0
- package/dist/dts/Entry.d.ts +26 -0
- package/dist/dts/Entry.d.ts.map +1 -0
- package/dist/dts/EventHandler.d.ts +61 -0
- package/dist/dts/EventHandler.d.ts.map +1 -0
- package/dist/dts/Html.d.ts +17 -0
- package/dist/dts/Html.d.ts.map +1 -0
- package/dist/dts/HtmlChunk.d.ts +56 -0
- package/dist/dts/HtmlChunk.d.ts.map +1 -0
- package/dist/dts/Hydrate.d.ts +20 -0
- package/dist/dts/Hydrate.d.ts.map +1 -0
- package/dist/dts/Many.d.ts +32 -0
- package/dist/dts/Many.d.ts.map +1 -0
- package/dist/dts/Meta.d.ts +24 -0
- package/dist/dts/Meta.d.ts.map +1 -0
- package/dist/dts/Parser.d.ts +16 -0
- package/dist/dts/Parser.d.ts.map +1 -0
- package/dist/dts/Part.d.ts +147 -0
- package/dist/dts/Part.d.ts.map +1 -0
- package/dist/dts/Placeholder.d.ts +51 -0
- package/dist/dts/Placeholder.d.ts.map +1 -0
- package/dist/dts/Platform.d.ts +23 -0
- package/dist/dts/Platform.d.ts.map +1 -0
- package/dist/dts/Render.d.ts +23 -0
- package/dist/dts/Render.d.ts.map +1 -0
- package/dist/dts/RenderContext.d.ts +88 -0
- package/dist/dts/RenderContext.d.ts.map +1 -0
- package/dist/dts/RenderEvent.d.ts +37 -0
- package/dist/dts/RenderEvent.d.ts.map +1 -0
- package/dist/dts/RenderTemplate.d.ts +38 -0
- package/dist/dts/RenderTemplate.d.ts.map +1 -0
- package/dist/dts/Renderable.d.ts +28 -0
- package/dist/dts/Renderable.d.ts.map +1 -0
- package/dist/dts/Template.d.ts +218 -0
- package/dist/dts/Template.d.ts.map +1 -0
- package/dist/dts/TemplateInstance.d.ts +32 -0
- package/dist/dts/TemplateInstance.d.ts.map +1 -0
- package/dist/dts/Test.d.ts +58 -0
- package/dist/dts/Test.d.ts.map +1 -0
- package/dist/dts/Token.d.ts +202 -0
- package/dist/dts/Token.d.ts.map +1 -0
- package/dist/dts/Tokenizer.d.ts +6 -0
- package/dist/dts/Tokenizer.d.ts.map +1 -0
- package/dist/dts/Vitest.d.ts +28 -0
- package/dist/dts/Vitest.d.ts.map +1 -0
- package/dist/dts/index.d.ts +65 -0
- package/dist/dts/index.d.ts.map +1 -0
- package/dist/dts/internal/HydrateContext.d.ts +2 -0
- package/dist/dts/internal/HydrateContext.d.ts.map +1 -0
- package/dist/dts/internal/browser.d.ts +8 -0
- package/dist/dts/internal/browser.d.ts.map +1 -0
- package/dist/dts/internal/chunks.d.ts +22 -0
- package/dist/dts/internal/chunks.d.ts.map +1 -0
- package/dist/dts/internal/errors.d.ts +9 -0
- package/dist/dts/internal/errors.d.ts.map +1 -0
- package/dist/dts/internal/hydrate.d.ts +37 -0
- package/dist/dts/internal/hydrate.d.ts.map +1 -0
- package/dist/dts/internal/indexRefCounter.d.ts +6 -0
- package/dist/dts/internal/indexRefCounter.d.ts.map +1 -0
- package/dist/dts/internal/module-augmentation.d.ts +36 -0
- package/dist/dts/internal/module-augmentation.d.ts.map +1 -0
- package/dist/dts/internal/parser.d.ts +12 -0
- package/dist/dts/internal/parser.d.ts.map +1 -0
- package/dist/dts/internal/parts.d.ts +304 -0
- package/dist/dts/internal/parts.d.ts.map +1 -0
- package/dist/dts/internal/readAttribute.d.ts +9 -0
- package/dist/dts/internal/readAttribute.d.ts.map +1 -0
- package/dist/dts/internal/render.d.ts +30 -0
- package/dist/dts/internal/render.d.ts.map +1 -0
- package/dist/dts/internal/server.d.ts +31 -0
- package/dist/dts/internal/server.d.ts.map +1 -0
- package/dist/dts/internal/tokenizer.d.ts +3 -0
- package/dist/dts/internal/tokenizer.d.ts.map +1 -0
- package/dist/dts/internal/utils.d.ts +15 -0
- package/dist/dts/internal/utils.d.ts.map +1 -0
- package/dist/esm/Directive.js +64 -0
- package/dist/esm/Directive.js.map +1 -0
- package/dist/esm/ElementRef.js +72 -0
- package/dist/esm/ElementRef.js.map +1 -0
- package/dist/esm/ElementSource.js +237 -0
- package/dist/esm/ElementSource.js.map +1 -0
- package/dist/esm/Entry.js +2 -0
- package/dist/esm/Entry.js.map +1 -0
- package/dist/esm/EventHandler.js +52 -0
- package/dist/esm/EventHandler.js.map +1 -0
- package/dist/esm/Html.js +167 -0
- package/dist/esm/Html.js.map +1 -0
- package/dist/esm/HtmlChunk.js +274 -0
- package/dist/esm/HtmlChunk.js.map +1 -0
- package/dist/esm/Hydrate.js +37 -0
- package/dist/esm/Hydrate.js.map +1 -0
- package/dist/esm/Many.js +33 -0
- package/dist/esm/Many.js.map +1 -0
- package/dist/esm/Meta.js +29 -0
- package/dist/esm/Meta.js.map +1 -0
- package/dist/esm/Parser.js +342 -0
- package/dist/esm/Parser.js.map +1 -0
- package/dist/esm/Part.js +5 -0
- package/dist/esm/Part.js.map +1 -0
- package/dist/esm/Placeholder.js +30 -0
- package/dist/esm/Placeholder.js.map +1 -0
- package/dist/esm/Platform.js +41 -0
- package/dist/esm/Platform.js.map +1 -0
- package/dist/esm/Render.js +27 -0
- package/dist/esm/Render.js.map +1 -0
- package/dist/esm/RenderContext.js +113 -0
- package/dist/esm/RenderContext.js.map +1 -0
- package/dist/esm/RenderEvent.js +36 -0
- package/dist/esm/RenderEvent.js.map +1 -0
- package/dist/esm/RenderTemplate.js +26 -0
- package/dist/esm/RenderTemplate.js.map +1 -0
- package/dist/esm/Renderable.js +2 -0
- package/dist/esm/Renderable.js.map +1 -0
- package/dist/esm/Template.js +239 -0
- package/dist/esm/Template.js.map +1 -0
- package/dist/esm/TemplateInstance.js +43 -0
- package/dist/esm/TemplateInstance.js.map +1 -0
- package/dist/esm/Test.js +68 -0
- package/dist/esm/Test.js.map +1 -0
- package/dist/esm/Token.js +264 -0
- package/dist/esm/Token.js.map +1 -0
- package/dist/esm/Tokenizer.js +9 -0
- package/dist/esm/Tokenizer.js.map +1 -0
- package/dist/esm/Vitest.js +29 -0
- package/dist/esm/Vitest.js.map +1 -0
- package/dist/esm/index.js +65 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/HydrateContext.js +7 -0
- package/dist/esm/internal/HydrateContext.js.map +1 -0
- package/dist/esm/internal/browser.js +102 -0
- package/dist/esm/internal/browser.js.map +1 -0
- package/dist/esm/internal/chunks.js +47 -0
- package/dist/esm/internal/chunks.js.map +1 -0
- package/dist/esm/internal/errors.js +15 -0
- package/dist/esm/internal/errors.js.map +1 -0
- package/dist/esm/internal/hydrate.js +165 -0
- package/dist/esm/internal/hydrate.js.map +1 -0
- package/dist/esm/internal/indexRefCounter.js +24 -0
- package/dist/esm/internal/indexRefCounter.js.map +1 -0
- package/dist/esm/internal/module-augmentation.js +2 -0
- package/dist/esm/internal/module-augmentation.js.map +1 -0
- package/dist/esm/internal/parser.js +493 -0
- package/dist/esm/internal/parser.js.map +1 -0
- package/dist/esm/internal/parts.js +291 -0
- package/dist/esm/internal/parts.js.map +1 -0
- package/dist/esm/internal/readAttribute.js +24 -0
- package/dist/esm/internal/readAttribute.js.map +1 -0
- package/dist/esm/internal/render.js +329 -0
- package/dist/esm/internal/render.js.map +1 -0
- package/dist/esm/internal/server.js +174 -0
- package/dist/esm/internal/server.js.map +1 -0
- package/dist/esm/internal/tokenizer.js +296 -0
- package/dist/esm/internal/tokenizer.js.map +1 -0
- package/dist/esm/internal/utils.js +52 -0
- package/dist/esm/internal/utils.js.map +1 -0
- package/dist/esm/package.json +4 -0
- package/package.json +242 -0
- package/src/Directive.ts +114 -0
- package/src/ElementRef.ts +123 -0
- package/src/ElementSource.ts +417 -0
- package/src/Entry.ts +28 -0
- package/src/EventHandler.ts +104 -0
- package/src/Html.ts +258 -0
- package/src/HtmlChunk.ts +346 -0
- package/src/Hydrate.ts +53 -0
- package/src/Many.ts +128 -0
- package/src/Meta.ts +32 -0
- package/src/Parser.ts +457 -0
- package/src/Part.ts +186 -0
- package/src/Placeholder.ts +70 -0
- package/src/Platform.ts +71 -0
- package/src/Render.ts +45 -0
- package/src/RenderContext.ts +221 -0
- package/src/RenderEvent.ts +67 -0
- package/src/RenderTemplate.ts +88 -0
- package/src/Renderable.ts +34 -0
- package/src/Template.ts +284 -0
- package/src/TemplateInstance.ts +83 -0
- package/src/Test.ts +151 -0
- package/src/Token.ts +269 -0
- package/src/Tokenizer.ts +10 -0
- package/src/Vitest.ts +61 -0
- package/src/index.ts +66 -0
- package/src/internal/HydrateContext.ts +23 -0
- package/src/internal/browser.ts +132 -0
- package/src/internal/chunks.ts +73 -0
- package/src/internal/errors.ts +11 -0
- package/src/internal/external.d.ts +11 -0
- package/src/internal/hydrate.ts +262 -0
- package/src/internal/indexRefCounter.ts +33 -0
- package/src/internal/module-augmentation.ts +48 -0
- package/src/internal/parser.ts +637 -0
- package/src/internal/parts.ts +527 -0
- package/src/internal/readAttribute.ts +28 -0
- package/src/internal/render.ts +529 -0
- package/src/internal/server.ts +293 -0
- package/src/internal/tokenizer.ts +338 -0
- package/src/internal/utils.ts +73 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as Context from "@typed/context"
|
|
6
|
+
import * as Fx from "@typed/fx/Fx"
|
|
7
|
+
import type { Rendered } from "@typed/wire"
|
|
8
|
+
import type * as Effect from "effect/Effect"
|
|
9
|
+
import type { Scope } from "effect/Scope"
|
|
10
|
+
import type { ElementRef } from "./ElementRef"
|
|
11
|
+
import type { Placeholder } from "./Placeholder"
|
|
12
|
+
import type { Renderable } from "./Renderable"
|
|
13
|
+
import type { RenderEvent } from "./RenderEvent"
|
|
14
|
+
import type { TemplateInstance } from "./TemplateInstance"
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @since 1.0.0
|
|
18
|
+
*/
|
|
19
|
+
export interface RenderTemplate {
|
|
20
|
+
<Values extends ReadonlyArray<Renderable<any, any>>, T extends Rendered = Rendered>(
|
|
21
|
+
templateStrings: TemplateStringsArray,
|
|
22
|
+
values: Values,
|
|
23
|
+
ref?: ElementRef<T>
|
|
24
|
+
): Effect.Effect<
|
|
25
|
+
Scope | Placeholder.Context<readonly [] extends Values ? never : Values[number]>,
|
|
26
|
+
never,
|
|
27
|
+
TemplateInstance<
|
|
28
|
+
Placeholder.Error<Values[number]>,
|
|
29
|
+
T
|
|
30
|
+
>
|
|
31
|
+
>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @since 1.0.0
|
|
36
|
+
*/
|
|
37
|
+
export const RenderTemplate: Context.Tagged<RenderTemplate, RenderTemplate> = Context.Tagged<
|
|
38
|
+
RenderTemplate,
|
|
39
|
+
RenderTemplate
|
|
40
|
+
>(
|
|
41
|
+
"./RenderTemplate"
|
|
42
|
+
)
|
|
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
|
+
/**
|
|
65
|
+
* @since 1.0.0
|
|
66
|
+
*/
|
|
67
|
+
export function html<const Values extends ReadonlyArray<Renderable<any, any>>>(
|
|
68
|
+
template: TemplateStringsArray,
|
|
69
|
+
...values: Values
|
|
70
|
+
): TemplateFx<Placeholder.Context<Values[number]>, Placeholder.Error<Values[number]>> {
|
|
71
|
+
const instance = RenderTemplate.withEffect((render) => render(template, values))
|
|
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
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import type { Fx } from "@typed/fx/Fx"
|
|
5
|
+
import type { Rendered } from "@typed/wire"
|
|
6
|
+
import type { Effect } from "effect/Effect"
|
|
7
|
+
import type { Placeholder } from "./Placeholder"
|
|
8
|
+
import type { RenderEvent } from "./RenderEvent"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @since 1.0.0
|
|
12
|
+
*/
|
|
13
|
+
export type Renderable<R = never, E = never> =
|
|
14
|
+
| Renderable.Value
|
|
15
|
+
| { readonly [key: string]: Renderable.Value } // TODO: Should we manage data attributes this way?
|
|
16
|
+
| Placeholder<R, E, any>
|
|
17
|
+
| Effect<R, E, any>
|
|
18
|
+
| Fx<R, E, any>
|
|
19
|
+
| ReadonlyArray<Renderable<R, E>>
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @since 1.0.0
|
|
23
|
+
*/
|
|
24
|
+
export namespace Renderable {
|
|
25
|
+
/**
|
|
26
|
+
* @since 1.0.0
|
|
27
|
+
*/
|
|
28
|
+
export type Primitive = string | number | boolean | null | undefined | void | ReadonlyArray<Primitive>
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @since 1.0.0
|
|
32
|
+
*/
|
|
33
|
+
export type Value = Primitive | Rendered | RenderEvent
|
|
34
|
+
}
|
package/src/Template.ts
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import type { Chunk } from "effect/Chunk"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @since 1.0.0
|
|
8
|
+
*/
|
|
9
|
+
export class Template {
|
|
10
|
+
readonly _tag = "template"
|
|
11
|
+
|
|
12
|
+
constructor(
|
|
13
|
+
readonly nodes: ReadonlyArray<Node>,
|
|
14
|
+
readonly hash: string,
|
|
15
|
+
// Parts are a array of Parts to the respective path from the root node to access it during
|
|
16
|
+
readonly parts: ReadonlyArray<readonly [part: PartNode | SparsePartNode, path: Chunk<number>]>
|
|
17
|
+
) {}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @since 1.0.0
|
|
22
|
+
*/
|
|
23
|
+
export type ParentNode = ElementNode | SelfClosingElementNode | TextOnlyElement
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @since 1.0.0
|
|
27
|
+
*/
|
|
28
|
+
export type Node =
|
|
29
|
+
| ElementNode
|
|
30
|
+
| SelfClosingElementNode
|
|
31
|
+
| TextOnlyElement
|
|
32
|
+
| TextNode
|
|
33
|
+
| NodePart
|
|
34
|
+
| Comment
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @since 1.0.0
|
|
38
|
+
*/
|
|
39
|
+
export type PartNode =
|
|
40
|
+
| AttrPartNode
|
|
41
|
+
| BooleanPartNode
|
|
42
|
+
| ClassNamePartNode
|
|
43
|
+
| DataPartNode
|
|
44
|
+
| EventPartNode
|
|
45
|
+
| NodePart
|
|
46
|
+
| PropertyPartNode
|
|
47
|
+
| RefPartNode
|
|
48
|
+
| TextPartNode
|
|
49
|
+
| CommentPartNode
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @since 1.0.0
|
|
53
|
+
*/
|
|
54
|
+
export type SparsePartNode = SparseAttrNode | SparseClassNameNode | SparseCommentNode
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @since 1.0.0
|
|
58
|
+
*/
|
|
59
|
+
export class ElementNode {
|
|
60
|
+
readonly _tag = "element"
|
|
61
|
+
constructor(
|
|
62
|
+
readonly tagName: string,
|
|
63
|
+
readonly attributes: Array<Attribute>,
|
|
64
|
+
readonly children: Array<Node>
|
|
65
|
+
) {}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @since 1.0.0
|
|
70
|
+
*/
|
|
71
|
+
export class NodePart {
|
|
72
|
+
readonly _tag = "node"
|
|
73
|
+
constructor(readonly index: number) {}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @since 1.0.0
|
|
78
|
+
*/
|
|
79
|
+
export class SelfClosingElementNode {
|
|
80
|
+
readonly _tag = "self-closing-element"
|
|
81
|
+
constructor(
|
|
82
|
+
readonly tagName: string,
|
|
83
|
+
readonly attributes: Array<Attribute>
|
|
84
|
+
) {}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @since 1.0.0
|
|
89
|
+
*/
|
|
90
|
+
export class TextOnlyElement {
|
|
91
|
+
readonly _tag = "text-only-element"
|
|
92
|
+
constructor(
|
|
93
|
+
readonly tagName: string,
|
|
94
|
+
readonly attributes: Array<Attribute>,
|
|
95
|
+
readonly children: Array<Text>
|
|
96
|
+
) {}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @since 1.0.0
|
|
101
|
+
*/
|
|
102
|
+
export type Attribute =
|
|
103
|
+
| AttributeNode
|
|
104
|
+
| AttrPartNode
|
|
105
|
+
| SparseAttrNode
|
|
106
|
+
| BooleanNode
|
|
107
|
+
| BooleanPartNode
|
|
108
|
+
| ClassNameNode
|
|
109
|
+
| SparseClassNameNode
|
|
110
|
+
| DataPartNode
|
|
111
|
+
| EventPartNode
|
|
112
|
+
| PropertyPartNode
|
|
113
|
+
| RefPartNode
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @since 1.0.0
|
|
117
|
+
*/
|
|
118
|
+
export class AttributeNode {
|
|
119
|
+
readonly _tag = "attribute" as const
|
|
120
|
+
constructor(
|
|
121
|
+
readonly name: string,
|
|
122
|
+
readonly value: string
|
|
123
|
+
) {}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @since 1.0.0
|
|
128
|
+
*/
|
|
129
|
+
export class AttrPartNode {
|
|
130
|
+
readonly _tag = "attr" as const
|
|
131
|
+
constructor(
|
|
132
|
+
readonly name: string,
|
|
133
|
+
readonly index: number
|
|
134
|
+
) {}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @since 1.0.0
|
|
139
|
+
*/
|
|
140
|
+
export class SparseAttrNode {
|
|
141
|
+
readonly _tag = "sparse-attr" as const
|
|
142
|
+
constructor(
|
|
143
|
+
readonly name: string,
|
|
144
|
+
readonly nodes: Array<AttrPartNode | TextNode>
|
|
145
|
+
) {}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @since 1.0.0
|
|
150
|
+
*/
|
|
151
|
+
export class BooleanNode {
|
|
152
|
+
readonly _tag = "boolean" as const
|
|
153
|
+
constructor(readonly name: string) {}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* @since 1.0.0
|
|
158
|
+
*/
|
|
159
|
+
export class BooleanPartNode {
|
|
160
|
+
readonly _tag = "boolean-part" as const
|
|
161
|
+
constructor(
|
|
162
|
+
readonly name: string,
|
|
163
|
+
readonly index: number
|
|
164
|
+
) {}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @since 1.0.0
|
|
169
|
+
*/
|
|
170
|
+
export type ClassNameNode = TextNode | ClassNamePartNode
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* @since 1.0.0
|
|
174
|
+
*/
|
|
175
|
+
export class ClassNamePartNode {
|
|
176
|
+
readonly _tag = "className-part" as const
|
|
177
|
+
constructor(readonly index: number) {}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* @since 1.0.0
|
|
182
|
+
*/
|
|
183
|
+
export class SparseClassNameNode {
|
|
184
|
+
readonly _tag = "sparse-class-name" as const
|
|
185
|
+
|
|
186
|
+
constructor(readonly nodes: Array<ClassNameNode>) {}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* @since 1.0.0
|
|
191
|
+
*/
|
|
192
|
+
export class DataPartNode {
|
|
193
|
+
readonly _tag = "data" as const
|
|
194
|
+
|
|
195
|
+
constructor(readonly index: number) {}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* @since 1.0.0
|
|
200
|
+
*/
|
|
201
|
+
export class EventPartNode {
|
|
202
|
+
readonly _tag = "event" as const
|
|
203
|
+
|
|
204
|
+
constructor(
|
|
205
|
+
readonly name: string,
|
|
206
|
+
readonly index: number
|
|
207
|
+
) {}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* @since 1.0.0
|
|
212
|
+
*/
|
|
213
|
+
export class PropertyPartNode {
|
|
214
|
+
readonly _tag = "property" as const
|
|
215
|
+
|
|
216
|
+
constructor(
|
|
217
|
+
readonly name: string,
|
|
218
|
+
readonly index: number
|
|
219
|
+
) {}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* @since 1.0.0
|
|
224
|
+
*/
|
|
225
|
+
export class RefPartNode {
|
|
226
|
+
readonly _tag = "ref" as const
|
|
227
|
+
|
|
228
|
+
constructor(readonly index: number) {}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* @since 1.0.0
|
|
233
|
+
*/
|
|
234
|
+
export type Text = TextNode | TextPartNode
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* @since 1.0.0
|
|
238
|
+
*/
|
|
239
|
+
export class TextNode {
|
|
240
|
+
readonly _tag = "text" as const
|
|
241
|
+
|
|
242
|
+
constructor(readonly value: string) {}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* @since 1.0.0
|
|
247
|
+
*/
|
|
248
|
+
export class TextPartNode {
|
|
249
|
+
readonly _tag = "text-part" as const
|
|
250
|
+
|
|
251
|
+
constructor(readonly index: number) {}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* @since 1.0.0
|
|
256
|
+
*/
|
|
257
|
+
export type Comment = CommentNode | CommentPartNode | SparseCommentNode
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* @since 1.0.0
|
|
261
|
+
*/
|
|
262
|
+
export class CommentNode {
|
|
263
|
+
readonly _tag = "comment" as const
|
|
264
|
+
|
|
265
|
+
constructor(readonly value: string) {}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* @since 1.0.0
|
|
270
|
+
*/
|
|
271
|
+
export class CommentPartNode {
|
|
272
|
+
readonly _tag = "comment-part" as const
|
|
273
|
+
|
|
274
|
+
constructor(readonly index: number) {}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* @since 1.0.0
|
|
279
|
+
*/
|
|
280
|
+
export class SparseCommentNode {
|
|
281
|
+
readonly _tag = "sparse-comment" as const
|
|
282
|
+
|
|
283
|
+
constructor(readonly nodes: Array<TextNode | CommentPartNode>) {}
|
|
284
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type * as Fx from "@typed/fx/Fx"
|
|
6
|
+
import { FxEffectBase } from "@typed/fx/internal/protos"
|
|
7
|
+
import type * as Versioned from "@typed/fx/Versioned"
|
|
8
|
+
import type { Rendered } from "@typed/wire"
|
|
9
|
+
import type { NoSuchElementException } from "effect/Cause"
|
|
10
|
+
import type * as Effect from "effect/Effect"
|
|
11
|
+
import { type ElementRef, ElementRefTypeId } from "./ElementRef"
|
|
12
|
+
import type { Placeholder } from "./Placeholder"
|
|
13
|
+
import type { RenderEvent } from "./RenderEvent"
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @since 1.0.0
|
|
17
|
+
*/
|
|
18
|
+
export const TemplateInstanceTypeId = Symbol.for("./TemplateInstance")
|
|
19
|
+
/**
|
|
20
|
+
* @since 1.0.0
|
|
21
|
+
*/
|
|
22
|
+
export type TemplateInstanceTypeId = typeof TemplateInstanceTypeId
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @since 1.0.0
|
|
26
|
+
*/
|
|
27
|
+
export interface TemplateInstance<E, T extends Rendered = Rendered>
|
|
28
|
+
extends Versioned.Versioned<never, never, never, E, RenderEvent, never, E | NoSuchElementException, T>
|
|
29
|
+
{
|
|
30
|
+
readonly [TemplateInstanceTypeId]: TemplateInstanceTypeId
|
|
31
|
+
|
|
32
|
+
readonly query: ElementRef<T>["query"]
|
|
33
|
+
|
|
34
|
+
readonly events: ElementRef<T>["events"]
|
|
35
|
+
|
|
36
|
+
readonly elements: ElementRef<T>["elements"]
|
|
37
|
+
|
|
38
|
+
readonly dispatchEvent: ElementRef<T>["dispatchEvent"]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @since 1.0.0
|
|
43
|
+
*/
|
|
44
|
+
export function TemplateInstance<T extends Rendered = Rendered, E = never>(
|
|
45
|
+
events: Fx.Fx<never, E, RenderEvent>,
|
|
46
|
+
ref: ElementRef<T>
|
|
47
|
+
): TemplateInstance<E, T> {
|
|
48
|
+
return new TemplateInstanceImpl(events, ref) as any
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// @ts-expect-error placeholder issues
|
|
52
|
+
class TemplateInstanceImpl<E, T extends Rendered>
|
|
53
|
+
extends FxEffectBase<never, E, RenderEvent, never, E | NoSuchElementException, T>
|
|
54
|
+
implements Omit<TemplateInstance<E, T>, keyof Placeholder<never, E, RenderEvent>>
|
|
55
|
+
{
|
|
56
|
+
readonly [TemplateInstanceTypeId]: TemplateInstanceTypeId = TemplateInstanceTypeId
|
|
57
|
+
query: TemplateInstance<E, T>["query"]
|
|
58
|
+
events: TemplateInstance<E, T>["events"]
|
|
59
|
+
elements: TemplateInstance<E, T>["elements"]
|
|
60
|
+
dispatchEvent: TemplateInstance<E, T>["dispatchEvent"]
|
|
61
|
+
version: Effect.Effect<never, never, number>
|
|
62
|
+
|
|
63
|
+
constructor(
|
|
64
|
+
readonly i0: Fx.Fx<never, E, RenderEvent>,
|
|
65
|
+
readonly i1: ElementRef<T>
|
|
66
|
+
) {
|
|
67
|
+
super()
|
|
68
|
+
|
|
69
|
+
this.query = this.i1.query
|
|
70
|
+
this.events = this.i1.events
|
|
71
|
+
this.elements = this.i1.elements
|
|
72
|
+
this.dispatchEvent = this.i1.dispatchEvent
|
|
73
|
+
this.version = this.i1[ElementRefTypeId].version
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
toFx(): Fx.Fx<never, E, RenderEvent> {
|
|
77
|
+
return this.i0
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
toEffect(): Effect.Effect<never, E | NoSuchElementException, T> {
|
|
81
|
+
return this.i1
|
|
82
|
+
}
|
|
83
|
+
}
|
package/src/Test.ts
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import type { Document } from "@typed/dom/Document"
|
|
5
|
+
import type { DomServices, DomServicesElementParams } from "@typed/dom/DomServices"
|
|
6
|
+
import type { GlobalThis } from "@typed/dom/GlobalThis"
|
|
7
|
+
import type { Window } from "@typed/dom/Window"
|
|
8
|
+
import type { CurrentEnvironment } from "@typed/environment"
|
|
9
|
+
import type { Computed } from "@typed/fx/Computed"
|
|
10
|
+
import type { Filtered } from "@typed/fx/Filtered"
|
|
11
|
+
import * as Fx from "@typed/fx/Fx"
|
|
12
|
+
import * as RefArray from "@typed/fx/RefArray"
|
|
13
|
+
import * as Sink from "@typed/fx/Sink"
|
|
14
|
+
import * as Cause from "effect/Cause"
|
|
15
|
+
import * as Effect from "effect/Effect"
|
|
16
|
+
import * as Either from "effect/Either"
|
|
17
|
+
import * as Fiber from "effect/Fiber"
|
|
18
|
+
import type * as Scope from "effect/Scope"
|
|
19
|
+
import * as happyDOM from "happy-dom"
|
|
20
|
+
import type IHappyDOMOptions from "happy-dom/lib/window/IHappyDOMOptions.js"
|
|
21
|
+
import * as ElementRef from "./ElementRef"
|
|
22
|
+
import { ROOT_CSS_SELECTOR } from "./ElementSource"
|
|
23
|
+
import { adjustTime } from "./internal/utils"
|
|
24
|
+
import { render } from "./Render"
|
|
25
|
+
import * as RenderContext from "./RenderContext"
|
|
26
|
+
import type { RenderEvent } from "./RenderEvent"
|
|
27
|
+
import type { RenderTemplate } from "./RenderTemplate"
|
|
28
|
+
|
|
29
|
+
// TODO: Instrument RenderTemplate to log info about when specific values are hanging for too long
|
|
30
|
+
// TODO: Input events
|
|
31
|
+
// TODO: Form events
|
|
32
|
+
// TODO: keyboard events
|
|
33
|
+
// TODO: drag and drop events
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @since 1.0.0
|
|
37
|
+
*/
|
|
38
|
+
export interface TestRender<E> {
|
|
39
|
+
readonly window: Window & GlobalThis & Pick<happyDOM.Window, "happyDOM">
|
|
40
|
+
readonly document: Document
|
|
41
|
+
readonly elementRef: ElementRef.ElementRef
|
|
42
|
+
readonly errors: Computed<never, never, ReadonlyArray<E>>
|
|
43
|
+
readonly lastError: Filtered<never, never, E>
|
|
44
|
+
readonly interrupt: Effect.Effect<never, never, void>
|
|
45
|
+
readonly makeEvent: (type: string, eventInitDict?: EventInit) => Event
|
|
46
|
+
readonly makeCustomEvent: <A>(type: string, eventInitDict?: CustomEventInit<A>) => CustomEvent<A>
|
|
47
|
+
readonly dispatchEvent: (options: EventOptions) => Effect.Effect<never, Cause.NoSuchElementException, void>
|
|
48
|
+
readonly click: (options?: Omit<EventOptions, "event">) => Effect.Effect<never, Cause.NoSuchElementException, void>
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @since 1.0.0
|
|
53
|
+
*/
|
|
54
|
+
export function testRender<R, E>(
|
|
55
|
+
fx: Fx.Fx<R, E, RenderEvent>,
|
|
56
|
+
options?:
|
|
57
|
+
& IHappyDOMOptions
|
|
58
|
+
& { readonly [K in keyof DomServicesElementParams]?: (document: Document) => DomServicesElementParams[K] }
|
|
59
|
+
): Effect.Effect<
|
|
60
|
+
Scope.Scope | Exclude<Exclude<R, RenderTemplate>, RenderContext.RenderContext | CurrentEnvironment | DomServices>,
|
|
61
|
+
never,
|
|
62
|
+
TestRender<E>
|
|
63
|
+
> {
|
|
64
|
+
return Effect.gen(function*(_) {
|
|
65
|
+
const window = makeWindow(options)
|
|
66
|
+
const elementRef = yield* _(ElementRef.make())
|
|
67
|
+
const errors = yield* _(RefArray.make<never, never, E>(Effect.succeed([])))
|
|
68
|
+
const fiber = yield* _(
|
|
69
|
+
fx,
|
|
70
|
+
render,
|
|
71
|
+
Fx.run(Sink.Sink(
|
|
72
|
+
(cause) =>
|
|
73
|
+
Cause.failureOrCause(cause).pipe(
|
|
74
|
+
Either.match({
|
|
75
|
+
onLeft: (error) => RefArray.append(errors, error),
|
|
76
|
+
onRight: (cause) => errors.onFailure(cause)
|
|
77
|
+
})
|
|
78
|
+
),
|
|
79
|
+
(rendered) => ElementRef.set(elementRef, rendered)
|
|
80
|
+
)),
|
|
81
|
+
Effect.forkScoped,
|
|
82
|
+
Effect.provide(RenderContext.browser(window, { skipRenderScheduling: true }))
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
const test: TestRender<E> = {
|
|
86
|
+
window,
|
|
87
|
+
document: window.document,
|
|
88
|
+
elementRef,
|
|
89
|
+
errors,
|
|
90
|
+
lastError: RefArray.last(errors),
|
|
91
|
+
interrupt: Fiber.interrupt(fiber),
|
|
92
|
+
makeEvent: (type, init) => new window.Event(type, init),
|
|
93
|
+
makeCustomEvent: (type, init) => new window.CustomEvent(type, init),
|
|
94
|
+
dispatchEvent: (options) => dispatchEvent(test, options),
|
|
95
|
+
click: (options) => click(test, options)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Allow our fibers to start
|
|
99
|
+
yield* _(adjustTime(1))
|
|
100
|
+
yield* _(adjustTime(1))
|
|
101
|
+
|
|
102
|
+
return test
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @since 1.0.0
|
|
108
|
+
*/
|
|
109
|
+
export type EventOptions = {
|
|
110
|
+
readonly event: string
|
|
111
|
+
readonly selector?: string
|
|
112
|
+
readonly eventInit?: EventInit
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// TODO: Find more events to add here
|
|
116
|
+
const NON_BUBBLING_EVENTS = new Set(["focus", "blur"])
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @since 1.0.0
|
|
120
|
+
*/
|
|
121
|
+
export function dispatchEvent<E>(
|
|
122
|
+
{ elementRef, makeEvent }: Pick<TestRender<E>, "elementRef" | "makeEvent">,
|
|
123
|
+
options: EventOptions
|
|
124
|
+
) {
|
|
125
|
+
const selector = options.selector ?? ROOT_CSS_SELECTOR
|
|
126
|
+
|
|
127
|
+
return elementRef.query(selector).dispatchEvent(
|
|
128
|
+
makeEvent(options.event, {
|
|
129
|
+
bubbles: !NON_BUBBLING_EVENTS.has(selector),
|
|
130
|
+
cancelable: true,
|
|
131
|
+
composed: false,
|
|
132
|
+
...options?.eventInit
|
|
133
|
+
})
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @since 1.0.0
|
|
139
|
+
*/
|
|
140
|
+
export function click<E>(
|
|
141
|
+
rendered: Pick<TestRender<E>, "elementRef" | "makeEvent">,
|
|
142
|
+
options?: Omit<EventOptions, "event">
|
|
143
|
+
) {
|
|
144
|
+
return dispatchEvent(rendered, { event: "click", ...options, eventInit: { bubbles: true, ...options?.eventInit } })
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// internals
|
|
148
|
+
|
|
149
|
+
function makeWindow(options?: IHappyDOMOptions) {
|
|
150
|
+
return new happyDOM.Window(options) as any as Window & GlobalThis & Pick<happyDOM.Window, "happyDOM">
|
|
151
|
+
}
|