@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
package/src/Many.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import type * as AsyncData from "@typed/async-data/AsyncData"
|
|
5
|
+
import type * as Computed from "@typed/fx/Computed"
|
|
6
|
+
import * as Fx from "@typed/fx/Fx"
|
|
7
|
+
import { makeHoldSubject } from "@typed/fx/internal/core-subject"
|
|
8
|
+
import * as RefAsyncData from "@typed/fx/RefAsyncData"
|
|
9
|
+
import * as RefSubject from "@typed/fx/RefSubject"
|
|
10
|
+
import * as Effect from "effect/Effect"
|
|
11
|
+
import { dual } from "effect/Function"
|
|
12
|
+
import { RenderContext } from "./RenderContext"
|
|
13
|
+
import { type RenderEvent } from "./RenderEvent"
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @since 1.0.0
|
|
17
|
+
*/
|
|
18
|
+
export function many<R, E, A, B, R2, E2>(
|
|
19
|
+
values: Fx.Fx<R, E, ReadonlyArray<A>>,
|
|
20
|
+
getKey: (a: A) => B,
|
|
21
|
+
f: (a: RefSubject.RefSubject<never, never, A>, key: B) => Fx.Fx<R2, E2, RenderEvent>
|
|
22
|
+
): Fx.Fx<R | R2 | RenderContext, E | E2, RenderEvent | ReadonlyArray<RenderEvent>> {
|
|
23
|
+
return Fx.fromFxEffect(
|
|
24
|
+
RenderContext.with((ctx): Fx.Fx<R | R2 | RenderContext, E | E2, RenderEvent | ReadonlyArray<RenderEvent>> => {
|
|
25
|
+
if (ctx.environment === "browser") {
|
|
26
|
+
return Fx.keyed(values, getKey, f)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return Fx.fromFxEffect(
|
|
30
|
+
Effect.map(Fx.first(values), (values) =>
|
|
31
|
+
values._tag === "None" ? Fx.empty : Fx.mergeBuffer(
|
|
32
|
+
values.value.map((value) => {
|
|
33
|
+
const ref = RefSubject.unsafeMake(Effect.succeed(value), makeHoldSubject())
|
|
34
|
+
|
|
35
|
+
return f({ ...ref, ...Fx.take(ref, 1) } as RefSubject.RefSubject<never, never, A>, getKey(value))
|
|
36
|
+
})
|
|
37
|
+
))
|
|
38
|
+
)
|
|
39
|
+
})
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @since 1.0.0
|
|
45
|
+
*/
|
|
46
|
+
export const manyAsyncData: {
|
|
47
|
+
<
|
|
48
|
+
E1,
|
|
49
|
+
A,
|
|
50
|
+
B,
|
|
51
|
+
NoData extends Fx.FxInput<any, any, any>,
|
|
52
|
+
Loading extends Fx.FxInput<any, any, any>,
|
|
53
|
+
Failure extends Fx.FxInput<any, any, any>,
|
|
54
|
+
Success extends Fx.FxInput<any, any, any>
|
|
55
|
+
>(
|
|
56
|
+
getKey: (a: A) => B,
|
|
57
|
+
matchers: {
|
|
58
|
+
NoData: () => NoData
|
|
59
|
+
Loading: (data: RefAsyncData.LoadingComputed) => Loading
|
|
60
|
+
Failure: (data: Computed.Computed<never, never, E1>, computed: RefAsyncData.FailureComputed<E1>) => Failure
|
|
61
|
+
Success: (value: Computed.Computed<never, never, A>, computed: RefAsyncData.SuccessComputed) => Success
|
|
62
|
+
}
|
|
63
|
+
): <R, E>(
|
|
64
|
+
fx: Fx.Fx<R, E, AsyncData.AsyncData<E1, ReadonlyArray<A>>>
|
|
65
|
+
) => Fx.Fx<
|
|
66
|
+
R | Fx.Fx.Context<NoData | Loading | Failure | Success>,
|
|
67
|
+
E | Fx.Fx.Error<NoData | Loading | Failure | Success>,
|
|
68
|
+
Fx.Fx.Success<NoData | Loading | Failure | Success>
|
|
69
|
+
>
|
|
70
|
+
|
|
71
|
+
<
|
|
72
|
+
R,
|
|
73
|
+
E,
|
|
74
|
+
E1,
|
|
75
|
+
A,
|
|
76
|
+
B,
|
|
77
|
+
NoData extends Fx.Fx<any, any, any>,
|
|
78
|
+
Loading extends Fx.Fx<any, any, any>,
|
|
79
|
+
Failure extends Fx.Fx<any, any, any>,
|
|
80
|
+
Success extends Fx.Fx<any, any, any>
|
|
81
|
+
>(
|
|
82
|
+
fx: Fx.Fx<R, E, AsyncData.AsyncData<E1, ReadonlyArray<A>>>,
|
|
83
|
+
getKey: (a: A) => B,
|
|
84
|
+
matchers: {
|
|
85
|
+
NoData: () => NoData
|
|
86
|
+
Loading: (data: RefAsyncData.LoadingComputed) => Loading
|
|
87
|
+
Failure: (data: Computed.Computed<never, never, E1>, computed: RefAsyncData.FailureComputed<E1>) => Failure
|
|
88
|
+
Success: (value: Computed.Computed<never, never, A>, computed: RefAsyncData.SuccessComputed) => Success
|
|
89
|
+
}
|
|
90
|
+
): Fx.Fx<
|
|
91
|
+
R | Fx.Fx.Context<NoData | Loading | Failure | Success>,
|
|
92
|
+
E | Fx.Fx.Error<NoData | Loading | Failure | Success>,
|
|
93
|
+
Fx.Fx.Success<NoData | Loading | Failure | Success>
|
|
94
|
+
>
|
|
95
|
+
} = dual(
|
|
96
|
+
3,
|
|
97
|
+
<
|
|
98
|
+
R,
|
|
99
|
+
E,
|
|
100
|
+
E1,
|
|
101
|
+
A,
|
|
102
|
+
NoData extends Fx.Fx<any, any, RenderEvent>,
|
|
103
|
+
Loading extends Fx.Fx<any, any, RenderEvent>,
|
|
104
|
+
Failure extends Fx.Fx<any, any, RenderEvent>,
|
|
105
|
+
Success extends Fx.Fx<any, any, RenderEvent>,
|
|
106
|
+
B
|
|
107
|
+
>(
|
|
108
|
+
fx: Fx.Fx<R, E, AsyncData.AsyncData<E1, ReadonlyArray<A>>>,
|
|
109
|
+
matchers: {
|
|
110
|
+
NoData: () => NoData
|
|
111
|
+
Loading: (data: RefAsyncData.LoadingComputed) => Loading
|
|
112
|
+
Failure: (data: Computed.Computed<never, never, E1>, computed: RefAsyncData.FailureComputed<E1>) => Failure
|
|
113
|
+
Success: (value: Computed.Computed<never, never, A>, computed: RefAsyncData.SuccessComputed) => Success
|
|
114
|
+
},
|
|
115
|
+
getKey: (a: A) => B
|
|
116
|
+
): Fx.Fx<
|
|
117
|
+
R | Fx.Fx.Context<NoData | Loading | Failure | Success>,
|
|
118
|
+
E | Fx.Fx.Error<NoData | Loading | Failure | Success>,
|
|
119
|
+
Fx.Fx.Success<NoData | Loading | Failure | Success>
|
|
120
|
+
> => {
|
|
121
|
+
return RefAsyncData.matchKeyed(fx, {
|
|
122
|
+
NoData: matchers.NoData,
|
|
123
|
+
Loading: matchers.Loading,
|
|
124
|
+
Failure: matchers.Failure,
|
|
125
|
+
Success: (ref, computed) => many(ref, getKey, (ref) => matchers.Success(ref, computed))
|
|
126
|
+
}) as any
|
|
127
|
+
}
|
|
128
|
+
)
|
package/src/Meta.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Used to mark where the typed templates are in the html.
|
|
6
|
+
/**
|
|
7
|
+
* @since 1.0.0
|
|
8
|
+
*/
|
|
9
|
+
export const TYPED_START = `<!--typed-start-->`
|
|
10
|
+
/**
|
|
11
|
+
* @since 1.0.0
|
|
12
|
+
*/
|
|
13
|
+
export const TYPED_END = `<!--typed-end-->`
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @since 1.0.0
|
|
17
|
+
*/
|
|
18
|
+
export const TYPED_HASH = (hash: string) => `data-typed="${hash}"`
|
|
19
|
+
|
|
20
|
+
// Inserted into the html to mark where the text starts and ensure separate
|
|
21
|
+
// text nodes are created.
|
|
22
|
+
/**
|
|
23
|
+
* @since 1.0.0
|
|
24
|
+
*/
|
|
25
|
+
export const TEXT_START = "<!--text-->"
|
|
26
|
+
|
|
27
|
+
// Used to mark where a NodePart is in the DOM. It can be found directly after the
|
|
28
|
+
// elements or text nodes it is managing.
|
|
29
|
+
/**
|
|
30
|
+
* @since 1.0.0
|
|
31
|
+
*/
|
|
32
|
+
export const TYPED_HOLE = (index: number) => `<!--hole${index}-->`
|
package/src/Parser.ts
ADDED
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as Chunk from "effect/Chunk"
|
|
6
|
+
import { globalValue } from "effect/GlobalValue"
|
|
7
|
+
import { templateHash } from "./internal/parser"
|
|
8
|
+
import * as Template from "./Template"
|
|
9
|
+
import type { Token } from "./Token"
|
|
10
|
+
import { tokenize } from "./Tokenizer"
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @since 1.0.0
|
|
14
|
+
*/
|
|
15
|
+
export interface Parser {
|
|
16
|
+
parse(template: ReadonlyArray<string>, tokenStream?: Iterator<Token>): Template.Template
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const iterator = <A>(iterable: Iterable<A>): Iterator<A> => iterable[Symbol.iterator]()
|
|
20
|
+
|
|
21
|
+
const dropLast = Chunk.dropRight(1)
|
|
22
|
+
|
|
23
|
+
class ParserImpl {
|
|
24
|
+
protected _template: ReadonlyArray<string> = []
|
|
25
|
+
protected _tokenStream!: Iterator<Token>
|
|
26
|
+
protected _lookahead!: Token | null
|
|
27
|
+
protected _stack!: Chunk.Chunk<number>
|
|
28
|
+
protected _parts!: Array<[Template.PartNode | Template.SparsePartNode, Chunk.Chunk<number>]>
|
|
29
|
+
protected _skipWhitespace!: boolean
|
|
30
|
+
|
|
31
|
+
parse(
|
|
32
|
+
template: ReadonlyArray<string>,
|
|
33
|
+
tokenStream: Iterator<Token> = iterator(tokenize(template))
|
|
34
|
+
): Template.Template {
|
|
35
|
+
this._template = template
|
|
36
|
+
this._tokenStream = tokenStream
|
|
37
|
+
this._lookahead = this.getNextToken()
|
|
38
|
+
this._stack = Chunk.empty()
|
|
39
|
+
this._parts = []
|
|
40
|
+
this._skipWhitespace = true
|
|
41
|
+
|
|
42
|
+
const ast = this.Template(templateHash(template))
|
|
43
|
+
|
|
44
|
+
return ast
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
protected Template(hash: string): Template.Template {
|
|
48
|
+
const template = new Template.Template(this.Children(), hash, this._parts)
|
|
49
|
+
|
|
50
|
+
return template
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
protected Node(): Template.Node | null {
|
|
54
|
+
const token = this.findTokenOfType(
|
|
55
|
+
"opening-tag",
|
|
56
|
+
"text",
|
|
57
|
+
"comment",
|
|
58
|
+
"comment-start",
|
|
59
|
+
"part-token",
|
|
60
|
+
"closing-tag"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
if (token._tag === "closing-tag") return null
|
|
64
|
+
|
|
65
|
+
if (token._tag === "text") {
|
|
66
|
+
if (this._skipWhitespace && token.value.trim() === "") return null
|
|
67
|
+
|
|
68
|
+
return new Template.TextNode(token.value)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (token._tag === "comment") {
|
|
72
|
+
return new Template.CommentNode(token.value)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (token._tag === "comment-start") {
|
|
76
|
+
const node = this.Comment("")
|
|
77
|
+
return node
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Some annoyances here for generating the correct path for node parts
|
|
81
|
+
if (token._tag === "part-token") {
|
|
82
|
+
this._skipWhitespace = false
|
|
83
|
+
return this.addPartWithoutCurrent(new Template.NodePart(token.index))
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
this._skipWhitespace = true
|
|
87
|
+
|
|
88
|
+
if (token.isSelfClosing) {
|
|
89
|
+
return this.SelfClosingElementNode(token.name)
|
|
90
|
+
} else if (token.textOnly) {
|
|
91
|
+
return this.TextOnlyElement(token.name)
|
|
92
|
+
} else {
|
|
93
|
+
return this.ElementNode(token.name)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
protected ElementNode(tagName: string): Template.ElementNode {
|
|
98
|
+
const attrs: Array<Template.Attribute> = []
|
|
99
|
+
const children: Array<Template.Node> = []
|
|
100
|
+
const element = new Template.ElementNode(tagName, attrs, children)
|
|
101
|
+
|
|
102
|
+
this.Attributes(attrs)
|
|
103
|
+
this.Children(children)
|
|
104
|
+
|
|
105
|
+
return element
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
protected SelfClosingElementNode(tagName: string): Template.SelfClosingElementNode {
|
|
109
|
+
const attrs: Array<Template.Attribute> = []
|
|
110
|
+
const element = new Template.SelfClosingElementNode(tagName, attrs)
|
|
111
|
+
|
|
112
|
+
this.Attributes(attrs)
|
|
113
|
+
|
|
114
|
+
return element
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
protected TextOnlyElement(tagName: string): Template.TextOnlyElement {
|
|
118
|
+
const attrs: Array<Template.Attribute> = []
|
|
119
|
+
const children: Array<Template.Text> = []
|
|
120
|
+
const element = new Template.TextOnlyElement(tagName, attrs, children)
|
|
121
|
+
|
|
122
|
+
this.Attributes(attrs)
|
|
123
|
+
this.TextChildren(children)
|
|
124
|
+
|
|
125
|
+
return element
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
protected Attributes(attributes: Array<Template.Attribute> = []): Array<Template.Attribute> {
|
|
129
|
+
while (this._lookahead !== null) {
|
|
130
|
+
if (this._lookahead._tag === "opening-tag-end") {
|
|
131
|
+
this._lookahead = this.getNextToken()
|
|
132
|
+
break
|
|
133
|
+
} else {
|
|
134
|
+
const attr = this.Attribute()
|
|
135
|
+
if (attr) {
|
|
136
|
+
attributes.push(attr)
|
|
137
|
+
} else {
|
|
138
|
+
break
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return attributes
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
protected Attribute(): Template.Attribute | null {
|
|
147
|
+
const token = this.findTokenOfType(
|
|
148
|
+
"attribute",
|
|
149
|
+
"attribute-start",
|
|
150
|
+
"boolean-attribute",
|
|
151
|
+
"boolean-attribute-start",
|
|
152
|
+
"className-attribute-start",
|
|
153
|
+
"data-attribute-start",
|
|
154
|
+
"event-attribute-start",
|
|
155
|
+
"property-attribute-start",
|
|
156
|
+
"ref-attribute-start",
|
|
157
|
+
"text"
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
switch (token._tag) {
|
|
161
|
+
case "attribute":
|
|
162
|
+
return new Template.AttributeNode(token.name, token.value)
|
|
163
|
+
case "attribute-start":
|
|
164
|
+
return this.SparseAttrNode(token.name)
|
|
165
|
+
case "boolean-attribute":
|
|
166
|
+
return new Template.BooleanNode(token.name)
|
|
167
|
+
case "boolean-attribute-start":
|
|
168
|
+
return this.BooleanNode(token.name)
|
|
169
|
+
case "className-attribute-start":
|
|
170
|
+
return this.SparseClassNameNode()
|
|
171
|
+
case "data-attribute-start":
|
|
172
|
+
return this.DataNode()
|
|
173
|
+
case "event-attribute-start":
|
|
174
|
+
return this.EventNode(token.name)
|
|
175
|
+
case "property-attribute-start":
|
|
176
|
+
return this.PropertyNode(token.name)
|
|
177
|
+
case "ref-attribute-start":
|
|
178
|
+
return this.RefNode()
|
|
179
|
+
case "text":
|
|
180
|
+
return null
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
protected SparseAttrNode(name: string): Template.SparseAttrNode | Template.AttrPartNode | Template.BooleanNode {
|
|
185
|
+
const nodes: Array<Template.AttrPartNode | Template.TextNode> = []
|
|
186
|
+
|
|
187
|
+
while (this._lookahead !== null) {
|
|
188
|
+
const token = this.findTokenOfType("text", "part-token", "attribute-end")
|
|
189
|
+
|
|
190
|
+
if (token._tag === "text") {
|
|
191
|
+
if (token.value === "") continue
|
|
192
|
+
nodes.push(new Template.TextNode(token.value))
|
|
193
|
+
} else if (token._tag === "part-token") {
|
|
194
|
+
nodes.push(new Template.AttrPartNode(name, token.index))
|
|
195
|
+
} else {
|
|
196
|
+
if (nodes.length === 0) {
|
|
197
|
+
return new Template.BooleanNode(name)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
break
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (nodes.length === 1) {
|
|
205
|
+
return this.addPart(nodes[0] as Template.AttrPartNode)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (nodes.length === 0) {
|
|
209
|
+
return new Template.BooleanNode(name)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return this.addPart(new Template.SparseAttrNode(name, nodes))
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
protected BooleanNode(name: string): Template.BooleanPartNode {
|
|
216
|
+
// We know that the next token MUST be a part-token
|
|
217
|
+
const part = this.predictNextToken("part-token")
|
|
218
|
+
|
|
219
|
+
// We don't need to do anything with a boolean-attribute-end token, skip it
|
|
220
|
+
this.skipIfNextToken("boolean-attribute-end")
|
|
221
|
+
|
|
222
|
+
return this.addPart(new Template.BooleanPartNode(name, part.index))
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
protected SparseClassNameNode(): Template.SparseClassNameNode | Template.ClassNameNode {
|
|
226
|
+
const nodes: Array<Template.ClassNameNode> = []
|
|
227
|
+
|
|
228
|
+
while (this._lookahead !== null) {
|
|
229
|
+
const token = this.findTokenOfType("text", "part-token", "className-attribute-end")
|
|
230
|
+
|
|
231
|
+
if (token._tag === "text") {
|
|
232
|
+
if (token.value.trim() === "") continue
|
|
233
|
+
nodes.push(new Template.TextNode(token.value))
|
|
234
|
+
} else if (token._tag === "part-token") {
|
|
235
|
+
nodes.push(new Template.ClassNamePartNode(token.index))
|
|
236
|
+
} else {
|
|
237
|
+
break
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (nodes.length === 0) {
|
|
242
|
+
throw new SyntaxError("Expected at least one node in class name attribute")
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (nodes.length === 1) {
|
|
246
|
+
return this.addPart(nodes[0] as Template.ClassNamePartNode)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return this.addPart(new Template.SparseClassNameNode(nodes))
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
protected DataNode(): Template.DataPartNode {
|
|
253
|
+
const part = this.predictNextToken("part-token")
|
|
254
|
+
|
|
255
|
+
// We don't need to do anything with a data-attribute-end token, skip it
|
|
256
|
+
this.skipIfNextToken("data-attribute-end")
|
|
257
|
+
|
|
258
|
+
return this.addPart(new Template.DataPartNode(part.index))
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
protected EventNode(name: string): Template.EventPartNode {
|
|
262
|
+
const part = this.predictNextToken("part-token")
|
|
263
|
+
|
|
264
|
+
// We don't need to do anything with a event-attribute-end token, skip it
|
|
265
|
+
this.skipIfNextToken("event-attribute-end")
|
|
266
|
+
|
|
267
|
+
return this.addPart(new Template.EventPartNode(name, part.index))
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
protected PropertyNode(name: string): Template.PropertyPartNode {
|
|
271
|
+
const part = this.predictNextToken("part-token")
|
|
272
|
+
|
|
273
|
+
// We don't need to do anything with a property-attribute-end token, skip it
|
|
274
|
+
this.skipIfNextToken("property-attribute-end")
|
|
275
|
+
|
|
276
|
+
return this.addPart(new Template.PropertyPartNode(name, part.index))
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
protected RefNode(): Template.RefPartNode {
|
|
280
|
+
const part = this.predictNextToken("part-token")
|
|
281
|
+
|
|
282
|
+
// We don't need to do anything with a ref-attribute-end token, skip it
|
|
283
|
+
this.skipIfNextToken("ref-attribute-end")
|
|
284
|
+
|
|
285
|
+
return this.addPart(new Template.RefPartNode(part.index))
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
protected TextPartNode(): Template.TextPartNode {
|
|
289
|
+
const token = this.predictNextToken("part-token")
|
|
290
|
+
|
|
291
|
+
return this.addPart(new Template.TextPartNode(token.index))
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
protected Children(children: Array<Template.Node> = []): Array<Template.Node> {
|
|
295
|
+
let i = 0
|
|
296
|
+
while (this._lookahead) {
|
|
297
|
+
if (this._lookahead._tag === "closing-tag") {
|
|
298
|
+
this._lookahead = this.getNextToken()
|
|
299
|
+
this._skipWhitespace = true
|
|
300
|
+
|
|
301
|
+
break
|
|
302
|
+
} else if (
|
|
303
|
+
this._skipWhitespace &&
|
|
304
|
+
this._lookahead._tag === "text" &&
|
|
305
|
+
this._lookahead.value.trim() === ""
|
|
306
|
+
) {
|
|
307
|
+
this._lookahead = this.getNextToken()
|
|
308
|
+
} else {
|
|
309
|
+
this._stack = Chunk.append(this._stack, i++)
|
|
310
|
+
const child = this.Node()
|
|
311
|
+
this._stack = Chunk.dropRight(this._stack, 1)
|
|
312
|
+
|
|
313
|
+
if (child) {
|
|
314
|
+
children.push(child)
|
|
315
|
+
} else {
|
|
316
|
+
break
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return children
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
protected TextChildren(children: Array<Template.Text> = []): Array<Template.Text> {
|
|
325
|
+
while (this._lookahead !== null) {
|
|
326
|
+
const token = this.findTokenOfType("text", "part-token", "closing-tag")
|
|
327
|
+
|
|
328
|
+
if (token._tag === "text") {
|
|
329
|
+
if (token.value) children.push(new Template.TextNode(token.value))
|
|
330
|
+
} else if (token._tag === `part-token`) {
|
|
331
|
+
children.push(this.addPart(new Template.TextPartNode(token.index)))
|
|
332
|
+
} else {
|
|
333
|
+
this._skipWhitespace = true
|
|
334
|
+
break
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return children
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
protected Comment(before: string): Template.Comment {
|
|
342
|
+
const nodes: Array<Template.CommentPartNode | Template.TextNode> = []
|
|
343
|
+
|
|
344
|
+
if (before) {
|
|
345
|
+
nodes.push(new Template.TextNode(before))
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
while (this._lookahead !== null) {
|
|
349
|
+
const token = this.findTokenOfType("part-token", "text", "comment-end")
|
|
350
|
+
|
|
351
|
+
if (token._tag === "part-token") {
|
|
352
|
+
nodes.push(new Template.CommentPartNode(token.index))
|
|
353
|
+
} else if (token._tag === "text") {
|
|
354
|
+
nodes.push(new Template.TextNode(token.value))
|
|
355
|
+
} else {
|
|
356
|
+
break
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (nodes.length === 1) {
|
|
361
|
+
const node = nodes[0]
|
|
362
|
+
|
|
363
|
+
if (node._tag === "comment-part") {
|
|
364
|
+
return new Template.CommentPartNode(node.index)
|
|
365
|
+
} else {
|
|
366
|
+
return new Template.CommentNode(node.value)
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return this.addPart(new Template.SparseCommentNode(nodes))
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
protected findTokenOfType<T extends ReadonlyArray<Token["_tag"]>>(
|
|
374
|
+
...tokenTypes: T
|
|
375
|
+
): Extract<Token, { readonly _tag: T[number] }> {
|
|
376
|
+
const token = this._lookahead
|
|
377
|
+
|
|
378
|
+
if (token === null) {
|
|
379
|
+
throw new SyntaxError(
|
|
380
|
+
`Unexpected end of template. Expected one of types ${tokenTypes.join(", ")}.`
|
|
381
|
+
)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
for (let i = 0; i < tokenTypes.length; i++) {
|
|
385
|
+
if (token._tag === tokenTypes[i]) {
|
|
386
|
+
this._lookahead = this.getNextToken()
|
|
387
|
+
|
|
388
|
+
return token as Extract<Token, { readonly _tag: T[number] }>
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
throw new SyntaxError(
|
|
393
|
+
`Unexpected token ${token._tag}. Expected one of types ${tokenTypes.join(", ")}.`
|
|
394
|
+
)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
protected predictNextToken<T extends Token["_tag"]>(
|
|
398
|
+
type: T
|
|
399
|
+
): Extract<Token, { readonly _tag: T }> {
|
|
400
|
+
const token = this._lookahead
|
|
401
|
+
|
|
402
|
+
if (token === null) {
|
|
403
|
+
throw new SyntaxError(`Unexpected end of template. Expected ${type}.`)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (token._tag !== type) {
|
|
407
|
+
throw new SyntaxError(`Unexpected token ${token._tag}. Expected ${type}.`)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
this._lookahead = this.getNextToken()
|
|
411
|
+
|
|
412
|
+
return token as Extract<Token, { readonly _tag: T }>
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
protected skipIfNextToken<T extends Token["_tag"]>(type: T): boolean {
|
|
416
|
+
const token = this._lookahead
|
|
417
|
+
|
|
418
|
+
if (token === null) {
|
|
419
|
+
return false
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (token._tag !== type) {
|
|
423
|
+
return false
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
this._lookahead = this.getNextToken()
|
|
427
|
+
|
|
428
|
+
return true
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
protected getNextToken(): Token | null {
|
|
432
|
+
const { done, value } = this._tokenStream.next()
|
|
433
|
+
|
|
434
|
+
if (done) {
|
|
435
|
+
return null
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return value
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
protected addPart<A extends Template.PartNode | Template.SparsePartNode>(part: A): A {
|
|
442
|
+
this._parts.push([part, this._stack])
|
|
443
|
+
|
|
444
|
+
return part
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
protected addPartWithoutCurrent<A extends Template.PartNode>(part: A): A {
|
|
448
|
+
this._parts.push([part, dropLast(this._stack)])
|
|
449
|
+
|
|
450
|
+
return part
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* @since 1.0.0
|
|
456
|
+
*/
|
|
457
|
+
export const parser: Parser = globalValue(Symbol.for("./Parser"), () => new ParserImpl())
|