@typed/template 0.9.6 → 0.10.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/RenderQueue/package.json +6 -0
- package/dist/cjs/Directive.js +1 -1
- package/dist/cjs/Directive.js.map +1 -1
- package/dist/cjs/ElementRef.js +1 -1
- package/dist/cjs/ElementRef.js.map +1 -1
- package/dist/cjs/ElementSource.js +1 -1
- package/dist/cjs/ElementSource.js.map +1 -1
- package/dist/cjs/EventHandler.js +11 -4
- package/dist/cjs/EventHandler.js.map +1 -1
- package/dist/cjs/Html.js +84 -44
- package/dist/cjs/Html.js.map +1 -1
- package/dist/cjs/HtmlChunk.js +67 -21
- package/dist/cjs/HtmlChunk.js.map +1 -1
- package/dist/cjs/Hydrate.js +6 -6
- package/dist/cjs/Hydrate.js.map +1 -1
- package/dist/cjs/Many.js +4 -4
- package/dist/cjs/Many.js.map +1 -1
- package/dist/cjs/Meta.js +10 -3
- package/dist/cjs/Meta.js.map +1 -1
- package/dist/cjs/Parser.js +1 -1
- package/dist/cjs/Placeholder.js +5 -9
- package/dist/cjs/Placeholder.js.map +1 -1
- package/dist/cjs/Platform.js +7 -5
- package/dist/cjs/Platform.js.map +1 -1
- package/dist/cjs/Render.js +8 -7
- package/dist/cjs/Render.js.map +1 -1
- package/dist/cjs/RenderContext.js +8 -92
- package/dist/cjs/RenderContext.js.map +1 -1
- package/dist/cjs/RenderEvent.js +9 -1
- package/dist/cjs/RenderEvent.js.map +1 -1
- package/dist/cjs/RenderQueue.js +341 -0
- package/dist/cjs/RenderQueue.js.map +1 -0
- package/dist/cjs/RenderTemplate.js +1 -1
- package/dist/cjs/RenderTemplate.js.map +1 -1
- package/dist/cjs/Template.js +12 -0
- package/dist/cjs/Template.js.map +1 -1
- package/dist/cjs/Test.js +64 -33
- package/dist/cjs/Test.js.map +1 -1
- package/dist/cjs/Vitest.js +12 -20
- package/dist/cjs/Vitest.js.map +1 -1
- package/dist/cjs/index.js +6 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/internal/EventSource.js +16 -9
- package/dist/cjs/internal/EventSource.js.map +1 -1
- package/dist/cjs/internal/HydrateContext.js.map +1 -1
- package/dist/cjs/internal/browser.js +11 -10
- package/dist/cjs/internal/browser.js.map +1 -1
- package/dist/cjs/internal/character-entities.js +2141 -0
- package/dist/cjs/internal/character-entities.js.map +1 -0
- package/dist/cjs/internal/errors.js +19 -2
- package/dist/cjs/internal/errors.js.map +1 -1
- package/dist/cjs/internal/indexRefCounter.js +36 -63
- package/dist/cjs/internal/indexRefCounter.js.map +1 -1
- package/dist/cjs/internal/parser.js +18 -17
- package/dist/cjs/internal/parser.js.map +1 -1
- package/dist/cjs/internal/parser2.js +382 -0
- package/dist/cjs/internal/parser2.js.map +1 -0
- package/dist/cjs/internal/server-parts.js +124 -0
- package/dist/cjs/internal/server-parts.js.map +1 -0
- package/dist/cjs/internal/server.js +15 -185
- package/dist/cjs/internal/server.js.map +1 -1
- package/dist/cjs/internal/utils.js +73 -9
- package/dist/cjs/internal/utils.js.map +1 -1
- package/dist/cjs/internal/v2/SyncPart.js +6 -0
- package/dist/cjs/internal/v2/SyncPart.js.map +1 -0
- package/dist/cjs/internal/v2/helpers.js +15 -0
- package/dist/cjs/internal/v2/helpers.js.map +1 -0
- package/dist/cjs/internal/v2/hydrate.js +202 -0
- package/dist/cjs/internal/v2/hydrate.js.map +1 -0
- package/dist/cjs/internal/v2/hydration-template.js +269 -0
- package/dist/cjs/internal/v2/hydration-template.js.map +1 -0
- package/dist/cjs/internal/v2/parts.js +169 -0
- package/dist/cjs/internal/v2/parts.js.map +1 -0
- package/dist/cjs/internal/v2/render-entry.js +110 -0
- package/dist/cjs/internal/v2/render-entry.js.map +1 -0
- package/dist/cjs/internal/v2/render-sync-parts.js +318 -0
- package/dist/cjs/internal/v2/render-sync-parts.js.map +1 -0
- package/dist/cjs/internal/v2/render.js +417 -0
- package/dist/cjs/internal/v2/render.js.map +1 -0
- package/dist/cjs/internal/v2/sync-parts.js +115 -0
- package/dist/cjs/internal/v2/sync-parts.js.map +1 -0
- package/dist/dts/ElementRef.d.ts +1 -1
- package/dist/dts/ElementRef.d.ts.map +1 -1
- package/dist/dts/ElementSource.d.ts +1 -1
- package/dist/dts/ElementSource.d.ts.map +1 -1
- package/dist/dts/EventHandler.d.ts +12 -8
- package/dist/dts/EventHandler.d.ts.map +1 -1
- package/dist/dts/Html.d.ts +6 -5
- package/dist/dts/Html.d.ts.map +1 -1
- package/dist/dts/HtmlChunk.d.ts.map +1 -1
- package/dist/dts/Hydrate.d.ts +1 -3
- package/dist/dts/Hydrate.d.ts.map +1 -1
- package/dist/dts/Many.d.ts +9 -11
- package/dist/dts/Many.d.ts.map +1 -1
- package/dist/dts/Meta.d.ts +5 -1
- package/dist/dts/Meta.d.ts.map +1 -1
- package/dist/dts/Parser.d.ts +1 -1
- package/dist/dts/Parser.d.ts.map +1 -1
- package/dist/dts/Part.d.ts +20 -56
- package/dist/dts/Part.d.ts.map +1 -1
- package/dist/dts/Placeholder.d.ts +6 -10
- package/dist/dts/Placeholder.d.ts.map +1 -1
- package/dist/dts/Platform.d.ts +2 -4
- package/dist/dts/Platform.d.ts.map +1 -1
- package/dist/dts/Render.d.ts +4 -8
- package/dist/dts/Render.d.ts.map +1 -1
- package/dist/dts/RenderContext.d.ts +3 -22
- package/dist/dts/RenderContext.d.ts.map +1 -1
- package/dist/dts/RenderEvent.d.ts +6 -1
- package/dist/dts/RenderEvent.d.ts.map +1 -1
- package/dist/dts/RenderQueue.d.ts +103 -0
- package/dist/dts/RenderQueue.d.ts.map +1 -0
- package/dist/dts/RenderTemplate.d.ts +3 -2
- package/dist/dts/RenderTemplate.d.ts.map +1 -1
- package/dist/dts/Renderable.d.ts +1 -1
- package/dist/dts/Template.d.ts +14 -1
- package/dist/dts/Template.d.ts.map +1 -1
- package/dist/dts/Test.d.ts +14 -1
- package/dist/dts/Test.d.ts.map +1 -1
- package/dist/dts/Vitest.d.ts +11 -8
- package/dist/dts/Vitest.d.ts.map +1 -1
- package/dist/dts/index.d.ts +4 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/internal/EventSource.d.ts +2 -1
- package/dist/dts/internal/EventSource.d.ts.map +1 -1
- package/dist/dts/internal/browser.d.ts +3 -3
- package/dist/dts/internal/browser.d.ts.map +1 -1
- package/dist/dts/internal/character-entities.d.ts +2133 -0
- package/dist/dts/internal/character-entities.d.ts.map +1 -0
- package/dist/dts/internal/errors.d.ts +9 -1
- package/dist/dts/internal/errors.d.ts.map +1 -1
- package/dist/dts/internal/indexRefCounter.d.ts +0 -4
- package/dist/dts/internal/indexRefCounter.d.ts.map +1 -1
- package/dist/dts/internal/parser.d.ts +13 -0
- package/dist/dts/internal/parser.d.ts.map +1 -1
- package/dist/dts/internal/parser2.d.ts +12 -0
- package/dist/dts/internal/parser2.d.ts.map +1 -0
- package/dist/dts/internal/server-parts.d.ts +223 -0
- package/dist/dts/internal/server-parts.d.ts.map +1 -0
- package/dist/dts/internal/server.d.ts +2 -28
- package/dist/dts/internal/server.d.ts.map +1 -1
- package/dist/dts/internal/utils.d.ts +4 -1
- package/dist/dts/internal/utils.d.ts.map +1 -1
- package/dist/dts/internal/v2/SyncPart.d.ts +87 -0
- package/dist/dts/internal/v2/SyncPart.d.ts.map +1 -0
- package/dist/dts/internal/v2/helpers.d.ts +3 -0
- package/dist/dts/internal/v2/helpers.d.ts.map +1 -0
- package/dist/dts/internal/v2/hydrate.d.ts +7 -0
- package/dist/dts/internal/v2/hydrate.d.ts.map +1 -0
- package/dist/dts/internal/v2/hydration-template.d.ts +54 -0
- package/dist/dts/internal/v2/hydration-template.d.ts.map +1 -0
- package/dist/dts/internal/v2/parts.d.ts +245 -0
- package/dist/dts/internal/v2/parts.d.ts.map +1 -0
- package/dist/dts/internal/v2/render-entry.d.ts +6 -0
- package/dist/dts/internal/v2/render-entry.d.ts.map +1 -0
- package/dist/dts/internal/v2/render-sync-parts.d.ts +22 -0
- package/dist/dts/internal/v2/render-sync-parts.d.ts.map +1 -0
- package/dist/dts/internal/v2/render.d.ts +62 -0
- package/dist/dts/internal/v2/render.d.ts.map +1 -0
- package/dist/dts/internal/v2/sync-parts.d.ts +129 -0
- package/dist/dts/internal/v2/sync-parts.d.ts.map +1 -0
- package/dist/esm/ElementRef.js.map +1 -1
- package/dist/esm/EventHandler.js +14 -4
- package/dist/esm/EventHandler.js.map +1 -1
- package/dist/esm/Html.js +91 -50
- package/dist/esm/Html.js.map +1 -1
- package/dist/esm/HtmlChunk.js +75 -24
- package/dist/esm/HtmlChunk.js.map +1 -1
- package/dist/esm/Hydrate.js +5 -5
- package/dist/esm/Hydrate.js.map +1 -1
- package/dist/esm/Many.js +3 -3
- package/dist/esm/Many.js.map +1 -1
- package/dist/esm/Meta.js +7 -1
- package/dist/esm/Meta.js.map +1 -1
- package/dist/esm/Parser.js +1 -1
- package/dist/esm/Parser.js.map +1 -1
- package/dist/esm/Placeholder.js +4 -8
- package/dist/esm/Placeholder.js.map +1 -1
- package/dist/esm/Platform.js +3 -1
- package/dist/esm/Platform.js.map +1 -1
- package/dist/esm/Render.js +6 -5
- package/dist/esm/Render.js.map +1 -1
- package/dist/esm/RenderContext.js +5 -85
- package/dist/esm/RenderContext.js.map +1 -1
- package/dist/esm/RenderEvent.js +8 -1
- package/dist/esm/RenderEvent.js.map +1 -1
- package/dist/esm/RenderQueue.js +336 -0
- package/dist/esm/RenderQueue.js.map +1 -0
- package/dist/esm/RenderTemplate.js.map +1 -1
- package/dist/esm/Template.js +12 -0
- package/dist/esm/Template.js.map +1 -1
- package/dist/esm/Test.js +71 -30
- package/dist/esm/Test.js.map +1 -1
- package/dist/esm/Vitest.js +11 -8
- package/dist/esm/Vitest.js.map +1 -1
- package/dist/esm/index.js +4 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/internal/EventSource.js +12 -7
- package/dist/esm/internal/EventSource.js.map +1 -1
- package/dist/esm/internal/HydrateContext.js.map +1 -1
- package/dist/esm/internal/browser.js +10 -9
- package/dist/esm/internal/browser.js.map +1 -1
- package/dist/esm/internal/character-entities.js +2134 -0
- package/dist/esm/internal/character-entities.js.map +1 -0
- package/dist/esm/internal/errors.js +22 -2
- package/dist/esm/internal/errors.js.map +1 -1
- package/dist/esm/internal/indexRefCounter.js +36 -61
- package/dist/esm/internal/indexRefCounter.js.map +1 -1
- package/dist/esm/internal/parser.js +18 -18
- package/dist/esm/internal/parser.js.map +1 -1
- package/dist/esm/internal/parser2.js +393 -0
- package/dist/esm/internal/parser2.js.map +1 -0
- package/dist/esm/internal/server-parts.js +109 -0
- package/dist/esm/internal/server-parts.js.map +1 -0
- package/dist/esm/internal/server.js +12 -161
- package/dist/esm/internal/server.js.map +1 -1
- package/dist/esm/internal/utils.js +71 -7
- package/dist/esm/internal/utils.js.map +1 -1
- package/dist/esm/internal/v2/SyncPart.js +5 -0
- package/dist/esm/internal/v2/SyncPart.js.map +1 -0
- package/dist/esm/internal/v2/helpers.js +12 -0
- package/dist/esm/internal/v2/helpers.js.map +1 -0
- package/dist/esm/internal/v2/hydrate.js +195 -0
- package/dist/esm/internal/v2/hydrate.js.map +1 -0
- package/dist/esm/internal/v2/hydration-template.js +265 -0
- package/dist/esm/internal/v2/hydration-template.js.map +1 -0
- package/dist/esm/internal/v2/parts.js +150 -0
- package/dist/esm/internal/v2/parts.js.map +1 -0
- package/dist/esm/internal/v2/render-entry.js +102 -0
- package/dist/esm/internal/v2/render-entry.js.map +1 -0
- package/dist/esm/internal/v2/render-sync-parts.js +265 -0
- package/dist/esm/internal/v2/render-sync-parts.js.map +1 -0
- package/dist/esm/internal/v2/render.js +353 -0
- package/dist/esm/internal/v2/render.js.map +1 -0
- package/dist/esm/internal/v2/sync-parts.js +102 -0
- package/dist/esm/internal/v2/sync-parts.js.map +1 -0
- package/package.json +20 -13
- package/src/ElementRef.ts +1 -1
- package/src/ElementSource.ts +1 -1
- package/src/EventHandler.ts +29 -11
- package/src/Html.ts +199 -90
- package/src/HtmlChunk.ts +77 -30
- package/src/Hydrate.ts +20 -14
- package/src/Many.ts +17 -14
- package/src/Meta.ts +8 -1
- package/src/Parser.ts +1 -1
- package/src/Part.ts +22 -66
- package/src/Placeholder.ts +17 -15
- package/src/Platform.ts +5 -5
- package/src/Render.ts +23 -26
- package/src/RenderContext.ts +14 -142
- package/src/RenderEvent.ts +10 -1
- package/src/RenderQueue.ts +445 -0
- package/src/RenderTemplate.ts +7 -2
- package/src/Renderable.ts +1 -1
- package/src/Template.ts +15 -1
- package/src/Test.ts +122 -38
- package/src/Vitest.ts +20 -10
- package/src/index.ts +4 -0
- package/src/internal/EventSource.ts +14 -8
- package/src/internal/HydrateContext.ts +3 -4
- package/src/internal/browser.ts +26 -21
- package/src/internal/character-entities.ts +2136 -0
- package/src/internal/errors.ts +30 -3
- package/src/internal/indexRefCounter.ts +38 -70
- package/src/internal/parser.ts +19 -19
- package/src/internal/parser2.ts +468 -0
- package/src/internal/server-parts.ts +161 -0
- package/src/internal/server.ts +16 -272
- package/src/internal/utils.ts +83 -7
- package/src/internal/v2/SyncPart.ts +112 -0
- package/src/internal/v2/helpers.ts +13 -0
- package/src/internal/v2/hydrate.ts +289 -0
- package/src/internal/v2/hydration-template.ts +308 -0
- package/src/internal/v2/parts.ts +254 -0
- package/src/internal/v2/render-entry.ts +131 -0
- package/src/internal/v2/render-sync-parts.ts +440 -0
- package/src/internal/v2/render.ts +588 -0
- package/src/internal/v2/sync-parts.ts +133 -0
- package/dist/cjs/internal/hydrate.js +0 -274
- package/dist/cjs/internal/hydrate.js.map +0 -1
- package/dist/cjs/internal/parts.js +0 -451
- package/dist/cjs/internal/parts.js.map +0 -1
- package/dist/cjs/internal/render.js +0 -704
- package/dist/cjs/internal/render.js.map +0 -1
- package/dist/dts/internal/hydrate.d.ts +0 -33
- package/dist/dts/internal/hydrate.d.ts.map +0 -1
- package/dist/dts/internal/parts.d.ts +0 -314
- package/dist/dts/internal/parts.d.ts.map +0 -1
- package/dist/dts/internal/render.d.ts +0 -16
- package/dist/dts/internal/render.d.ts.map +0 -1
- package/dist/esm/internal/hydrate.js +0 -239
- package/dist/esm/internal/hydrate.js.map +0 -1
- package/dist/esm/internal/parts.js +0 -373
- package/dist/esm/internal/parts.js.map +0 -1
- package/dist/esm/internal/render.js +0 -689
- package/dist/esm/internal/render.js.map +0 -1
- package/src/internal/hydrate.ts +0 -366
- package/src/internal/parts.ts +0 -609
- package/src/internal/render.ts +0 -971
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import * as Fx from "@typed/fx"
|
|
2
|
+
import { isText } from "@typed/wire"
|
|
3
|
+
import type * as Chunk from "effect/Chunk"
|
|
4
|
+
import { unsafeGet } from "effect/Context"
|
|
5
|
+
import * as Effect from "effect/Effect"
|
|
6
|
+
import { flow } from "effect/Function"
|
|
7
|
+
import * as Scope from "effect/Scope"
|
|
8
|
+
import type { BrowserEntry } from "../../Entry.js"
|
|
9
|
+
import type { Placeholder } from "../../Placeholder.js"
|
|
10
|
+
import type { Renderable } from "../../Renderable.js"
|
|
11
|
+
import type { RenderContext } from "../../RenderContext.js"
|
|
12
|
+
import { DomRenderEvent, type RenderEvent } from "../../RenderEvent.js"
|
|
13
|
+
import type { RenderQueue } from "../../RenderQueue.js"
|
|
14
|
+
import type { RenderTemplate } from "../../RenderTemplate.js"
|
|
15
|
+
import type * as Template from "../../Template.js"
|
|
16
|
+
import { CouldNotFindCommentError, isHydrationError } from "../errors.js"
|
|
17
|
+
import { HydrateContext } from "../HydrateContext.js"
|
|
18
|
+
import { findHydratePath, isCommentWithValue, type ParentChildNodes } from "../utils.js"
|
|
19
|
+
import type { HydrationHole, HydrationNode, HydrationTemplate } from "./hydration-template.js"
|
|
20
|
+
import {
|
|
21
|
+
findHydrationHole,
|
|
22
|
+
findHydrationMany,
|
|
23
|
+
findHydrationTemplate,
|
|
24
|
+
getChildNodes,
|
|
25
|
+
getNodes,
|
|
26
|
+
getPreviousNodes
|
|
27
|
+
} from "./hydration-template.js"
|
|
28
|
+
import { getBrowserEntry } from "./render-entry.js"
|
|
29
|
+
import type { TemplateContext } from "./render.js"
|
|
30
|
+
import {
|
|
31
|
+
makeTemplateContext,
|
|
32
|
+
renderTemplate,
|
|
33
|
+
setupAttrPart,
|
|
34
|
+
setupBooleanPart,
|
|
35
|
+
setupClassNamePart,
|
|
36
|
+
setupCommentPart,
|
|
37
|
+
setupDataPart,
|
|
38
|
+
setupEventPart,
|
|
39
|
+
setupNodePart,
|
|
40
|
+
setupPropertiesPart,
|
|
41
|
+
setupPropertyPart,
|
|
42
|
+
setupRefPart,
|
|
43
|
+
setupSparseAttrPart,
|
|
44
|
+
setupSparseClassNamePart,
|
|
45
|
+
setupSparseCommentPart,
|
|
46
|
+
setupTextPart
|
|
47
|
+
} from "./render.js"
|
|
48
|
+
|
|
49
|
+
type HydrateTemplateContext = TemplateContext & {
|
|
50
|
+
readonly where: HydrationNode
|
|
51
|
+
readonly manyKey: string | undefined
|
|
52
|
+
readonly makeHydrateContext: (where: HydrationNode, index: number) => HydrateContext
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function findWhere(hydrateCtx: HydrateContext, entry: BrowserEntry): HydrationTemplate | null {
|
|
56
|
+
// If there is not a manyKey, we can just find the template by its hash
|
|
57
|
+
if (hydrateCtx.manyKey === undefined) {
|
|
58
|
+
return findHydrationTemplate(getChildNodes(hydrateCtx.where), entry.template.hash)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// If there is a manyKey, we need to find the many node first
|
|
62
|
+
const many = findHydrationMany(getChildNodes(hydrateCtx.where), hydrateCtx.manyKey)
|
|
63
|
+
|
|
64
|
+
if (many === null) return null
|
|
65
|
+
|
|
66
|
+
// Then we can find the template by its hash
|
|
67
|
+
return findHydrationTemplate(getChildNodes(many), entry.template.hash)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const hydrateTemplate: (document: Document, ctx: RenderContext) => RenderTemplate = (
|
|
71
|
+
document,
|
|
72
|
+
renderContext
|
|
73
|
+
) => {
|
|
74
|
+
const render_ = renderTemplate(document, renderContext)
|
|
75
|
+
|
|
76
|
+
return <Values extends ReadonlyArray<Renderable<any, any>>>(
|
|
77
|
+
templateStrings: TemplateStringsArray,
|
|
78
|
+
values: Values
|
|
79
|
+
): Fx.Fx<
|
|
80
|
+
RenderEvent,
|
|
81
|
+
Placeholder.Error<Values[number]>,
|
|
82
|
+
Scope.Scope | RenderQueue | Placeholder.Context<Values[number]>
|
|
83
|
+
> => {
|
|
84
|
+
const entry = getBrowserEntry(document, renderContext, templateStrings)
|
|
85
|
+
|
|
86
|
+
return Fx.make((sink) =>
|
|
87
|
+
Effect.catchAllDefect(
|
|
88
|
+
Effect.gen(function*() {
|
|
89
|
+
const ctx = yield* makeTemplateContext(entry.content, document, renderContext, values, sink.onFailure)
|
|
90
|
+
const hydrateCtx = unsafeGet(ctx.context, HydrateContext)
|
|
91
|
+
|
|
92
|
+
// If we're not longer hydrating, just render normally
|
|
93
|
+
if (hydrateCtx.hydrate === false) {
|
|
94
|
+
return yield* render_(templateStrings, values).run(sink)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const where: HydrationTemplate | null = findWhere(hydrateCtx, entry)
|
|
98
|
+
|
|
99
|
+
if (where === null) {
|
|
100
|
+
hydrateCtx.hydrate = false
|
|
101
|
+
return yield* render_(templateStrings, values).run(sink)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const wire = getWire(where)
|
|
105
|
+
if (entry.template.parts.length === 0) return yield* sink.onSuccess(DomRenderEvent(wire))
|
|
106
|
+
|
|
107
|
+
const makeHydrateContext = (where: HydrationNode): HydrateContext => ({
|
|
108
|
+
where,
|
|
109
|
+
parentTemplate: entry.template,
|
|
110
|
+
hydrate: true
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
const effects = setupParts(entry.template.parts, {
|
|
114
|
+
...ctx,
|
|
115
|
+
where,
|
|
116
|
+
manyKey: hydrateCtx.manyKey,
|
|
117
|
+
makeHydrateContext
|
|
118
|
+
})
|
|
119
|
+
if (effects.length > 0) {
|
|
120
|
+
yield* Effect.forEach(effects, flow(Effect.catchAllCause(ctx.onCause), Effect.forkIn(ctx.scope)))
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Setup our event listeners for our wire.
|
|
124
|
+
// We use the parentScope to allow event listeners to exist
|
|
125
|
+
// beyond the lifetime of the current Fiber, but no further than its parent template.
|
|
126
|
+
yield* ctx.eventSource.setup(wire, ctx.parentScope)
|
|
127
|
+
|
|
128
|
+
// We're done setting up so we can resume in "regular" rendering mode when
|
|
129
|
+
// and new templates come in.
|
|
130
|
+
hydrateCtx.hydrate = false
|
|
131
|
+
|
|
132
|
+
// Emit our DomRenderEvent
|
|
133
|
+
yield* sink.onSuccess(DomRenderEvent(wire)).pipe(
|
|
134
|
+
// Ensure our templates last forever in the DOM environment
|
|
135
|
+
// so event listeners are kept attached to the current Scope.
|
|
136
|
+
Effect.zipRight(Effect.never),
|
|
137
|
+
// Close our scope whenever the current Fiber is interrupted
|
|
138
|
+
Effect.onExit((exit) => Scope.close(ctx.scope, exit))
|
|
139
|
+
)
|
|
140
|
+
}),
|
|
141
|
+
(defect) =>
|
|
142
|
+
Effect.gen(function*(_) {
|
|
143
|
+
if (isHydrationError(defect)) {
|
|
144
|
+
yield* _(Effect.logError(defect))
|
|
145
|
+
const hydrateCtx = yield* _(HydrateContext)
|
|
146
|
+
hydrateCtx.hydrate = false
|
|
147
|
+
return yield* _(render_(templateStrings, values).run(sink))
|
|
148
|
+
} else {
|
|
149
|
+
return yield* _(Effect.die(defect))
|
|
150
|
+
}
|
|
151
|
+
})
|
|
152
|
+
)
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function setupParts(parts: Template.Template["parts"], ctx: HydrateTemplateContext) {
|
|
158
|
+
const effects: Array<Effect.Effect<void, any, any>> = []
|
|
159
|
+
|
|
160
|
+
for (const [part, path] of parts) {
|
|
161
|
+
const effect = setupPart(part, path, ctx)
|
|
162
|
+
if (effect) {
|
|
163
|
+
effects.push(effect)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return effects
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function setupPart(
|
|
171
|
+
part: Template.PartNode | Template.SparsePartNode,
|
|
172
|
+
path: Chunk.Chunk<number>,
|
|
173
|
+
ctx: HydrateTemplateContext
|
|
174
|
+
) {
|
|
175
|
+
switch (part._tag) {
|
|
176
|
+
case "attr":
|
|
177
|
+
return setupAttrPart(part, findHydratePath(ctx.where, path) as any, ctx, ctx.values[part.index])
|
|
178
|
+
case "boolean-part":
|
|
179
|
+
return setupBooleanPart(part, findHydratePath(ctx.where, path) as any, ctx, ctx.values[part.index])
|
|
180
|
+
case "className-part":
|
|
181
|
+
return setupClassNamePart(part, findHydratePath(ctx.where, path) as any, ctx, ctx.values[part.index])
|
|
182
|
+
case "comment-part":
|
|
183
|
+
return setupCommentPart(part, findHydratePath(ctx.where, path) as any, ctx)
|
|
184
|
+
case "data":
|
|
185
|
+
return setupDataPart(part, findHydratePath(ctx.where, path) as any, ctx, ctx.values[part.index])
|
|
186
|
+
case "event":
|
|
187
|
+
return setupEventPart(part, findHydratePath(ctx.where, path) as any, ctx, ctx.values[part.index])
|
|
188
|
+
case "node": {
|
|
189
|
+
const hole = findHydrationHole(getChildNodes(ctx.where), part.index)
|
|
190
|
+
if (hole === null) {
|
|
191
|
+
throw new CouldNotFindCommentError(part.index)
|
|
192
|
+
}
|
|
193
|
+
return setupHydratedNodePart(part, hole, ctx)
|
|
194
|
+
}
|
|
195
|
+
case "properties":
|
|
196
|
+
return setupPropertiesPart(part, findHydratePath(ctx.where, path) as any, ctx)
|
|
197
|
+
case "property":
|
|
198
|
+
return setupPropertyPart(part, findHydratePath(ctx.where, path) as any, ctx, ctx.values[part.index])
|
|
199
|
+
case "ref":
|
|
200
|
+
return setupRefPart(part, findHydratePath(ctx.where, path) as any, ctx)
|
|
201
|
+
case "sparse-attr":
|
|
202
|
+
return setupSparseAttrPart(part, findHydratePath(ctx.where, path) as any, ctx)
|
|
203
|
+
case "sparse-class-name":
|
|
204
|
+
return setupSparseClassNamePart(part, findHydratePath(ctx.where, path) as any, ctx)
|
|
205
|
+
case "sparse-comment":
|
|
206
|
+
return setupSparseCommentPart(part, findHydratePath(ctx.where, path) as any, ctx)
|
|
207
|
+
case "text-part": {
|
|
208
|
+
const hole = findHydrationHole(getChildNodes(ctx.where), part.index)
|
|
209
|
+
if (hole === null) throw new CouldNotFindCommentError(part.index)
|
|
210
|
+
return setupTextPart(part, hole.endComment, ctx)
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function setupHydratedNodePart(
|
|
216
|
+
part: Template.NodePart,
|
|
217
|
+
hole: HydrationHole,
|
|
218
|
+
ctx: HydrateTemplateContext
|
|
219
|
+
) {
|
|
220
|
+
const nestedCtx = ctx.makeHydrateContext(hole, part.index)
|
|
221
|
+
const previousNodes = getPreviousNodes(hole)
|
|
222
|
+
const text = previousNodes.length === 2 && isCommentWithValue(previousNodes[0], "text") && isText(previousNodes[1])
|
|
223
|
+
? previousNodes[1]
|
|
224
|
+
: null
|
|
225
|
+
const effect = setupNodePart(part, hole.endComment, ctx, text, text === null ? previousNodes : [text])
|
|
226
|
+
if (effect === null) return null
|
|
227
|
+
return Effect.provideService(effect, HydrateContext, nestedCtx)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function findRootParentChildNodes(where: HTMLElement): ParentChildNodes {
|
|
231
|
+
const childNodes = findRootChildNodes(where)
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
parentNode: where,
|
|
235
|
+
childNodes
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const START = "typed-start"
|
|
240
|
+
const END = "typed-end"
|
|
241
|
+
|
|
242
|
+
// Finds all of the childNodes between the "typed-start" and "typed-end" comments
|
|
243
|
+
export function findRootChildNodes(where: HTMLElement): Array<Node> {
|
|
244
|
+
let start = -1
|
|
245
|
+
let end = -1
|
|
246
|
+
|
|
247
|
+
const { childNodes } = where
|
|
248
|
+
const length = childNodes.length
|
|
249
|
+
|
|
250
|
+
for (let i = 0; i < length; i++) {
|
|
251
|
+
const node = childNodes[i]
|
|
252
|
+
|
|
253
|
+
if (node.nodeType === node.COMMENT_NODE && node.nodeValue === START) {
|
|
254
|
+
start = i
|
|
255
|
+
break
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
for (let i = length - 1; i >= Math.max(start, 0); i--) {
|
|
260
|
+
const node = childNodes[i]
|
|
261
|
+
|
|
262
|
+
if (node.nodeType === node.COMMENT_NODE && node.nodeValue === END) {
|
|
263
|
+
end = i
|
|
264
|
+
break
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// If we can't find the start and end comments, just return all childNodes
|
|
269
|
+
if (start === -1 && end === -1) {
|
|
270
|
+
return Array.from(childNodes)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
start = start === -1 ? 0 : start
|
|
274
|
+
end = end === -1 ? length - 1 : end
|
|
275
|
+
|
|
276
|
+
const rootChildNodes: Array<Node> = Array(end - start)
|
|
277
|
+
|
|
278
|
+
for (let i = start + 1, j = 0; i <= end; i++) {
|
|
279
|
+
rootChildNodes[j++] = childNodes[i]
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return rootChildNodes
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function getWire(where: HydrationNode) {
|
|
286
|
+
const nodes = getNodes(where)
|
|
287
|
+
if (nodes.length === 1) return nodes[0]
|
|
288
|
+
return nodes
|
|
289
|
+
}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { isComment, isElement, toHtml } from "@typed/wire"
|
|
2
|
+
import { type Inspectable, NodeInspectSymbol } from "effect/Inspectable"
|
|
3
|
+
import { CouldNotFindRootElement, CouldNotFindTemplateEndError } from "../errors"
|
|
4
|
+
|
|
5
|
+
const TYPED_TEMPLATE_PREFIX = `typed-`
|
|
6
|
+
const TYPED_TEMPLATE_END_PREFIX = `/typed-`
|
|
7
|
+
const MANY_PREFIX = `many`
|
|
8
|
+
const HOLE_PREFIX = `hole`
|
|
9
|
+
|
|
10
|
+
export function getHydrationRoot(root: HTMLElement): HydrationElement {
|
|
11
|
+
const childNodes = Array.from(root.childNodes)
|
|
12
|
+
let hydrationNodes = getHydrationNodes(childNodes)
|
|
13
|
+
|
|
14
|
+
// If your whole template is wrapped in a single hole, unwrap it.
|
|
15
|
+
if (hydrationNodes.length === 1 && hydrationNodes[0]._tag === "hole") {
|
|
16
|
+
hydrationNodes = getChildNodes(hydrationNodes[0])
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return new HydrationElement(root, hydrationNodes)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getHydrationNodes(nodes: Array<Node>): Array<HydrationNode> {
|
|
23
|
+
const out: Array<HydrationNode> = []
|
|
24
|
+
|
|
25
|
+
for (let i = 0; i < nodes.length; ++i) {
|
|
26
|
+
const node = nodes[i]
|
|
27
|
+
|
|
28
|
+
if (isComment(node)) {
|
|
29
|
+
if (node.data.startsWith(TYPED_TEMPLATE_PREFIX)) {
|
|
30
|
+
const hash = node.data.slice(TYPED_TEMPLATE_PREFIX.length)
|
|
31
|
+
const endIndex = getTemplateEndIndex(nodes, i, hash)
|
|
32
|
+
const childNodes = nodes.slice(i + 1, endIndex)
|
|
33
|
+
|
|
34
|
+
out.push(new HydrationTemplate(hash, getHydrationNodes(childNodes)))
|
|
35
|
+
|
|
36
|
+
i = endIndex
|
|
37
|
+
} else if (node.data.startsWith(MANY_PREFIX)) {
|
|
38
|
+
const last = out.pop()
|
|
39
|
+
out.push(new HydrationMany(node.data.slice(MANY_PREFIX.length), node, last ? [last] : []))
|
|
40
|
+
} else if (node.data.startsWith(HOLE_PREFIX)) {
|
|
41
|
+
const index = parseInt(node.data.slice(HOLE_PREFIX.length), 10)
|
|
42
|
+
const endIndex = getHoleEndIndex(nodes, i, index)
|
|
43
|
+
const endComment = nodes[endIndex] as Comment
|
|
44
|
+
out.push(new HydrationHole(index, node, endComment, getHydrationNodes(nodes.slice(i + 1, endIndex))))
|
|
45
|
+
i = endIndex
|
|
46
|
+
} else {
|
|
47
|
+
out.push(new HydrationLiteral(node))
|
|
48
|
+
}
|
|
49
|
+
} else if (isElement(node)) {
|
|
50
|
+
out.push(new HydrationElement(node, getHydrationNodes(Array.from(node.childNodes))))
|
|
51
|
+
} else {
|
|
52
|
+
out.push(new HydrationLiteral(node))
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return out
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function getTemplateEndIndex(nodes: Array<Node>, start: number, hash: string): number {
|
|
60
|
+
const endHash = TYPED_TEMPLATE_END_PREFIX + hash
|
|
61
|
+
|
|
62
|
+
for (let i = start; i < nodes.length; ++i) {
|
|
63
|
+
const node = nodes[i]
|
|
64
|
+
|
|
65
|
+
if (isComment(node) && node.data === endHash) {
|
|
66
|
+
return i
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
throw new CouldNotFindTemplateEndError(hash)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function getHoleEndIndex(nodes: Array<Node>, start: number, index: number): number {
|
|
74
|
+
const endHash = `/hole${index}`
|
|
75
|
+
|
|
76
|
+
let templateDepth = 0
|
|
77
|
+
|
|
78
|
+
for (let i = start; i < nodes.length; ++i) {
|
|
79
|
+
const node = nodes[i]
|
|
80
|
+
|
|
81
|
+
if (isComment(node)) {
|
|
82
|
+
if (templateDepth === 0 && node.data === endHash) return i
|
|
83
|
+
else if (node.data.startsWith(TYPED_TEMPLATE_PREFIX)) templateDepth++
|
|
84
|
+
else if (node.data.startsWith(TYPED_TEMPLATE_END_PREFIX)) templateDepth--
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
throw new CouldNotFindRootElement(index)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export class HydrationElement implements Inspectable {
|
|
92
|
+
readonly _tag = "element" as const
|
|
93
|
+
|
|
94
|
+
constructor(
|
|
95
|
+
readonly parentNode: Element,
|
|
96
|
+
readonly childNodes: Array<HydrationNode>
|
|
97
|
+
) {}
|
|
98
|
+
|
|
99
|
+
toJSON(): unknown {
|
|
100
|
+
return {
|
|
101
|
+
_tag: this._tag,
|
|
102
|
+
parentNode: toHtml(this.parentNode),
|
|
103
|
+
childNodes: this.childNodes.map((n) => n.toJSON())
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
[NodeInspectSymbol]() {
|
|
108
|
+
return JSON.stringify(this.toJSON(), null, 2)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export class HydrationTemplate implements Inspectable {
|
|
113
|
+
readonly _tag = "template" as const
|
|
114
|
+
|
|
115
|
+
constructor(
|
|
116
|
+
readonly hash: string,
|
|
117
|
+
readonly childNodes: Array<HydrationNode>
|
|
118
|
+
) {}
|
|
119
|
+
|
|
120
|
+
toJSON(): unknown {
|
|
121
|
+
return {
|
|
122
|
+
_tag: this._tag,
|
|
123
|
+
hash: this.hash,
|
|
124
|
+
childNodes: this.childNodes.map((n) => n.toJSON())
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
[NodeInspectSymbol]() {
|
|
129
|
+
return JSON.stringify(this.toJSON(), null, 2)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export type HydrationNode = HydrationElement | HydrationTemplate | HydrationMany | HydrationHole | HydrationLiteral
|
|
134
|
+
|
|
135
|
+
export class HydrationMany implements Inspectable {
|
|
136
|
+
readonly _tag = "many" as const
|
|
137
|
+
|
|
138
|
+
constructor(
|
|
139
|
+
readonly key: string,
|
|
140
|
+
readonly comment: Comment,
|
|
141
|
+
readonly childNodes: Array<HydrationNode>
|
|
142
|
+
) {}
|
|
143
|
+
|
|
144
|
+
toJSON(): unknown {
|
|
145
|
+
return {
|
|
146
|
+
_tag: this._tag,
|
|
147
|
+
key: this.key,
|
|
148
|
+
childNodes: this.childNodes.map((n) => n.toJSON())
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
[NodeInspectSymbol]() {
|
|
153
|
+
return JSON.stringify(this.toJSON(), null, 2)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export class HydrationHole implements Inspectable {
|
|
158
|
+
readonly _tag = "hole" as const
|
|
159
|
+
|
|
160
|
+
constructor(
|
|
161
|
+
readonly index: number,
|
|
162
|
+
readonly startComment: Comment,
|
|
163
|
+
readonly endComment: Comment,
|
|
164
|
+
readonly childNodes: Array<HydrationNode>
|
|
165
|
+
) {}
|
|
166
|
+
|
|
167
|
+
toJSON(): unknown {
|
|
168
|
+
return {
|
|
169
|
+
_tag: this._tag,
|
|
170
|
+
index: this.index,
|
|
171
|
+
childNodes: this.childNodes.map((n) => n.toJSON())
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
[NodeInspectSymbol]() {
|
|
176
|
+
return JSON.stringify(this.toJSON(), null, 2)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export class HydrationLiteral implements Inspectable {
|
|
181
|
+
readonly _tag = "literal" as const
|
|
182
|
+
|
|
183
|
+
constructor(
|
|
184
|
+
readonly node: Node
|
|
185
|
+
) {}
|
|
186
|
+
|
|
187
|
+
toJSON(): unknown {
|
|
188
|
+
return {
|
|
189
|
+
_tag: this._tag,
|
|
190
|
+
node: toHtml(this.node)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
[NodeInspectSymbol]() {
|
|
195
|
+
return JSON.stringify(this.toJSON(), null, 2)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function getChildNodes(node: HydrationNode): Array<HydrationNode> {
|
|
200
|
+
switch (node._tag) {
|
|
201
|
+
case "literal":
|
|
202
|
+
return []
|
|
203
|
+
case "hole":
|
|
204
|
+
case "element":
|
|
205
|
+
case "template":
|
|
206
|
+
case "many":
|
|
207
|
+
return node.childNodes
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function findHydrationTemplate(
|
|
212
|
+
nodes: Array<HydrationNode>,
|
|
213
|
+
templateHash: string
|
|
214
|
+
): HydrationTemplate | null {
|
|
215
|
+
const toProcess: Array<HydrationNode> = [...nodes]
|
|
216
|
+
|
|
217
|
+
while (toProcess.length > 0) {
|
|
218
|
+
const node = toProcess.shift()!
|
|
219
|
+
|
|
220
|
+
if (node._tag === "template" && node.hash === templateHash) {
|
|
221
|
+
return node
|
|
222
|
+
} else if (node._tag === "element") {
|
|
223
|
+
toProcess.push(...node.childNodes)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return null
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function findHydrationMany(
|
|
231
|
+
nodes: Array<HydrationNode>,
|
|
232
|
+
key: string
|
|
233
|
+
): HydrationMany | null {
|
|
234
|
+
for (let i = 0; i < nodes.length; ++i) {
|
|
235
|
+
const node = nodes[i]
|
|
236
|
+
if (node._tag === "many" && node.key === key) {
|
|
237
|
+
return node
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return null
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function findHydrationHole(nodes: Array<HydrationNode>, index: number): HydrationHole | null {
|
|
245
|
+
for (const node of nodes) {
|
|
246
|
+
if (node._tag === "hole" && node.index === index) {
|
|
247
|
+
return node
|
|
248
|
+
} else if (node._tag === "element") {
|
|
249
|
+
const found = findHydrationHole(node.childNodes, index)
|
|
250
|
+
if (found !== null) {
|
|
251
|
+
return found
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return null
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export function findHydrationNode(
|
|
260
|
+
node: HydrationNode,
|
|
261
|
+
index: number,
|
|
262
|
+
manyKey?: string
|
|
263
|
+
): HydrationHole | HydrationMany | null {
|
|
264
|
+
const childNodes = getChildNodes(node)
|
|
265
|
+
const found = manyKey === undefined
|
|
266
|
+
? findHydrationHole(childNodes, index)
|
|
267
|
+
: findHydrationMany(childNodes, manyKey)
|
|
268
|
+
|
|
269
|
+
return found
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export function getNodes(node: HydrationNode): Array<Node> {
|
|
273
|
+
switch (node._tag) {
|
|
274
|
+
case "element":
|
|
275
|
+
return [node.parentNode]
|
|
276
|
+
case "literal":
|
|
277
|
+
return [node.node]
|
|
278
|
+
case "hole":
|
|
279
|
+
return [node.startComment, ...node.childNodes.flatMap(getNodes), node.endComment]
|
|
280
|
+
case "many":
|
|
281
|
+
return [...node.childNodes.flatMap(getNodes), node.comment]
|
|
282
|
+
case "template":
|
|
283
|
+
return node.childNodes.flatMap(getNodes)
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export function getNodesExcludingStartComment(node: HydrationNode): Array<Node> {
|
|
288
|
+
switch (node._tag) {
|
|
289
|
+
case "element":
|
|
290
|
+
return [node.parentNode]
|
|
291
|
+
case "literal":
|
|
292
|
+
return [node.node]
|
|
293
|
+
case "hole":
|
|
294
|
+
return [...node.childNodes.flatMap(getNodesExcludingStartComment), node.endComment]
|
|
295
|
+
case "many":
|
|
296
|
+
return [...node.childNodes.flatMap(getNodesExcludingStartComment), node.comment]
|
|
297
|
+
case "template":
|
|
298
|
+
return node.childNodes.flatMap(getNodesExcludingStartComment)
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function getPreviousNodes(hole: HydrationHole | HydrationMany): Array<Node> {
|
|
303
|
+
const predicate: (n: Node) => boolean = hole._tag === "hole"
|
|
304
|
+
? (n) => n !== hole.startComment && n !== hole.startComment
|
|
305
|
+
: (n) => n !== hole.comment
|
|
306
|
+
|
|
307
|
+
return hole.childNodes.flatMap(getNodes).filter(predicate)
|
|
308
|
+
}
|