@typed/template 0.2.0 → 0.3.1
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/chunks.js +4 -1
- package/dist/cjs/internal/chunks.js.map +1 -1
- package/dist/cjs/internal/errors.js +4 -0
- package/dist/cjs/internal/errors.js.map +1 -1
- package/dist/cjs/internal/hydrate.js +113 -80
- 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 +72 -21
- 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 +460 -161
- 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/chunks.d.ts +1 -0
- package/dist/dts/internal/chunks.d.ts.map +1 -1
- package/dist/dts/internal/errors.d.ts +1 -0
- package/dist/dts/internal/errors.d.ts.map +1 -1
- package/dist/dts/internal/hydrate.d.ts +9 -16
- 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 +1 -15
- 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/chunks.js +2 -0
- package/dist/esm/internal/chunks.js.map +1 -1
- package/dist/esm/internal/errors.js +3 -0
- package/dist/esm/internal/errors.js.map +1 -1
- package/dist/esm/internal/hydrate.js +113 -76
- 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 +98 -22
- 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 +446 -157
- 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/chunks.ts +4 -0
- package/src/internal/errors.ts +4 -0
- package/src/internal/hydrate.ts +161 -107
- package/src/internal/indexRefCounter.ts +63 -2
- package/src/internal/module-augmentation.ts +0 -4
- package/src/internal/parser.ts +107 -25
- package/src/internal/parts.ts +158 -73
- package/src/internal/render.ts +638 -289
- 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/internal/parser.ts
CHANGED
|
@@ -2,9 +2,9 @@ import * as Chunk from "effect/Chunk"
|
|
|
2
2
|
import { globalValue } from "effect/GlobalValue"
|
|
3
3
|
import * as Option from "effect/Option"
|
|
4
4
|
import * as Template from "../Template.js"
|
|
5
|
-
import { SELF_CLOSING_TAGS, TEXT_ONLY_NODES_REGEX } from "../Token.js"
|
|
6
5
|
import type { TextChunk } from "./chunks.js"
|
|
7
6
|
import {
|
|
7
|
+
getClosingTagName,
|
|
8
8
|
getPart,
|
|
9
9
|
getStrictPart,
|
|
10
10
|
getTextUntilCloseBrace,
|
|
@@ -16,6 +16,40 @@ import {
|
|
|
16
16
|
// TODO: Consider ways to surface useful errors and warnings.
|
|
17
17
|
// TODO: Profile for performance optimization
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* @since 1.0.0
|
|
21
|
+
*/
|
|
22
|
+
export const TEXT_ONLY_NODES_REGEX = new Set([
|
|
23
|
+
"textarea",
|
|
24
|
+
"script",
|
|
25
|
+
"style",
|
|
26
|
+
"title",
|
|
27
|
+
"plaintext",
|
|
28
|
+
"xmp"
|
|
29
|
+
])
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @since 1.0.0
|
|
33
|
+
*/
|
|
34
|
+
export const SELF_CLOSING_TAGS = new Set([
|
|
35
|
+
"area",
|
|
36
|
+
"base",
|
|
37
|
+
"br",
|
|
38
|
+
"col",
|
|
39
|
+
"command",
|
|
40
|
+
"embed",
|
|
41
|
+
"hr",
|
|
42
|
+
"img",
|
|
43
|
+
"input",
|
|
44
|
+
"keygen",
|
|
45
|
+
"link",
|
|
46
|
+
"meta",
|
|
47
|
+
"param",
|
|
48
|
+
"source",
|
|
49
|
+
"track",
|
|
50
|
+
"wbr"
|
|
51
|
+
])
|
|
52
|
+
|
|
19
53
|
export interface Parser {
|
|
20
54
|
parse(templateStrings: ReadonlyArray<string>): Template.Template
|
|
21
55
|
}
|
|
@@ -25,18 +59,39 @@ export function parse(templateStrings: ReadonlyArray<string>): Template.Template
|
|
|
25
59
|
}
|
|
26
60
|
|
|
27
61
|
const SPACE_REGEX = /\s/
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
62
|
+
const PART_START = "{{__PART"
|
|
63
|
+
const PART_END = "__}}"
|
|
64
|
+
const chars = {
|
|
65
|
+
openBracket: "{",
|
|
66
|
+
closeBracket: "}",
|
|
67
|
+
underscore: "_",
|
|
68
|
+
equals: "=",
|
|
69
|
+
quote: `"`,
|
|
70
|
+
singleQuote: "'",
|
|
71
|
+
slash: "/",
|
|
72
|
+
greaterThan: ">",
|
|
73
|
+
lessThan: "<",
|
|
74
|
+
hypen: "-"
|
|
75
|
+
} as const
|
|
76
|
+
|
|
77
|
+
const isPartToken: TextPredicate = (input, pos) =>
|
|
78
|
+
input[pos] === chars.openBracket && input.slice(pos, pos + 8) === PART_START
|
|
79
|
+
const isPartEndToken: TextPredicate = (input, pos) =>
|
|
80
|
+
input[pos] === chars.underscore && input.slice(pos, pos + 4) === PART_END
|
|
81
|
+
const isElementOpenToken: TextPredicate = (input, pos) =>
|
|
82
|
+
input[pos] === chars.lessThan && input[pos + 1] !== chars.slash
|
|
83
|
+
const isElementCloseToken: TextPredicate = (input, pos) =>
|
|
84
|
+
input[pos] === chars.lessThan && input[pos + 1] === chars.slash
|
|
85
|
+
const isEqualsToken: TextPredicate = (input, pos) => input[pos] === chars.equals
|
|
86
|
+
const isQuoteToken: TextPredicate = (input, pos) => input[pos] === chars.quote
|
|
87
|
+
const isSingleQuoteToken: TextPredicate = (input, pos) => input[pos] === chars.singleQuote
|
|
35
88
|
const isWhitespaceToken: TextPredicate = (input, pos) => SPACE_REGEX.test(input[pos])
|
|
36
|
-
const isOpenTagEndToken: TextPredicate = (input, pos) => input[pos] ===
|
|
37
|
-
const isSelfClosingTagEndToken: TextPredicate = (input, pos) =>
|
|
89
|
+
const isOpenTagEndToken: TextPredicate = (input, pos) => input[pos] === chars.greaterThan
|
|
90
|
+
const isSelfClosingTagEndToken: TextPredicate = (input, pos) =>
|
|
91
|
+
input[pos] === chars.slash && input[pos + 1] === chars.greaterThan
|
|
38
92
|
const isCommentEndToken: TextPredicate = (input, pos) =>
|
|
39
|
-
input[pos] ===
|
|
93
|
+
input[pos] === chars.hypen && input[pos + 1] === chars.hypen && input[pos + 2] === chars.greaterThan
|
|
94
|
+
const isGreaterThanToken: TextPredicate = (input, pos) => input[pos] === chars.greaterThan
|
|
40
95
|
|
|
41
96
|
type Context = "unknown" | "element"
|
|
42
97
|
|
|
@@ -154,6 +209,7 @@ class ParserImpl implements Parser {
|
|
|
154
209
|
|
|
155
210
|
while (this.pos < this.length) {
|
|
156
211
|
const node = this.parseNodeFromContext(this.context)
|
|
212
|
+
|
|
157
213
|
if (node === undefined) {
|
|
158
214
|
return nodes
|
|
159
215
|
} else {
|
|
@@ -187,9 +243,18 @@ class ParserImpl implements Parser {
|
|
|
187
243
|
const nextChar = this.nextChar()
|
|
188
244
|
|
|
189
245
|
if (nextChar === "!") { // Comment
|
|
190
|
-
this.consumeAmount(
|
|
246
|
+
this.consumeAmount(1)
|
|
191
247
|
|
|
192
|
-
|
|
248
|
+
const nextChar = this.nextChar()
|
|
249
|
+
|
|
250
|
+
if (nextChar == "-") {
|
|
251
|
+
this.consumeAmount(2)
|
|
252
|
+
return [this.parseComment()]
|
|
253
|
+
} else if (nextChar.toLowerCase() === "d") {
|
|
254
|
+
return [this.parseDocType()]
|
|
255
|
+
} else {
|
|
256
|
+
throw new SyntaxError(`Unknown comment type ${nextChar}`)
|
|
257
|
+
}
|
|
193
258
|
} else if (nextChar === "/") { // Self-closing tag
|
|
194
259
|
return this.selfClosingTagEnd()
|
|
195
260
|
} else { // Elements
|
|
@@ -251,10 +316,17 @@ class ParserImpl implements Parser {
|
|
|
251
316
|
this.path.pop()
|
|
252
317
|
const element = new Template.ElementNode(tagName, attributes, children)
|
|
253
318
|
|
|
319
|
+
this.skipWhitespace()
|
|
320
|
+
this.consumeClosingTag(tagName)
|
|
321
|
+
|
|
254
322
|
return element
|
|
255
323
|
}
|
|
256
324
|
}
|
|
257
325
|
|
|
326
|
+
private consumeClosingTag(tagName: string) {
|
|
327
|
+
this.chunk(getClosingTagName(tagName))
|
|
328
|
+
}
|
|
329
|
+
|
|
258
330
|
private parseSelfClosingElement(tagName: string): Template.SelfClosingElementNode {
|
|
259
331
|
const attributes = this.parseAttributes()
|
|
260
332
|
|
|
@@ -263,6 +335,7 @@ class ParserImpl implements Parser {
|
|
|
263
335
|
|
|
264
336
|
private parseTextOnlyElement(tagName: string): Template.TextOnlyElement {
|
|
265
337
|
const attributes = this.parseAttributes()
|
|
338
|
+
|
|
266
339
|
this.path.push()
|
|
267
340
|
const children = this.parseTextChildren()
|
|
268
341
|
this.path.pop()
|
|
@@ -289,6 +362,14 @@ class ParserImpl implements Parser {
|
|
|
289
362
|
return this.addPart(new Template.SparseCommentNode(textAndParts))
|
|
290
363
|
}
|
|
291
364
|
|
|
365
|
+
private parseDocType(): Template.DocType {
|
|
366
|
+
this.parseTextUntil(isGreaterThanToken)
|
|
367
|
+
this.consumeAmount(1)
|
|
368
|
+
this.skipWhitespace()
|
|
369
|
+
|
|
370
|
+
return new Template.DocType("html")
|
|
371
|
+
}
|
|
372
|
+
|
|
292
373
|
private parseTagName() {
|
|
293
374
|
return this.parseTextUntilMany(tagNameMatches)
|
|
294
375
|
}
|
|
@@ -304,6 +385,7 @@ class ParserImpl implements Parser {
|
|
|
304
385
|
case null:
|
|
305
386
|
return Skip
|
|
306
387
|
case "whitespace":
|
|
388
|
+
this.skipWhitespace()
|
|
307
389
|
return Continue([new Template.BooleanNode(name)])
|
|
308
390
|
case "equals": {
|
|
309
391
|
this.consumeAmount(1)
|
|
@@ -366,11 +448,13 @@ class ParserImpl implements Parser {
|
|
|
366
448
|
case ".": {
|
|
367
449
|
const property = name.slice(1)
|
|
368
450
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
451
|
+
if (property === "data") {
|
|
452
|
+
return this.addPart(new Template.DataPartNode(unsafeParsePartIndex(text)))
|
|
453
|
+
} else if (property === "props" || property === "properties") {
|
|
454
|
+
return this.addPart(new Template.PropertiesPartNode(unsafeParsePartIndex(text)))
|
|
455
|
+
} else {
|
|
456
|
+
return this.addPart(new Template.PropertyPartNode(property, unsafeParsePartIndex(text)))
|
|
457
|
+
}
|
|
374
458
|
}
|
|
375
459
|
case "@":
|
|
376
460
|
return this.addPart(new Template.EventPartNode(name.slice(1), unsafeParsePartIndex(text)))
|
|
@@ -450,20 +534,18 @@ class ParserImpl implements Parser {
|
|
|
450
534
|
}
|
|
451
535
|
|
|
452
536
|
private parseTextUntil(predicate: (char: string, pos: number) => boolean) {
|
|
453
|
-
|
|
454
|
-
|
|
537
|
+
const start = this.pos
|
|
538
|
+
let i = 0
|
|
455
539
|
while (this.pos < this.length) {
|
|
456
|
-
const char = this.nextChar()
|
|
457
|
-
|
|
458
540
|
if (predicate(this.input, this.pos)) {
|
|
459
541
|
break
|
|
460
542
|
}
|
|
461
543
|
|
|
462
|
-
|
|
544
|
+
i++
|
|
463
545
|
this.consumeAmount(1)
|
|
464
546
|
}
|
|
465
547
|
|
|
466
|
-
return
|
|
548
|
+
return this.input.slice(start, start + i)
|
|
467
549
|
}
|
|
468
550
|
|
|
469
551
|
private parseTextUntilMany<const T extends Predicates>(
|
|
@@ -617,7 +699,7 @@ function parseTextAndParts<T>(s: string, f: (index: number) => T): Array<Templat
|
|
|
617
699
|
return out
|
|
618
700
|
}
|
|
619
701
|
|
|
620
|
-
export const parser: Parser = globalValue(Symbol.for("
|
|
702
|
+
export const parser: Parser = globalValue(Symbol.for("@typed/template/Parser2"), () => new ParserImpl())
|
|
621
703
|
|
|
622
704
|
const digestSize = 2
|
|
623
705
|
const multiplier = 33
|
package/src/internal/parts.ts
CHANGED
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as Fx from "@typed/fx/Fx"
|
|
3
|
-
import { WithContext } from "@typed/fx/Sink"
|
|
4
|
-
import { isText, type Rendered } from "@typed/wire"
|
|
1
|
+
import { isText } from "@typed/wire"
|
|
5
2
|
import type { Cause } from "effect/Cause"
|
|
3
|
+
import * as Data from "effect/Data"
|
|
6
4
|
import * as Effect from "effect/Effect"
|
|
7
5
|
import { equals } from "effect/Equal"
|
|
8
|
-
import
|
|
9
|
-
import type { Equivalence } from "effect/Equivalence"
|
|
10
|
-
import * as Fiber from "effect/Fiber"
|
|
6
|
+
import * as Equivalence from "effect/Equivalence"
|
|
11
7
|
import * as ReadonlyArray from "effect/ReadonlyArray"
|
|
12
8
|
import type { Scope } from "effect/Scope"
|
|
13
|
-
import * as SynchronizedRef from "effect/SynchronizedRef"
|
|
14
|
-
import type { ElementRef } from "../ElementRef.js"
|
|
15
9
|
import type { ElementSource } from "../ElementSource.js"
|
|
10
|
+
import type { EventHandler } from "../EventHandler.js"
|
|
16
11
|
import { unescape } from "../HtmlChunk.js"
|
|
17
12
|
import type {
|
|
18
13
|
AttributePart,
|
|
@@ -23,6 +18,7 @@ import type {
|
|
|
23
18
|
EventPart,
|
|
24
19
|
NodePart,
|
|
25
20
|
Part,
|
|
21
|
+
PropertiesPart,
|
|
26
22
|
PropertyPart,
|
|
27
23
|
RefPart,
|
|
28
24
|
SparseAttributePart,
|
|
@@ -35,7 +31,7 @@ import type {
|
|
|
35
31
|
import type { RenderContext } from "../RenderContext.js"
|
|
36
32
|
import { findHoleComment } from "./utils.js"
|
|
37
33
|
|
|
38
|
-
const strictEq = strict<any>()
|
|
34
|
+
const strictEq = Equivalence.strict<any>()
|
|
39
35
|
|
|
40
36
|
const base = <T extends Part["_tag"]>(tag: T) =>
|
|
41
37
|
class Base {
|
|
@@ -51,7 +47,7 @@ const base = <T extends Part["_tag"]>(tag: T) =>
|
|
|
51
47
|
}
|
|
52
48
|
) => Effect.Effect<Scope, never, void>,
|
|
53
49
|
public value: Extract<Part, { readonly _tag: T }>["value"],
|
|
54
|
-
readonly eq: Equivalence<Extract<Part, { readonly _tag: T }>["value"]> = equals
|
|
50
|
+
readonly eq: Equivalence.Equivalence<Extract<Part, { readonly _tag: T }>["value"]> = equals
|
|
55
51
|
) {
|
|
56
52
|
this.update = this.update.bind(this)
|
|
57
53
|
}
|
|
@@ -64,7 +60,7 @@ const base = <T extends Part["_tag"]>(tag: T) =>
|
|
|
64
60
|
return Effect.unit
|
|
65
61
|
}
|
|
66
62
|
|
|
67
|
-
return Effect.
|
|
63
|
+
return Effect.flatMap(
|
|
68
64
|
this.commit.call(this, {
|
|
69
65
|
previous,
|
|
70
66
|
value,
|
|
@@ -291,73 +287,20 @@ function diffDataSet(
|
|
|
291
287
|
}
|
|
292
288
|
}
|
|
293
289
|
|
|
294
|
-
export class EventPartImpl
|
|
290
|
+
export class EventPartImpl implements EventPart {
|
|
291
|
+
readonly _tag = "event"
|
|
292
|
+
readonly value: EventPart["value"] = null
|
|
293
|
+
|
|
295
294
|
constructor(
|
|
296
295
|
readonly name: string,
|
|
296
|
+
readonly index: number,
|
|
297
|
+
readonly source: ElementSource<any>,
|
|
297
298
|
readonly onCause: <E>(cause: Cause<E>) => Effect.Effect<never, never, unknown>,
|
|
298
|
-
|
|
299
|
-
commit: EventPartImpl["commit"],
|
|
300
|
-
value: EventPart["value"]
|
|
299
|
+
readonly addEventListener: <Ev extends Event>(handler: EventHandler<never, never, Ev>) => void
|
|
301
300
|
) {
|
|
302
|
-
super(index, commit, value, strictEq)
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
static browser<T extends Rendered, E>(
|
|
306
|
-
name: string,
|
|
307
|
-
index: number,
|
|
308
|
-
ref: ElementRef<T>,
|
|
309
|
-
element: HTMLElement | SVGElement,
|
|
310
|
-
onCause: (cause: Cause<E>) => Effect.Effect<never, never, unknown>
|
|
311
|
-
): Effect.Effect<unknown, never, void> {
|
|
312
|
-
return withSwitchFork((fork, ctx) => {
|
|
313
|
-
const source = ref.query(element)
|
|
314
|
-
|
|
315
|
-
return Effect.succeed(
|
|
316
|
-
new EventPartImpl(
|
|
317
|
-
name,
|
|
318
|
-
onCause as any,
|
|
319
|
-
index,
|
|
320
|
-
({ value }) => {
|
|
321
|
-
return value
|
|
322
|
-
? Fx.run(
|
|
323
|
-
source.events(name as keyof HTMLElementEventMap | keyof SVGElementEventMap, value.options),
|
|
324
|
-
WithContext(onCause, value.handler)
|
|
325
|
-
).pipe(
|
|
326
|
-
Effect.provide(ctx),
|
|
327
|
-
fork
|
|
328
|
-
)
|
|
329
|
-
: fork(Effect.unit)
|
|
330
|
-
},
|
|
331
|
-
null
|
|
332
|
-
)
|
|
333
|
-
)
|
|
334
|
-
})
|
|
335
301
|
}
|
|
336
302
|
}
|
|
337
303
|
|
|
338
|
-
function withScopedFork<R, E, A>(f: (fork: Fx.ScopedFork) => Effect.Effect<R, E, A>): Effect.Effect<R | Scope, E, A> {
|
|
339
|
-
return Effect.scopeWith((scope) => f(Effect.forkIn(scope)))
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// Ensures only a single fiber is executing
|
|
343
|
-
function withSwitchFork<R, E, A>(
|
|
344
|
-
f: (fork: Fx.FxFork, ctx: Context<R | Scope>) => Effect.Effect<R, E, A>
|
|
345
|
-
): Effect.Effect<R | Scope, E, A> {
|
|
346
|
-
return Effect.contextWithEffect((ctx) =>
|
|
347
|
-
withScopedFork((fork) =>
|
|
348
|
-
Effect.flatMap(
|
|
349
|
-
SynchronizedRef.make<Fiber.Fiber<never, void>>(Fiber.unit),
|
|
350
|
-
(ref) =>
|
|
351
|
-
f((effect) =>
|
|
352
|
-
SynchronizedRef.updateAndGetEffect(
|
|
353
|
-
ref,
|
|
354
|
-
(fiber) => Effect.flatMap(Fiber.interrupt(fiber), () => fork(effect))
|
|
355
|
-
), ctx)
|
|
356
|
-
)
|
|
357
|
-
)
|
|
358
|
-
)
|
|
359
|
-
}
|
|
360
|
-
|
|
361
304
|
export class NodePartImpl extends base("node") implements NodePart {}
|
|
362
305
|
|
|
363
306
|
export class PropertyPartImpl extends base("property") implements PropertyPart {
|
|
@@ -403,6 +346,147 @@ export class TextPartImpl extends base("text") implements TextPart {
|
|
|
403
346
|
strictEq
|
|
404
347
|
)
|
|
405
348
|
}
|
|
349
|
+
|
|
350
|
+
static fromText(text: Text, index: number, ctx: RenderContext) {
|
|
351
|
+
return new TextPartImpl(
|
|
352
|
+
index,
|
|
353
|
+
({ part, value }) => ctx.queue.add(part, () => text.nodeValue = value ?? null),
|
|
354
|
+
text.nodeValue,
|
|
355
|
+
strictEq
|
|
356
|
+
)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
static getOrCreateText(document: Document, index: number, element: Element) {
|
|
360
|
+
const comment = findHoleComment(element, index)
|
|
361
|
+
|
|
362
|
+
return comment.previousSibling && isText(comment.previousSibling)
|
|
363
|
+
? comment.previousSibling.nodeValue
|
|
364
|
+
: document.createTextNode("")
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export class PropertiesPartImpl extends base("properties") implements PropertiesPart {
|
|
369
|
+
constructor(
|
|
370
|
+
index: number,
|
|
371
|
+
commit: PropertiesPartImpl["commit"],
|
|
372
|
+
value: PropertiesPartImpl["value"]
|
|
373
|
+
) {
|
|
374
|
+
super(index, commit, value, equals)
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
getValue(value: unknown): unknown {
|
|
378
|
+
if (value == null) return null
|
|
379
|
+
return Data.struct(value)
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
static browser(index: number, element: HTMLElement | SVGElement, ctx: RenderContext) {
|
|
383
|
+
return new PropertiesPartImpl(
|
|
384
|
+
index,
|
|
385
|
+
({ part, previous, value }) =>
|
|
386
|
+
ctx.queue.add(
|
|
387
|
+
part,
|
|
388
|
+
() => {
|
|
389
|
+
const diff = diffProperties(previous, value)
|
|
390
|
+
if (diff) {
|
|
391
|
+
const { added, removed } = diff
|
|
392
|
+
|
|
393
|
+
removed.forEach((nv) => removeNameValue(element, nv))
|
|
394
|
+
added.forEach((nv) => {
|
|
395
|
+
if ((nv.name[0] === "o" && nv.name[1] === "n") || nv.name[0] === "@") return
|
|
396
|
+
|
|
397
|
+
return addNameValue(element, nv)
|
|
398
|
+
})
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
),
|
|
402
|
+
{}
|
|
403
|
+
)
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function removeNameValue(element: HTMLElement | SVGElement, { name, type }: NameValue) {
|
|
408
|
+
switch (type) {
|
|
409
|
+
case "attr":
|
|
410
|
+
case "bool":
|
|
411
|
+
return element.removeAttribute(name)
|
|
412
|
+
case "prop":
|
|
413
|
+
return delete (element as any)[name]
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function addNameValue(element: HTMLElement | SVGElement, { name, type, value }: NameValue) {
|
|
418
|
+
switch (type) {
|
|
419
|
+
case "attr":
|
|
420
|
+
return value == null ? element.removeAttribute(name) : element.setAttribute(name, value)
|
|
421
|
+
case "bool":
|
|
422
|
+
return value == null ? element.removeAttribute(name) : element.toggleAttribute(name, value)
|
|
423
|
+
case "prop":
|
|
424
|
+
return value == null ? (delete (element as any)[name]) : (element as any)[name] = value
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
type AttrNameValue = {
|
|
429
|
+
readonly type: "attr"
|
|
430
|
+
readonly name: string
|
|
431
|
+
readonly value: string
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
type BoolAttrNameValue = {
|
|
435
|
+
readonly type: "bool"
|
|
436
|
+
readonly name: string
|
|
437
|
+
readonly value: boolean
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
type PropNameValue = {
|
|
441
|
+
readonly type: "prop"
|
|
442
|
+
readonly name: string
|
|
443
|
+
readonly value: unknown
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
type NameValue = AttrNameValue | BoolAttrNameValue | PropNameValue
|
|
447
|
+
|
|
448
|
+
function diffProperties(
|
|
449
|
+
a: Record<string, unknown> | null | undefined,
|
|
450
|
+
b: Record<string, unknown> | null | undefined
|
|
451
|
+
): { added: Array<NameValue>; removed: ReadonlyArray<NameValue> } | null {
|
|
452
|
+
if (!a) {
|
|
453
|
+
if (b) {
|
|
454
|
+
return { added: Object.entries(b).flatMap(([k, v]) => fromKeyValue(k, v)), removed: [] }
|
|
455
|
+
} else return null
|
|
456
|
+
} else if (!b) {
|
|
457
|
+
return { added: [], removed: Object.entries(a).flatMap(([k, v]) => fromKeyValue(k, v)) }
|
|
458
|
+
} else {
|
|
459
|
+
const { added, removed, unchanged } = diffStrings(Object.keys(a), Object.keys(b))
|
|
460
|
+
|
|
461
|
+
return {
|
|
462
|
+
added: added.concat(unchanged).flatMap((k) => fromKeyValue(k, b[k])),
|
|
463
|
+
removed: removed.flatMap((k) => fromKeyValue(k, a[k]))
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function fromKeyValue(name: string, value: unknown): Array<NameValue> {
|
|
469
|
+
if (name[0] === ".") {
|
|
470
|
+
return value == null ? [] : [{
|
|
471
|
+
type: "prop",
|
|
472
|
+
name: name.slice(1),
|
|
473
|
+
value
|
|
474
|
+
}]
|
|
475
|
+
} else if (typeof value === "boolean") {
|
|
476
|
+
return [{
|
|
477
|
+
type: "bool",
|
|
478
|
+
name,
|
|
479
|
+
value
|
|
480
|
+
}]
|
|
481
|
+
} else {
|
|
482
|
+
if (name[0] === "o" || name[1] === "n") return []
|
|
483
|
+
|
|
484
|
+
return value == null ? [] : [{
|
|
485
|
+
type: "attr",
|
|
486
|
+
name,
|
|
487
|
+
value: String(value)
|
|
488
|
+
}]
|
|
489
|
+
}
|
|
406
490
|
}
|
|
407
491
|
|
|
408
492
|
const sparse = <T extends SparsePart["_tag"]>(tag: T) =>
|
|
@@ -418,7 +502,8 @@ const sparse = <T extends SparsePart["_tag"]>(tag: T) =>
|
|
|
418
502
|
}
|
|
419
503
|
) => Effect.Effect<Scope, never, void>,
|
|
420
504
|
public value: SparseAttributeValues<Extract<SparsePart, { readonly _tag: T }>["parts"]>,
|
|
421
|
-
readonly eq: Equivalence<SparseAttributeValues<Extract<SparsePart, { readonly _tag: T }>["parts"]>> =
|
|
505
|
+
readonly eq: Equivalence.Equivalence<SparseAttributeValues<Extract<SparsePart, { readonly _tag: T }>["parts"]>> =
|
|
506
|
+
equals
|
|
422
507
|
) {}
|
|
423
508
|
|
|
424
509
|
update = (value: this["value"]) => {
|