@typed/template 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Directive.js +1 -1
- package/dist/cjs/Directive.js.map +1 -1
- package/dist/cjs/ElementRef.js +23 -13
- package/dist/cjs/ElementRef.js.map +1 -1
- package/dist/cjs/ElementSource.js +16 -18
- package/dist/cjs/ElementSource.js.map +1 -1
- package/dist/cjs/EventHandler.js +1 -1
- package/dist/cjs/EventHandler.js.map +1 -1
- package/dist/cjs/Html.js +31 -32
- package/dist/cjs/Html.js.map +1 -1
- package/dist/cjs/HtmlChunk.js +4 -1
- package/dist/cjs/HtmlChunk.js.map +1 -1
- package/dist/cjs/Hydrate.js +1 -1
- package/dist/cjs/Hydrate.js.map +1 -1
- package/dist/cjs/Many.js +14 -13
- package/dist/cjs/Many.js.map +1 -1
- package/dist/cjs/Parser.js +11 -323
- package/dist/cjs/Parser.js.map +1 -1
- package/dist/cjs/Placeholder.js +3 -3
- package/dist/cjs/Placeholder.js.map +1 -1
- package/dist/cjs/Platform.js +4 -4
- package/dist/cjs/Platform.js.map +1 -1
- package/dist/cjs/Render.js +1 -1
- package/dist/cjs/Render.js.map +1 -1
- package/dist/cjs/RenderContext.js +48 -27
- package/dist/cjs/RenderContext.js.map +1 -1
- package/dist/cjs/RenderTemplate.js +2 -17
- package/dist/cjs/RenderTemplate.js.map +1 -1
- package/dist/cjs/Template.js +27 -1
- package/dist/cjs/Template.js.map +1 -1
- package/dist/cjs/TemplateInstance.js +2 -2
- package/dist/cjs/TemplateInstance.js.map +1 -1
- package/dist/cjs/Test.js +20 -7
- package/dist/cjs/Test.js.map +1 -1
- package/dist/cjs/index.js +0 -12
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/internal/EventSource.js +95 -0
- package/dist/cjs/internal/EventSource.js.map +1 -0
- package/dist/cjs/internal/browser.js +11 -11
- package/dist/cjs/internal/browser.js.map +1 -1
- package/dist/cjs/internal/hydrate.js +49 -50
- package/dist/cjs/internal/hydrate.js.map +1 -1
- package/dist/cjs/internal/indexRefCounter.js +49 -2
- package/dist/cjs/internal/indexRefCounter.js.map +1 -1
- package/dist/cjs/internal/parser.js +60 -17
- package/dist/cjs/internal/parser.js.map +1 -1
- package/dist/cjs/internal/parts.js +128 -28
- package/dist/cjs/internal/parts.js.map +1 -1
- package/dist/cjs/internal/render.js +486 -53
- package/dist/cjs/internal/render.js.map +1 -1
- package/dist/cjs/internal/server.js +5 -2
- package/dist/cjs/internal/server.js.map +1 -1
- package/dist/dts/Directive.d.ts.map +1 -1
- package/dist/dts/ElementRef.d.ts +4 -2
- package/dist/dts/ElementRef.d.ts.map +1 -1
- package/dist/dts/ElementSource.d.ts +10 -5
- package/dist/dts/ElementSource.d.ts.map +1 -1
- package/dist/dts/EventHandler.d.ts.map +1 -1
- package/dist/dts/Html.d.ts +1 -1
- package/dist/dts/Html.d.ts.map +1 -1
- package/dist/dts/HtmlChunk.d.ts.map +1 -1
- package/dist/dts/Many.d.ts +13 -11
- package/dist/dts/Many.d.ts.map +1 -1
- package/dist/dts/Parser.d.ts +3 -6
- package/dist/dts/Parser.d.ts.map +1 -1
- package/dist/dts/Part.d.ts +13 -3
- package/dist/dts/Part.d.ts.map +1 -1
- package/dist/dts/Placeholder.d.ts +2 -2
- package/dist/dts/Placeholder.d.ts.map +1 -1
- package/dist/dts/Render.d.ts +2 -1
- package/dist/dts/Render.d.ts.map +1 -1
- package/dist/dts/RenderContext.d.ts +5 -4
- package/dist/dts/RenderContext.d.ts.map +1 -1
- package/dist/dts/RenderTemplate.d.ts +2 -16
- package/dist/dts/RenderTemplate.d.ts.map +1 -1
- package/dist/dts/Renderable.d.ts +2 -2
- package/dist/dts/Renderable.d.ts.map +1 -1
- package/dist/dts/Template.d.ts +21 -3
- package/dist/dts/Template.d.ts.map +1 -1
- package/dist/dts/TemplateInstance.d.ts +3 -2
- package/dist/dts/TemplateInstance.d.ts.map +1 -1
- package/dist/dts/Test.d.ts +4 -6
- package/dist/dts/Test.d.ts.map +1 -1
- package/dist/dts/index.d.ts +0 -4
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/internal/EventSource.d.ts +12 -0
- package/dist/dts/internal/EventSource.d.ts.map +1 -0
- package/dist/dts/internal/browser.d.ts.map +1 -1
- package/dist/dts/internal/hydrate.d.ts +5 -5
- package/dist/dts/internal/hydrate.d.ts.map +1 -1
- package/dist/dts/internal/indexRefCounter.d.ts +5 -0
- package/dist/dts/internal/indexRefCounter.d.ts.map +1 -1
- package/dist/dts/internal/module-augmentation.d.ts +0 -4
- package/dist/dts/internal/module-augmentation.d.ts.map +1 -1
- package/dist/dts/internal/parser.d.ts +8 -0
- package/dist/dts/internal/parser.d.ts.map +1 -1
- package/dist/dts/internal/parts.d.ts +66 -56
- package/dist/dts/internal/parts.d.ts.map +1 -1
- package/dist/dts/internal/render.d.ts +7 -7
- package/dist/dts/internal/render.d.ts.map +1 -1
- package/dist/dts/internal/server.d.ts.map +1 -1
- package/dist/esm/Directive.js +1 -1
- package/dist/esm/Directive.js.map +1 -1
- package/dist/esm/ElementRef.js +12 -7
- package/dist/esm/ElementRef.js.map +1 -1
- package/dist/esm/ElementSource.js +16 -13
- package/dist/esm/ElementSource.js.map +1 -1
- package/dist/esm/EventHandler.js +1 -1
- package/dist/esm/EventHandler.js.map +1 -1
- package/dist/esm/Html.js +29 -24
- package/dist/esm/Html.js.map +1 -1
- package/dist/esm/HtmlChunk.js +6 -1
- package/dist/esm/HtmlChunk.js.map +1 -1
- package/dist/esm/Hydrate.js +1 -1
- package/dist/esm/Hydrate.js.map +1 -1
- package/dist/esm/Many.js +14 -10
- package/dist/esm/Many.js.map +1 -1
- package/dist/esm/Parser.js +6 -335
- package/dist/esm/Parser.js.map +1 -1
- package/dist/esm/Placeholder.js +2 -2
- package/dist/esm/Placeholder.js.map +1 -1
- package/dist/esm/Platform.js +2 -2
- package/dist/esm/Platform.js.map +1 -1
- package/dist/esm/Render.js +1 -1
- package/dist/esm/Render.js.map +1 -1
- package/dist/esm/RenderContext.js +38 -17
- package/dist/esm/RenderContext.js.map +1 -1
- package/dist/esm/RenderTemplate.js +2 -12
- package/dist/esm/RenderTemplate.js.map +1 -1
- package/dist/esm/Template.js +24 -0
- package/dist/esm/Template.js.map +1 -1
- package/dist/esm/TemplateInstance.js +2 -2
- package/dist/esm/TemplateInstance.js.map +1 -1
- package/dist/esm/Test.js +20 -7
- package/dist/esm/Test.js.map +1 -1
- package/dist/esm/index.js +0 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/internal/EventSource.js +91 -0
- package/dist/esm/internal/EventSource.js.map +1 -0
- package/dist/esm/internal/browser.js +12 -12
- package/dist/esm/internal/browser.js.map +1 -1
- package/dist/esm/internal/hydrate.js +45 -46
- package/dist/esm/internal/hydrate.js.map +1 -1
- package/dist/esm/internal/indexRefCounter.js +50 -2
- package/dist/esm/internal/indexRefCounter.js.map +1 -1
- package/dist/esm/internal/parser.js +84 -17
- package/dist/esm/internal/parser.js.map +1 -1
- package/dist/esm/internal/parts.js +110 -27
- package/dist/esm/internal/parts.js.map +1 -1
- package/dist/esm/internal/render.js +476 -58
- package/dist/esm/internal/render.js.map +1 -1
- package/dist/esm/internal/server.js +5 -4
- package/dist/esm/internal/server.js.map +1 -1
- package/package.json +10 -26
- package/src/Directive.ts +1 -1
- package/src/ElementRef.ts +18 -14
- package/src/ElementSource.ts +62 -47
- package/src/EventHandler.ts +1 -1
- package/src/Html.ts +58 -57
- package/src/HtmlChunk.ts +15 -1
- package/src/Hydrate.ts +1 -1
- package/src/Many.ts +53 -43
- package/src/Parser.ts +10 -453
- package/src/Part.ts +15 -3
- package/src/Placeholder.ts +4 -4
- package/src/Platform.ts +2 -2
- package/src/Render.ts +7 -2
- package/src/RenderContext.ts +47 -19
- package/src/RenderTemplate.ts +9 -54
- package/src/Renderable.ts +2 -1
- package/src/Template.ts +26 -0
- package/src/TemplateInstance.ts +9 -9
- package/src/Test.ts +40 -21
- package/src/index.ts +0 -4
- package/src/internal/EventSource.ts +153 -0
- package/src/internal/browser.ts +26 -25
- package/src/internal/hydrate.ts +68 -61
- package/src/internal/indexRefCounter.ts +63 -2
- package/src/internal/module-augmentation.ts +0 -4
- package/src/internal/parser.ts +92 -19
- package/src/internal/parts.ts +158 -73
- package/src/internal/render.ts +701 -89
- package/src/internal/server.ts +5 -3
- package/Token/package.json +0 -6
- package/Tokenizer/package.json +0 -6
- package/dist/cjs/Token.js +0 -270
- package/dist/cjs/Token.js.map +0 -1
- package/dist/cjs/Tokenizer.js +0 -18
- package/dist/cjs/Tokenizer.js.map +0 -1
- package/dist/cjs/internal/readAttribute.js +0 -34
- package/dist/cjs/internal/readAttribute.js.map +0 -1
- package/dist/cjs/internal/tokenizer.js +0 -264
- package/dist/cjs/internal/tokenizer.js.map +0 -1
- package/dist/dts/Token.d.ts +0 -202
- package/dist/dts/Token.d.ts.map +0 -1
- package/dist/dts/Tokenizer.d.ts +0 -6
- package/dist/dts/Tokenizer.d.ts.map +0 -1
- package/dist/dts/internal/readAttribute.d.ts +0 -9
- package/dist/dts/internal/readAttribute.d.ts.map +0 -1
- package/dist/dts/internal/tokenizer.d.ts +0 -3
- package/dist/dts/internal/tokenizer.d.ts.map +0 -1
- package/dist/esm/Token.js +0 -264
- package/dist/esm/Token.js.map +0 -1
- package/dist/esm/Tokenizer.js +0 -9
- package/dist/esm/Tokenizer.js.map +0 -1
- package/dist/esm/internal/readAttribute.js +0 -24
- package/dist/esm/internal/readAttribute.js.map +0 -1
- package/dist/esm/internal/tokenizer.js +0 -296
- package/dist/esm/internal/tokenizer.js.map +0 -1
- package/src/Token.ts +0 -269
- package/src/Tokenizer.ts +0 -10
- package/src/internal/readAttribute.ts +0 -28
- package/src/internal/tokenizer.ts +0 -338
package/src/Parser.ts
CHANGED
|
@@ -2,456 +2,13 @@
|
|
|
2
2
|
* @since 1.0.0
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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.js"), () => new ParserImpl())
|
|
5
|
+
export {
|
|
6
|
+
/**
|
|
7
|
+
* @since 1.0.0
|
|
8
|
+
*/
|
|
9
|
+
parse,
|
|
10
|
+
/**
|
|
11
|
+
* @since 1.0.0
|
|
12
|
+
*/
|
|
13
|
+
templateHash
|
|
14
|
+
} from "./internal/parser.js"
|
package/src/Part.ts
CHANGED
|
@@ -22,6 +22,7 @@ export type Part =
|
|
|
22
22
|
| PropertyPart
|
|
23
23
|
| RefPart
|
|
24
24
|
| TextPart
|
|
25
|
+
| PropertiesPart
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* @since 1.0.0
|
|
@@ -75,11 +76,11 @@ export interface DataPart {
|
|
|
75
76
|
export interface EventPart {
|
|
76
77
|
readonly _tag: "event"
|
|
77
78
|
readonly name: string
|
|
78
|
-
readonly
|
|
79
|
+
readonly source: ElementSource<any>
|
|
80
|
+
readonly value: null
|
|
79
81
|
readonly index: number
|
|
80
82
|
readonly onCause: (cause: Cause<unknown>) => Effect<never, never, unknown>
|
|
81
|
-
|
|
82
|
-
readonly update: <R = never>(value: EventHandler<R, never> | null | undefined) => Effect<R | Scope, never, void>
|
|
83
|
+
readonly addEventListener: (handler: EventHandler<never, never, Event>) => void
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
/**
|
|
@@ -136,6 +137,17 @@ export interface NodePart {
|
|
|
136
137
|
readonly update: (value: this["value"]) => Effect<Scope, never, void>
|
|
137
138
|
}
|
|
138
139
|
|
|
140
|
+
/**
|
|
141
|
+
* @since 1.0.0
|
|
142
|
+
*/
|
|
143
|
+
export interface PropertiesPart {
|
|
144
|
+
readonly _tag: "properties"
|
|
145
|
+
readonly value: Readonly<Record<string, any>> | null | undefined
|
|
146
|
+
readonly index: number
|
|
147
|
+
|
|
148
|
+
readonly update: (value: this["value"]) => Effect<Scope, never, void>
|
|
149
|
+
}
|
|
150
|
+
|
|
139
151
|
/**
|
|
140
152
|
* @since 1.0.0
|
|
141
153
|
*/
|
package/src/Placeholder.ts
CHANGED
|
@@ -6,13 +6,13 @@ import "./internal/module-augmentation.js"
|
|
|
6
6
|
import type { Fx } from "@typed/fx/Fx"
|
|
7
7
|
import { isFx } from "@typed/fx/Fx"
|
|
8
8
|
import * as RefSubject from "@typed/fx/RefSubject"
|
|
9
|
-
import
|
|
10
|
-
import
|
|
9
|
+
import * as Effect from "effect/Effect"
|
|
10
|
+
import type * as Scope from "effect/Scope"
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* @since 1.0.0
|
|
14
14
|
*/
|
|
15
|
-
export const PlaceholderTypeId = Symbol.for("
|
|
15
|
+
export const PlaceholderTypeId = Symbol.for("@typed/template/Placholder")
|
|
16
16
|
/**
|
|
17
17
|
* @since 1.0.0
|
|
18
18
|
*/
|
|
@@ -64,7 +64,7 @@ export namespace Placeholder {
|
|
|
64
64
|
if (isFx<R, E, A>(placeholder) || Effect.isEffect(placeholder)) {
|
|
65
65
|
return RefSubject.make(placeholder as Fx<R, E, A>)
|
|
66
66
|
} else {
|
|
67
|
-
return RefSubject.of(placeholder as A)
|
|
67
|
+
return RefSubject.of<A, E>(placeholder as A)
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
}
|
package/src/Platform.ts
CHANGED
|
@@ -30,7 +30,7 @@ export function htmlResponse<R, E>(
|
|
|
30
30
|
{
|
|
31
31
|
...CAMEL_CASE_CONTENT_TYPE,
|
|
32
32
|
...options,
|
|
33
|
-
headers: { ...HYPHENATED_CONTENT_TYPE, ...options?.headers }
|
|
33
|
+
headers: Headers.unsafeFromRecord({ ...HYPHENATED_CONTENT_TYPE, ...options?.headers })
|
|
34
34
|
}
|
|
35
35
|
)
|
|
36
36
|
)
|
|
@@ -48,7 +48,7 @@ export function htmlResponseString(
|
|
|
48
48
|
{
|
|
49
49
|
...CAMEL_CASE_CONTENT_TYPE,
|
|
50
50
|
...options,
|
|
51
|
-
headers: { ...HYPHENATED_CONTENT_TYPE, ...options?.headers }
|
|
51
|
+
headers: Headers.unsafeFromRecord({ ...HYPHENATED_CONTENT_TYPE, ...options?.headers })
|
|
52
52
|
}
|
|
53
53
|
)
|
|
54
54
|
}
|
package/src/Render.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { Document } from "@typed/dom/Document"
|
|
|
7
7
|
import { RootElement } from "@typed/dom/RootElement"
|
|
8
8
|
import * as Fx from "@typed/fx/Fx"
|
|
9
9
|
import { type Rendered } from "@typed/wire"
|
|
10
|
+
import type { Layer, Scope } from "effect"
|
|
10
11
|
import * as Effect from "effect/Effect"
|
|
11
12
|
import { attachRoot, renderTemplate } from "./internal/render.js"
|
|
12
13
|
import { RenderContext } from "./RenderContext.js"
|
|
@@ -40,6 +41,10 @@ export function render<R, E, T extends RenderEvent | null>(
|
|
|
40
41
|
*/
|
|
41
42
|
export function renderLayer<R, E, T extends RenderEvent | null>(
|
|
42
43
|
rendered: Fx.Fx<R, E, T>
|
|
43
|
-
)
|
|
44
|
-
|
|
44
|
+
): Layer.Layer<
|
|
45
|
+
Document | RenderContext | RootElement | Exclude<Exclude<R, RenderTemplate>, Scope.Scope>,
|
|
46
|
+
never,
|
|
47
|
+
never
|
|
48
|
+
> {
|
|
49
|
+
return Fx.drainLayer(Fx.switchMapCause(render(rendered), (e) => Fx.fromEffect(Effect.logError(e))))
|
|
45
50
|
}
|
package/src/RenderContext.ts
CHANGED
|
@@ -11,10 +11,13 @@ import type { Environment } from "@typed/environment"
|
|
|
11
11
|
import { CurrentEnvironment } from "@typed/environment"
|
|
12
12
|
import * as Idle from "@typed/fx/Idle"
|
|
13
13
|
import type { Rendered } from "@typed/wire"
|
|
14
|
-
import
|
|
14
|
+
import * as Effect from "effect/Effect"
|
|
15
|
+
import * as Layer from "effect/Layer"
|
|
16
|
+
import * as Option from "effect/Option"
|
|
15
17
|
import * as Scope from "effect/Scope"
|
|
16
18
|
import type { Entry } from "./Entry.js"
|
|
17
|
-
|
|
19
|
+
|
|
20
|
+
// TODO: We should probably have a more explicit environment type between DOM/HTML rendering
|
|
18
21
|
|
|
19
22
|
/**
|
|
20
23
|
* The context in which templates are rendered within
|
|
@@ -54,7 +57,7 @@ export const RenderContext: Context.Tagged<RenderContext, RenderContext> = Conte
|
|
|
54
57
|
* @since 1.0.0
|
|
55
58
|
*/
|
|
56
59
|
export interface RenderQueue {
|
|
57
|
-
readonly add: (part:
|
|
60
|
+
readonly add: (part: unknown, task: () => void) => Effect.Effect<Scope.Scope, never, void>
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
/**
|
|
@@ -114,14 +117,14 @@ const buildWithCurrentEnvironment = (environment: Environment, skipRenderSchedul
|
|
|
114
117
|
/**
|
|
115
118
|
* @since 1.0.0
|
|
116
119
|
*/
|
|
117
|
-
export const
|
|
120
|
+
export const dom: (
|
|
118
121
|
window: Window & GlobalThis,
|
|
119
122
|
options?: DomServicesElementParams & { readonly skipRenderScheduling?: boolean }
|
|
120
123
|
) => Layer.Layer<never, never, RenderContext | CurrentEnvironment | DomServices> = (window, options) =>
|
|
121
124
|
Layer.provideMerge(
|
|
122
125
|
Layer.mergeAll(
|
|
123
126
|
buildWithCurrentEnvironment(
|
|
124
|
-
"
|
|
127
|
+
"dom",
|
|
125
128
|
options?.skipRenderScheduling
|
|
126
129
|
),
|
|
127
130
|
domServices(options)
|
|
@@ -146,8 +149,9 @@ export {
|
|
|
146
149
|
}
|
|
147
150
|
|
|
148
151
|
class RenderQueueImpl implements RenderQueue {
|
|
149
|
-
queue = new Map<
|
|
152
|
+
queue = new Map<unknown, () => void>()
|
|
150
153
|
scheduled = false
|
|
154
|
+
run: Effect.Effect<Scope.Scope, never, void>
|
|
151
155
|
|
|
152
156
|
constructor(
|
|
153
157
|
readonly scope: Scope.Scope,
|
|
@@ -155,9 +159,11 @@ class RenderQueueImpl implements RenderQueue {
|
|
|
155
159
|
readonly skipRenderScheduling: boolean = false
|
|
156
160
|
) {
|
|
157
161
|
this.add.bind(this)
|
|
162
|
+
|
|
163
|
+
this.run = typeof requestAnimationFrame === "undefined" ? this.runIdle : this.runAnimationFrame
|
|
158
164
|
}
|
|
159
165
|
|
|
160
|
-
add(part:
|
|
166
|
+
add(part: unknown, task: () => void) {
|
|
161
167
|
if (this.skipRenderScheduling) return Effect.sync(task)
|
|
162
168
|
|
|
163
169
|
return Effect.suspend(() => {
|
|
@@ -190,26 +196,20 @@ class RenderQueueImpl implements RenderQueue {
|
|
|
190
196
|
)
|
|
191
197
|
})
|
|
192
198
|
|
|
193
|
-
|
|
194
|
-
Effect.flatMap(
|
|
199
|
+
runIdle: Effect.Effect<Scope.Scope, never, void> = Effect.suspend(() => {
|
|
200
|
+
return Effect.flatMap(
|
|
195
201
|
Idle.whenIdle(this.options),
|
|
196
202
|
(deadline) =>
|
|
197
203
|
Effect.suspend(() => {
|
|
198
204
|
const iterator = this.queue.entries()
|
|
199
205
|
|
|
200
|
-
while (Idle.shouldContinue(deadline)) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
if (result.done) break
|
|
204
|
-
else {
|
|
205
|
-
const [part, task] = result.value
|
|
206
|
-
this.queue.delete(part)
|
|
207
|
-
task()
|
|
208
|
-
}
|
|
206
|
+
while (Idle.shouldContinue(deadline) && this.runTask(iterator)) {
|
|
207
|
+
// Continue
|
|
209
208
|
}
|
|
210
209
|
|
|
210
|
+
// If we have more work to do, schedule another run
|
|
211
211
|
if (this.queue.size > 0) {
|
|
212
|
-
return this.
|
|
212
|
+
return this.runIdle
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
this.scheduled = false
|
|
@@ -217,5 +217,33 @@ class RenderQueueImpl implements RenderQueue {
|
|
|
217
217
|
return Effect.unit
|
|
218
218
|
})
|
|
219
219
|
)
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
runAnimationFrame: Effect.Effect<Scope.Scope, never, void> = Effect.zipRight(
|
|
223
|
+
Effect.asyncOption<never, never, void>((cb) => {
|
|
224
|
+
const id = requestAnimationFrame(() => cb(Effect.unit))
|
|
225
|
+
return Option.some(Effect.sync(() => cancelAnimationFrame(id)))
|
|
226
|
+
}),
|
|
227
|
+
Effect.sync(() => {
|
|
228
|
+
const iterator = this.queue.entries()
|
|
229
|
+
|
|
230
|
+
while (this.runTask(iterator)) {
|
|
231
|
+
// Continue
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
this.scheduled = false
|
|
235
|
+
})
|
|
220
236
|
)
|
|
237
|
+
|
|
238
|
+
private runTask = (iterator: Iterator<[unknown, () => void]>) => {
|
|
239
|
+
const result = iterator.next()
|
|
240
|
+
|
|
241
|
+
if (result.done) return false
|
|
242
|
+
else {
|
|
243
|
+
const [part, task] = result.value
|
|
244
|
+
this.queue.delete(part)
|
|
245
|
+
task()
|
|
246
|
+
return true
|
|
247
|
+
}
|
|
248
|
+
}
|
|
221
249
|
}
|