@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,293 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect"
|
|
2
|
+
import type { HtmlChunk, PartChunk, SparsePartChunk } from "../HtmlChunk"
|
|
3
|
+
import type {
|
|
4
|
+
AttributePart,
|
|
5
|
+
ClassNamePart,
|
|
6
|
+
CommentPart,
|
|
7
|
+
Part,
|
|
8
|
+
SparseAttributePart,
|
|
9
|
+
SparseClassNamePart,
|
|
10
|
+
SparseCommentPart,
|
|
11
|
+
SparsePart,
|
|
12
|
+
StaticText
|
|
13
|
+
} from "../Part"
|
|
14
|
+
import type { Renderable } from "../Renderable"
|
|
15
|
+
import type { PartNode, SparseAttrNode, SparseClassNameNode, SparseCommentNode, SparsePartNode } from "../Template"
|
|
16
|
+
import {
|
|
17
|
+
AttributePartImpl,
|
|
18
|
+
BooleanPartImpl,
|
|
19
|
+
ClassNamePartImpl,
|
|
20
|
+
CommentPartImpl,
|
|
21
|
+
DataPartImpl,
|
|
22
|
+
NodePartImpl,
|
|
23
|
+
PropertyPartImpl,
|
|
24
|
+
SparseAttributePartImpl,
|
|
25
|
+
SparseClassNamePartImpl,
|
|
26
|
+
SparseCommentPartImpl,
|
|
27
|
+
StaticTextImpl,
|
|
28
|
+
TextPartImpl
|
|
29
|
+
} from "./parts"
|
|
30
|
+
|
|
31
|
+
export type RenderChunk<R, E> =
|
|
32
|
+
| TextRenderChunk
|
|
33
|
+
| PartRenderChunk<R, E>
|
|
34
|
+
| SparsePartRenderChunk<R, E>
|
|
35
|
+
|
|
36
|
+
export class TextRenderChunk {
|
|
37
|
+
readonly type = "text"
|
|
38
|
+
|
|
39
|
+
constructor(
|
|
40
|
+
readonly index: number,
|
|
41
|
+
readonly value: string
|
|
42
|
+
) {}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class PartRenderChunk<R, E> {
|
|
46
|
+
readonly type = "part"
|
|
47
|
+
|
|
48
|
+
constructor(
|
|
49
|
+
readonly index: number,
|
|
50
|
+
readonly chunk: PartChunk,
|
|
51
|
+
readonly part: Part,
|
|
52
|
+
readonly renderable: Renderable<R, E>
|
|
53
|
+
) {}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export class SparsePartRenderChunk<R, E> {
|
|
57
|
+
readonly type = "sparse-part"
|
|
58
|
+
|
|
59
|
+
constructor(
|
|
60
|
+
readonly index: number,
|
|
61
|
+
readonly chunk: SparsePartChunk,
|
|
62
|
+
readonly part: SparsePart,
|
|
63
|
+
readonly renderables: Array<Renderable<R, E>>
|
|
64
|
+
) {}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
type RenderChunkMap = {
|
|
68
|
+
readonly [K in HtmlChunk["_tag"]]: <R, E>(
|
|
69
|
+
chunk: Extract<HtmlChunk, { _tag: K }>,
|
|
70
|
+
index: number,
|
|
71
|
+
values: ReadonlyArray<Renderable<any, any>>,
|
|
72
|
+
onChunk: (index: number, value: string) => Effect.Effect<never, never, void>
|
|
73
|
+
) => RenderChunk<R, E>
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const renderChunkMap: RenderChunkMap = {
|
|
77
|
+
text: (chunk, index) => new TextRenderChunk(index, chunk.value),
|
|
78
|
+
part: (chunk, index, values, onChunk) =>
|
|
79
|
+
// @ts-expect-error
|
|
80
|
+
new PartRenderChunk(
|
|
81
|
+
index,
|
|
82
|
+
chunk,
|
|
83
|
+
partNodeToPart(chunk.node, (v) => onChunk(index, chunk.render(v))),
|
|
84
|
+
values[chunk.node.index]
|
|
85
|
+
),
|
|
86
|
+
"sparse-part": (chunk, index, values, onChunk) =>
|
|
87
|
+
// @ts-expect-error
|
|
88
|
+
new SparsePartRenderChunk(
|
|
89
|
+
index,
|
|
90
|
+
chunk,
|
|
91
|
+
sparsePartNodeToPart(chunk.node, (v) => onChunk(index, chunk.render(v))),
|
|
92
|
+
// @ts-expect-error
|
|
93
|
+
chunk.node.nodes.map((n) => (n._tag === "text" ? n.value : values[n.index]))
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function htmlChunksToRenderChunks<R, E>(
|
|
98
|
+
chunks: ReadonlyArray<HtmlChunk>,
|
|
99
|
+
values: ReadonlyArray<Renderable<R, E>>,
|
|
100
|
+
onChunk: (index: number, value: string) => Effect.Effect<never, never, void>
|
|
101
|
+
) {
|
|
102
|
+
const output: Array<RenderChunk<R, E>> = Array(chunks.length)
|
|
103
|
+
|
|
104
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
105
|
+
// @ts-expect-error
|
|
106
|
+
output[i] = renderChunkMap[chunks[i]._tag](chunks[i] as any, i, values, onChunk)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return output
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
type PartNodeMap = {
|
|
113
|
+
readonly [K in PartNode["_tag"]]: (
|
|
114
|
+
node: Extract<PartNode, { _tag: K }>,
|
|
115
|
+
onChunk: (value: unknown) => Effect.Effect<never, never, void>
|
|
116
|
+
) => Part
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const partNodeMap: PartNodeMap = {
|
|
120
|
+
attr: (node, onChunk) => new AttributePartImpl(node.name, node.index, ({ value }) => onChunk(value), null),
|
|
121
|
+
"boolean-part": (node, onChunk) => new BooleanPartImpl(node.name, node.index, ({ value }) => onChunk(value), null),
|
|
122
|
+
"className-part": (node, onChunk) => new ClassNamePartImpl(node.index, ({ value }) => onChunk(value), null),
|
|
123
|
+
"comment-part": (node, onChunk) => new CommentPartImpl(node.index, ({ value }) => onChunk(value), null),
|
|
124
|
+
"data": (node, onChunk) => new DataPartImpl(node.index, ({ value }) => onChunk(value), null),
|
|
125
|
+
"event": () => {
|
|
126
|
+
throw new Error("Events are not utilized on the server")
|
|
127
|
+
},
|
|
128
|
+
"node": (node, onChunk) => new NodePartImpl(node.index, ({ value }) => onChunk(value), null),
|
|
129
|
+
"property": (node, onChunk) => new PropertyPartImpl(node.name, node.index, ({ value }) => onChunk(value), null),
|
|
130
|
+
"ref": () => {
|
|
131
|
+
throw new Error("Refs are not utilized on the server")
|
|
132
|
+
},
|
|
133
|
+
"text-part": (node, onChunk) => new TextPartImpl(node.index, ({ value }) => onChunk(value), null)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function partNodeToPart(
|
|
137
|
+
node: PartNode,
|
|
138
|
+
onChunk: (value: unknown) => Effect.Effect<never, never, void>
|
|
139
|
+
): Part {
|
|
140
|
+
return partNodeMap[node._tag](node as any, onChunk)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function sparsePartNodeToPart(
|
|
144
|
+
node: SparsePartNode,
|
|
145
|
+
onChunk: (value: string | null) => Effect.Effect<never, never, void>
|
|
146
|
+
) {
|
|
147
|
+
if (node._tag === "sparse-attr") {
|
|
148
|
+
return renderSparseAttr(node, onChunk)
|
|
149
|
+
} else if (node._tag === "sparse-class-name") {
|
|
150
|
+
return renderSparseClassName(node, onChunk)
|
|
151
|
+
} else {
|
|
152
|
+
return renderSparseComment(node, onChunk)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function renderSparseAttr(
|
|
157
|
+
attrNode: SparseAttrNode,
|
|
158
|
+
setAttribute: (value: string | null) => Effect.Effect<never, never, void>
|
|
159
|
+
): SparseAttributePart {
|
|
160
|
+
const { nodes } = attrNode
|
|
161
|
+
const values: Map<number, string | null> = new Map()
|
|
162
|
+
|
|
163
|
+
function getValue() {
|
|
164
|
+
return (part.value = Array.from({ length: nodes.length }, (_, i) => values.get(i) || ""))
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function setValue(value: string | null, index: number) {
|
|
168
|
+
return Effect.suspend(() => {
|
|
169
|
+
values.set(index, value)
|
|
170
|
+
|
|
171
|
+
if (values.size === nodes.length) return setAttribute(getValue().join(""))
|
|
172
|
+
|
|
173
|
+
return Effect.unit
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const parts: Array<StaticText | AttributePart> = []
|
|
178
|
+
|
|
179
|
+
let partIndex = 0
|
|
180
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
181
|
+
const node = nodes[i]
|
|
182
|
+
|
|
183
|
+
if (node._tag === "text") {
|
|
184
|
+
values.set(i, node.value)
|
|
185
|
+
parts.push(new StaticTextImpl(node.value))
|
|
186
|
+
} else {
|
|
187
|
+
parts.push(
|
|
188
|
+
new AttributePartImpl(
|
|
189
|
+
node.name,
|
|
190
|
+
partIndex++,
|
|
191
|
+
({ value }) => setValue(value || "", i),
|
|
192
|
+
null
|
|
193
|
+
)
|
|
194
|
+
)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const part = new SparseAttributePartImpl(attrNode.name, parts, ({ value }) => setAttribute(value.join("")))
|
|
199
|
+
|
|
200
|
+
return part
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function renderSparseClassName(
|
|
204
|
+
classNameNode: SparseClassNameNode,
|
|
205
|
+
setClassName: (value: string | null) => Effect.Effect<never, never, void>
|
|
206
|
+
): SparseClassNamePart {
|
|
207
|
+
const { nodes } = classNameNode
|
|
208
|
+
const values: Map<number, string | null> = new Map()
|
|
209
|
+
|
|
210
|
+
function getValue() {
|
|
211
|
+
return (part.value = Array.from({ length: nodes.length }, (_, i) => values.get(i) || ""))
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function setValue(value: string | null, index: number) {
|
|
215
|
+
return Effect.suspend(() => {
|
|
216
|
+
values.set(index, value)
|
|
217
|
+
|
|
218
|
+
if (values.size === nodes.length) return setClassName(getValue().join(""))
|
|
219
|
+
|
|
220
|
+
return Effect.unit
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const parts: Array<StaticText | ClassNamePart> = []
|
|
225
|
+
|
|
226
|
+
let partIndex = 0
|
|
227
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
228
|
+
const node = nodes[i]
|
|
229
|
+
|
|
230
|
+
if (node._tag === "text") {
|
|
231
|
+
values.set(i, node.value)
|
|
232
|
+
parts.push(new StaticTextImpl(node.value))
|
|
233
|
+
} else {
|
|
234
|
+
parts.push(
|
|
235
|
+
new ClassNamePartImpl(
|
|
236
|
+
partIndex++,
|
|
237
|
+
({ value }) => setValue(value?.join(" ") || null, i),
|
|
238
|
+
null
|
|
239
|
+
)
|
|
240
|
+
)
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const part = new SparseClassNamePartImpl(parts, ({ value }) => setClassName(value.join("")), [])
|
|
245
|
+
|
|
246
|
+
return part
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function renderSparseComment(
|
|
250
|
+
commentNode: SparseCommentNode,
|
|
251
|
+
setComment: (value: string | null) => Effect.Effect<never, never, void>
|
|
252
|
+
): SparseCommentPart {
|
|
253
|
+
const { nodes } = commentNode
|
|
254
|
+
const values: Map<number, string | null | undefined> = new Map()
|
|
255
|
+
|
|
256
|
+
function getValue(): ReadonlyArray<string> {
|
|
257
|
+
return (part.value = Array.from({ length: nodes.length }, (_, i) => values.get(i) || ""))
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function setValue(value: string | null | undefined, index: number) {
|
|
261
|
+
return Effect.suspend(() => {
|
|
262
|
+
values.set(index, value)
|
|
263
|
+
|
|
264
|
+
if (values.size === nodes.length) return setComment(getValue().join(""))
|
|
265
|
+
|
|
266
|
+
return Effect.unit
|
|
267
|
+
})
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const parts: Array<StaticText | CommentPart> = []
|
|
271
|
+
|
|
272
|
+
let partIndex = 0
|
|
273
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
274
|
+
const node = nodes[i]
|
|
275
|
+
|
|
276
|
+
if (node._tag === "text") {
|
|
277
|
+
values.set(i, node.value)
|
|
278
|
+
parts.push(new StaticTextImpl(node.value))
|
|
279
|
+
} else {
|
|
280
|
+
parts.push(
|
|
281
|
+
new CommentPartImpl(
|
|
282
|
+
partIndex++,
|
|
283
|
+
({ value }) => setValue(value, i),
|
|
284
|
+
null
|
|
285
|
+
)
|
|
286
|
+
)
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const part = new SparseCommentPartImpl(parts, ({ value }) => setComment(value.join("")), [])
|
|
291
|
+
|
|
292
|
+
return part
|
|
293
|
+
}
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import * as Token from "../Token"
|
|
2
|
+
import {
|
|
3
|
+
getAllTextUntilElementClose,
|
|
4
|
+
getAttributeWithoutQuotes,
|
|
5
|
+
getAttributeWithQuotes,
|
|
6
|
+
getBooleanAttribute,
|
|
7
|
+
getClosingTag,
|
|
8
|
+
getComment,
|
|
9
|
+
getOpeningTag,
|
|
10
|
+
getOpeningTagEnd,
|
|
11
|
+
getPart,
|
|
12
|
+
getSelfClosingTagEnd,
|
|
13
|
+
getTextUntilCloseBrace,
|
|
14
|
+
getTextUntilPart,
|
|
15
|
+
getWhitespace,
|
|
16
|
+
PART_REGEX,
|
|
17
|
+
PART_STRING
|
|
18
|
+
} from "./chunks"
|
|
19
|
+
import type { TextChunk } from "./chunks"
|
|
20
|
+
|
|
21
|
+
export function tokenize(template: ReadonlyArray<string>): Iterable<Token.Token> {
|
|
22
|
+
return new Tokenizer(template)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function templateWithParts(template: ReadonlyArray<string>): string {
|
|
26
|
+
const length = template.length
|
|
27
|
+
const lastIndex = length - 1
|
|
28
|
+
|
|
29
|
+
let output = ""
|
|
30
|
+
|
|
31
|
+
for (let i = 0; i < length; i++) {
|
|
32
|
+
const str = template[i]
|
|
33
|
+
|
|
34
|
+
if (i === lastIndex) {
|
|
35
|
+
output += str
|
|
36
|
+
} else {
|
|
37
|
+
output += str + PART_STRING(i)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return output
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
class Tokenizer implements Iterable<Token.Token> {
|
|
45
|
+
protected input: string = ""
|
|
46
|
+
protected pos: number = 0
|
|
47
|
+
protected context: "text" | "element" | "self-closing" | "text-only" = "text"
|
|
48
|
+
protected currentTag: Stack<string> | null = null
|
|
49
|
+
|
|
50
|
+
constructor(private readonly template: ReadonlyArray<string>) {}
|
|
51
|
+
|
|
52
|
+
*[Symbol.iterator](): Generator<Token.Token> {
|
|
53
|
+
this.init()
|
|
54
|
+
|
|
55
|
+
// eslint-disable-next-line no-constant-condition
|
|
56
|
+
while (this.pos < this.input.length) {
|
|
57
|
+
switch (this.context) {
|
|
58
|
+
case "element":
|
|
59
|
+
yield* this.nextElementToken()
|
|
60
|
+
break
|
|
61
|
+
case "self-closing":
|
|
62
|
+
yield* this.nextSelfClosingToken()
|
|
63
|
+
break
|
|
64
|
+
case "text-only":
|
|
65
|
+
yield* this.nextTextOnlyToken()
|
|
66
|
+
break
|
|
67
|
+
default:
|
|
68
|
+
yield* this.nextTextToken()
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private init(): void {
|
|
74
|
+
// Convert our template into a string for easier tokenization
|
|
75
|
+
// Remove any whitespace at the beginning or end of the template since we don't care about it.
|
|
76
|
+
this.input = this.template.length === 1 ? this.template[0] : templateWithParts(this.template)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private *nextTextToken(): Generator<Token.Token> {
|
|
80
|
+
const char = this.nextChar()
|
|
81
|
+
const isOpenBracket = char === "<"
|
|
82
|
+
|
|
83
|
+
let next: TextChunk | undefined
|
|
84
|
+
|
|
85
|
+
if (isOpenBracket && (next = this.chunk(getOpeningTag))) {
|
|
86
|
+
const name = next.match[2]
|
|
87
|
+
|
|
88
|
+
this.pushTag(name)
|
|
89
|
+
this.context = Token.SELF_CLOSING_TAGS.has(name)
|
|
90
|
+
? "self-closing"
|
|
91
|
+
: Token.TEXT_ONLY_NODES_REGEX.has(name)
|
|
92
|
+
? "text-only"
|
|
93
|
+
: "element"
|
|
94
|
+
|
|
95
|
+
yield new Token.OpeningTagToken(name)
|
|
96
|
+
} else if (isOpenBracket && (next = this.chunk(getClosingTag))) {
|
|
97
|
+
yield new Token.ClosingTagToken(next.match[3])
|
|
98
|
+
this.popTag()
|
|
99
|
+
} else if (isOpenBracket && (next = this.chunk(getComment))) {
|
|
100
|
+
yield* this.parseComment(next.match[2])
|
|
101
|
+
} else if ((next = this.chunk(getPart))) {
|
|
102
|
+
yield new Token.PartToken(parseInt(next.match[2], 10))
|
|
103
|
+
} else if ((next = this.chunk(getWhitespace))) {
|
|
104
|
+
yield new Token.TextToken(next.match[1])
|
|
105
|
+
} else if ((next = this.chunk(getTextUntilCloseBrace))) {
|
|
106
|
+
yield* parseTextAndParts(next.match[1])
|
|
107
|
+
} else {
|
|
108
|
+
yield new Token.TextToken(this.takeNextChar())
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private *nextElementToken(): Generator<Token.Token> {
|
|
113
|
+
let next: TextChunk | undefined
|
|
114
|
+
|
|
115
|
+
if ((next = this.chunk(getAttributeWithQuotes))) {
|
|
116
|
+
yield* this.parseAttribute(next.match[2], next.match[4])
|
|
117
|
+
} else if ((next = this.chunk(getAttributeWithoutQuotes))) {
|
|
118
|
+
yield* this.parseAttribute(next.match[2], next.match[4])
|
|
119
|
+
} else if ((next = this.chunk(getOpeningTagEnd))) {
|
|
120
|
+
this.context = "text"
|
|
121
|
+
|
|
122
|
+
yield new Token.OpeningTagEndToken(this.currentTag!.value)
|
|
123
|
+
} else if ((next = this.chunk(getSelfClosingTagEnd))) {
|
|
124
|
+
yield new Token.OpeningTagEndToken(this.currentTag!.value, false)
|
|
125
|
+
yield new Token.ClosingTagToken(this.currentTag!.value)
|
|
126
|
+
this.popTag()
|
|
127
|
+
this.context = "text"
|
|
128
|
+
} else if ((next = this.chunk(getBooleanAttribute))) {
|
|
129
|
+
yield new Token.BooleanAttributeToken(next.match[2])
|
|
130
|
+
} else if ((next = this.chunk(getWhitespace))) {
|
|
131
|
+
// Continue
|
|
132
|
+
} else {
|
|
133
|
+
this.invalidTemplate()
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private *nextSelfClosingToken(): Generator<Token.Token> {
|
|
138
|
+
let next: TextChunk | undefined
|
|
139
|
+
|
|
140
|
+
if ((next = this.chunk(getAttributeWithQuotes))) {
|
|
141
|
+
yield* this.parseAttribute(next.match[2], next.match[4])
|
|
142
|
+
} else if ((next = this.chunk(getAttributeWithoutQuotes))) {
|
|
143
|
+
yield* this.parseAttribute(next.match[2], next.match[4])
|
|
144
|
+
} else if ((next = this.chunk(getSelfClosingTagEnd))) {
|
|
145
|
+
yield new Token.OpeningTagEndToken(this.currentTag!.value, true)
|
|
146
|
+
this.popTag()
|
|
147
|
+
this.context = "text"
|
|
148
|
+
} else if ((next = this.chunk(getBooleanAttribute))) {
|
|
149
|
+
yield new Token.BooleanAttributeToken(next.match[2])
|
|
150
|
+
} else {
|
|
151
|
+
this.invalidTemplate()
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private *nextTextOnlyToken(): Generator<Token.Token> {
|
|
156
|
+
let next: TextChunk | undefined
|
|
157
|
+
|
|
158
|
+
if ((next = this.chunk(getAttributeWithQuotes))) {
|
|
159
|
+
yield* this.parseAttribute(next.match[2], next.match[4])
|
|
160
|
+
} else if ((next = this.chunk(getAttributeWithoutQuotes))) {
|
|
161
|
+
yield* this.parseAttribute(next.match[2], next.match[4])
|
|
162
|
+
} else if ((next = this.chunk(getOpeningTagEnd))) {
|
|
163
|
+
yield new Token.OpeningTagEndToken(this.currentTag!.value)
|
|
164
|
+
} else if ((next = this.chunk(getClosingTag))) {
|
|
165
|
+
yield new Token.ClosingTagToken(next.match[3])
|
|
166
|
+
this.context = "text"
|
|
167
|
+
} else if ((next = this.chunk(getAllTextUntilElementClose(this.currentTag!.value)))) {
|
|
168
|
+
const text = next.match[1]
|
|
169
|
+
|
|
170
|
+
if (PART_REGEX.test(text)) {
|
|
171
|
+
yield* parseTextAndParts(text)
|
|
172
|
+
} else {
|
|
173
|
+
yield new Token.TextToken(text)
|
|
174
|
+
}
|
|
175
|
+
this.context = "text"
|
|
176
|
+
} else {
|
|
177
|
+
this.invalidTemplate()
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private *parseComment(comment: string): Generator<Token.Token> {
|
|
182
|
+
const parts = Array.from(parseTextAndParts(comment))
|
|
183
|
+
|
|
184
|
+
if (parts.length === 1 && parts[0]._tag === "text") {
|
|
185
|
+
yield new Token.CommentToken(parts[0].value)
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
yield new Token.CommentStartToken("<!--")
|
|
190
|
+
|
|
191
|
+
for (const part of parts) {
|
|
192
|
+
yield part
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
yield new Token.CommentEndToken("-->")
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private *parseAttribute(name: string, value: string) {
|
|
199
|
+
const parts = Array.from(parseTextAndParts(value))
|
|
200
|
+
|
|
201
|
+
if (value === "") {
|
|
202
|
+
yield new Token.BooleanAttributeToken(name)
|
|
203
|
+
return
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (parts.length === 1 && parts[0]._tag === "text") {
|
|
207
|
+
yield getAttributeToken(name, value)
|
|
208
|
+
return
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
yield getAttributeTokenPartial(name, "start")
|
|
212
|
+
|
|
213
|
+
for (const part of parts) {
|
|
214
|
+
yield part
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
yield getAttributeTokenPartial(name, "end")
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
private nextChar(): string {
|
|
221
|
+
return this.input.charAt(this.pos)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
private takeNextChar(): string {
|
|
225
|
+
const text = this.input.substring(this.pos, this.pos + 1)
|
|
226
|
+
this.pos += 1
|
|
227
|
+
|
|
228
|
+
return text
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private chunk(f: (str: string, pos: number) => TextChunk | undefined): TextChunk | undefined {
|
|
232
|
+
const chunk = f(this.input, this.pos)
|
|
233
|
+
|
|
234
|
+
if (chunk) {
|
|
235
|
+
this.pos += chunk.length
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return chunk
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
private pushTag(name: string): void {
|
|
242
|
+
this.currentTag = new Stack(name, this.currentTag)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private popTag(): void {
|
|
246
|
+
if (this.currentTag) {
|
|
247
|
+
this.currentTag = this.currentTag.previous
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
protected invalidTemplate() {
|
|
252
|
+
let message = `Invalid template ${this.input}`
|
|
253
|
+
message += "\n\nFrom:\n\n"
|
|
254
|
+
message += this.input.substring(this.pos)
|
|
255
|
+
|
|
256
|
+
throw new SyntaxError(message)
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
class Stack<A> {
|
|
261
|
+
constructor(
|
|
262
|
+
readonly value: A,
|
|
263
|
+
readonly previous: Stack<A> | null
|
|
264
|
+
) {}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function* parseTextAndParts(s: string): Generator<Token.TextToken | Token.PartToken> {
|
|
268
|
+
let next: TextChunk | undefined
|
|
269
|
+
let pos: number = 0
|
|
270
|
+
|
|
271
|
+
while (pos < s.length) {
|
|
272
|
+
if ((next = getPart(s, pos))) {
|
|
273
|
+
yield new Token.PartToken(parseInt(next.match[2], 10))
|
|
274
|
+
pos += next.length
|
|
275
|
+
} else if ((next = getTextUntilPart(s, pos))) {
|
|
276
|
+
yield new Token.TextToken(next.match[1])
|
|
277
|
+
|
|
278
|
+
pos += next.length
|
|
279
|
+
} else {
|
|
280
|
+
yield new Token.TextToken(s.substring(pos))
|
|
281
|
+
|
|
282
|
+
break
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function getAttributeTokenPartial(name: string, ctx: "start" | "end") {
|
|
288
|
+
switch (name[0]) {
|
|
289
|
+
case "?":
|
|
290
|
+
return ctx === "start"
|
|
291
|
+
? new Token.BooleanAttributeStartToken(name.substring(1))
|
|
292
|
+
: new Token.BooleanAttributeEndToken(name.substring(1))
|
|
293
|
+
case ".": {
|
|
294
|
+
const propertyName = name.substring(1)
|
|
295
|
+
|
|
296
|
+
switch (propertyName) {
|
|
297
|
+
case "data":
|
|
298
|
+
return ctx === "start"
|
|
299
|
+
? new Token.DataAttributeStartToken()
|
|
300
|
+
: new Token.DataAttributeEndToken()
|
|
301
|
+
default:
|
|
302
|
+
return ctx === "start"
|
|
303
|
+
? new Token.PropertyAttributeStartToken(propertyName)
|
|
304
|
+
: new Token.PropertyAttributeEndToken(propertyName)
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
case "@":
|
|
308
|
+
return ctx === "start"
|
|
309
|
+
? new Token.EventAttributeStartToken(name.substring(1))
|
|
310
|
+
: new Token.EventAttributeEndToken(name.substring(1))
|
|
311
|
+
case "o":
|
|
312
|
+
if (name[1] === "n") {
|
|
313
|
+
return ctx === "start"
|
|
314
|
+
? new Token.EventAttributeStartToken(name.substring(2))
|
|
315
|
+
: new Token.EventAttributeEndToken(name.substring(2))
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
switch (name.toLowerCase()) {
|
|
320
|
+
case "class":
|
|
321
|
+
case "classname":
|
|
322
|
+
return ctx === "start"
|
|
323
|
+
? new Token.ClassNameAttributeStartToken()
|
|
324
|
+
: new Token.ClassNameAttributeEndToken()
|
|
325
|
+
case "ref":
|
|
326
|
+
return ctx === "start" ? new Token.RefAttributeStartToken() : new Token.RefAttributeEndToken()
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return ctx === "start" ? new Token.AttributeStartToken(name) : new Token.AttributeEndToken(name)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function getAttributeToken(name: string, value: string) {
|
|
333
|
+
if (name[0] === "?") {
|
|
334
|
+
return new Token.BooleanAttributeToken(name.substring(1))
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return new Token.AttributeToken(name, value)
|
|
338
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { getOption } from "@typed/context"
|
|
2
|
+
import * as Chunk from "effect/Chunk"
|
|
3
|
+
import type * as Duration from "effect/Duration"
|
|
4
|
+
import * as Effect from "effect/Effect"
|
|
5
|
+
import { isSome } from "effect/Option"
|
|
6
|
+
import { TestClock } from "effect/TestClock"
|
|
7
|
+
import { TEXT_START, TYPED_START } from "../Meta"
|
|
8
|
+
|
|
9
|
+
export function isComment(node: Node): node is Comment {
|
|
10
|
+
return node.nodeType === node.COMMENT_NODE
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function isCommentWithValue(node: Node, value: string): node is Comment {
|
|
14
|
+
return isComment(node) && node.nodeValue === value
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function isHtmlElement(node: Node): node is HTMLElement {
|
|
18
|
+
return node.nodeType === node.ELEMENT_NODE
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function findHoleComment(parent: Element, index: number) {
|
|
22
|
+
const childNodes = parent.childNodes
|
|
23
|
+
|
|
24
|
+
for (let i = 0; i < childNodes.length; ++i) {
|
|
25
|
+
const node = childNodes[i]
|
|
26
|
+
|
|
27
|
+
if (node.nodeType === 8 && node.nodeValue === `hole${index}`) {
|
|
28
|
+
return node as Comment
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
throw new Error(`Unable to find hole comment for index ${index}`)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const previousComments = new Set([TEXT_START, TYPED_START])
|
|
36
|
+
|
|
37
|
+
export function getPreviousNodes(comment: Node, index: number) {
|
|
38
|
+
const nodes: Array<Node> = []
|
|
39
|
+
let node = comment.previousSibling
|
|
40
|
+
const previousHole = `hole${index - 1}`
|
|
41
|
+
|
|
42
|
+
previousComments.add(previousHole)
|
|
43
|
+
|
|
44
|
+
while (node && !previousComments.has(String(node.nodeValue))) {
|
|
45
|
+
nodes.unshift(node)
|
|
46
|
+
node = node.previousSibling
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
previousComments.delete(previousHole)
|
|
50
|
+
|
|
51
|
+
return nodes
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const findPath = (node: ParentChildNodes, path: Chunk.Chunk<number>): Node =>
|
|
55
|
+
Chunk.reduce(path, node, ({ childNodes }, index) => childNodes[index]) as Node
|
|
56
|
+
|
|
57
|
+
export interface ParentChildNodes {
|
|
58
|
+
readonly parentNode: Node | null
|
|
59
|
+
readonly childNodes: ArrayLike<Node>
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function adjustTime(input?: Duration.DurationInput) {
|
|
63
|
+
return Effect.gen(function*(_) {
|
|
64
|
+
const ctx = yield* _(Effect.context<never>())
|
|
65
|
+
const testClock = getOption(ctx, TestClock)
|
|
66
|
+
|
|
67
|
+
if (isSome(testClock)) {
|
|
68
|
+
yield* _(testClock.value.adjust(input ?? 1))
|
|
69
|
+
} else if (input) {
|
|
70
|
+
yield* _(Effect.sleep(input))
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
}
|