@abide/abide 0.32.2 → 0.33.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/AGENTS.md +3 -3
- package/CHANGELOG.md +79 -63
- package/package.json +6 -2
- package/src/lib/server/runtime/buildCacheSnapshot.ts +5 -4
- package/src/lib/server/runtime/types/InspectorCacheEntry.ts +1 -1
- package/src/lib/shared/cache.ts +43 -29
- package/src/lib/shared/types/CacheEntry.ts +12 -12
- package/src/lib/shared/types/CacheOptions.ts +17 -13
- package/src/lib/ui/README.md +1 -1
- package/src/lib/ui/compile/UI_RUNTIME_IMPORTS.ts +4 -1
- package/src/lib/ui/compile/componentWrapperTag.ts +1 -1
- package/src/lib/ui/compile/generateBuild.ts +265 -121
- package/src/lib/ui/compile/generateSSR.ts +78 -37
- package/src/lib/ui/compile/parseTemplate.ts +34 -0
- package/src/lib/ui/compile/skeletonable.ts +80 -0
- package/src/lib/ui/dom/MATHML_NAMESPACE.ts +6 -0
- package/src/lib/ui/dom/SVG_NAMESPACE.ts +7 -0
- package/src/lib/ui/dom/anchorCursor.ts +24 -0
- package/src/lib/ui/dom/appendSnippet.ts +1 -1
- package/src/lib/ui/dom/appendTextAt.ts +70 -0
- package/src/lib/ui/dom/awaitBlock.ts +27 -7
- package/src/lib/ui/dom/cloneStatic.ts +14 -7
- package/src/lib/ui/dom/each.ts +44 -25
- package/src/lib/ui/dom/eachAsync.ts +6 -2
- package/src/lib/ui/dom/effectiveChildNamespace.ts +13 -0
- package/src/lib/ui/dom/enterNamespace.ts +20 -0
- package/src/lib/ui/dom/fillBefore.ts +20 -3
- package/src/lib/ui/dom/foreignWrapperTag.ts +22 -0
- package/src/lib/ui/dom/hydrate.ts +1 -1
- package/src/lib/ui/dom/inheritedNamespace.ts +19 -0
- package/src/lib/ui/dom/mountSlot.ts +32 -0
- package/src/lib/ui/dom/openMarker.ts +4 -2
- package/src/lib/ui/dom/skeleton.ts +202 -0
- package/src/lib/ui/dom/switchBlock.ts +10 -3
- package/src/lib/ui/dom/tryBlock.ts +7 -5
- package/src/lib/ui/dom/types/SkeletonHoles.ts +8 -0
- package/src/lib/ui/dom/when.ts +6 -2
- package/src/lib/ui/installHotBridge.ts +8 -2
- package/src/lib/ui/runtime/HOLE_ATTRIBUTE.ts +9 -0
- package/src/lib/ui/runtime/RENDER.ts +7 -0
- package/src/lib/ui/runtime/types/UiProps.ts +3 -4
- package/template/src/ui/pages/about/page.abide +4 -6
- package/template/src/ui/pages/layout.abide +21 -0
- package/template/src/ui/pages/page.abide +5 -8
- package/src/lib/ui/compile/partitionSlots.ts +0 -36
- package/src/lib/ui/dom/openChild.ts +0 -22
- package/template/src/ui/Layout.abide +0 -19
|
@@ -2,9 +2,11 @@ import { html } from '../shared/html.ts'
|
|
|
2
2
|
import { snippet } from '../shared/snippet.ts'
|
|
3
3
|
import { derived } from './derived.ts'
|
|
4
4
|
import { doc } from './doc.ts'
|
|
5
|
+
import { anchorCursor } from './dom/anchorCursor.ts'
|
|
5
6
|
import { appendSnippet } from './dom/appendSnippet.ts'
|
|
6
7
|
import { appendStatic } from './dom/appendStatic.ts'
|
|
7
8
|
import { appendText } from './dom/appendText.ts'
|
|
9
|
+
import { appendTextAt } from './dom/appendTextAt.ts'
|
|
8
10
|
import { attach } from './dom/attach.ts'
|
|
9
11
|
import { attr } from './dom/attr.ts'
|
|
10
12
|
import { awaitBlock } from './dom/awaitBlock.ts'
|
|
@@ -14,8 +16,9 @@ import { eachAsync } from './dom/eachAsync.ts'
|
|
|
14
16
|
import { hydrate } from './dom/hydrate.ts'
|
|
15
17
|
import { mount } from './dom/mount.ts'
|
|
16
18
|
import { mountChild } from './dom/mountChild.ts'
|
|
19
|
+
import { mountSlot } from './dom/mountSlot.ts'
|
|
17
20
|
import { on } from './dom/on.ts'
|
|
18
|
-
import {
|
|
21
|
+
import { skeleton } from './dom/skeleton.ts'
|
|
19
22
|
import { switchBlock } from './dom/switchBlock.ts'
|
|
20
23
|
import { tryBlock } from './dom/tryBlock.ts'
|
|
21
24
|
import { when } from './dom/when.ts'
|
|
@@ -48,11 +51,13 @@ export function installHotBridge(): void {
|
|
|
48
51
|
derived,
|
|
49
52
|
effect,
|
|
50
53
|
mount,
|
|
51
|
-
openChild,
|
|
52
54
|
appendText,
|
|
55
|
+
appendTextAt,
|
|
53
56
|
appendSnippet,
|
|
54
57
|
appendStatic,
|
|
55
58
|
cloneStatic,
|
|
59
|
+
skeleton,
|
|
60
|
+
anchorCursor,
|
|
56
61
|
attr,
|
|
57
62
|
on,
|
|
58
63
|
attach,
|
|
@@ -62,6 +67,7 @@ export function installHotBridge(): void {
|
|
|
62
67
|
awaitBlock,
|
|
63
68
|
tryBlock,
|
|
64
69
|
switchBlock,
|
|
70
|
+
mountSlot,
|
|
65
71
|
mountChild,
|
|
66
72
|
hydrate,
|
|
67
73
|
nextBlockId,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/*
|
|
2
|
+
The marker attribute the client back-end stamps on each hole element in a skeleton
|
|
3
|
+
string — an element carrying reactive attributes/listeners/binds whose live node the
|
|
4
|
+
build must wire up. Its value is the hole's index, so attach code addresses holes by
|
|
5
|
+
number regardless of document order. `skeleton` records each marked node's path,
|
|
6
|
+
strips the attribute, and returns the holes indexed by it. One source of truth so the
|
|
7
|
+
compiler's emit and the runtime's read can't drift.
|
|
8
|
+
*/
|
|
9
|
+
export const HOLE_ATTRIBUTE = 'data-abide-hole'
|
|
@@ -11,13 +11,20 @@ child components it inlines, so ids are globally unique within one render pass (
|
|
|
11
11
|
SSR stream and client hydration agree on them — `RESUME` is keyed by id). `depth`
|
|
12
12
|
tracks nesting so the OUTERMOST render/mount resets the counter and a child render/
|
|
13
13
|
mount continues it. See `enterRenderPass`/`nextBlockId`.
|
|
14
|
+
|
|
15
|
+
`namespace` is the ambient foreign-content namespace (SVG/MathML) a control-flow block
|
|
16
|
+
sets from its insertion parent while building into a detached fragment, so foreign
|
|
17
|
+
elements built there get the right namespace — the fragment itself carries none. It is
|
|
18
|
+
undefined outside foreign content. See `enterNamespace`/`effectiveChildNamespace`.
|
|
14
19
|
*/
|
|
15
20
|
export const RENDER: {
|
|
16
21
|
hydration: { next: Map<Node, Node | null> } | undefined
|
|
17
22
|
blockId: number
|
|
18
23
|
depth: number
|
|
24
|
+
namespace: string | undefined
|
|
19
25
|
} = {
|
|
20
26
|
hydration: undefined,
|
|
21
27
|
blockId: 0,
|
|
22
28
|
depth: 0,
|
|
29
|
+
namespace: undefined,
|
|
23
30
|
}
|
|
@@ -3,13 +3,12 @@ What a component is invoked with. Two real shapes flow through the one parameter
|
|
|
3
3
|
A top-level page/layout is called by the router (client) and `renderChain` (SSR)
|
|
4
4
|
with its decoded route params — a plain string map. A nested child is called by
|
|
5
5
|
the compiler-emitted `mountChild` with a map of reactive thunks (each authored
|
|
6
|
-
prop, read in the body as `$props[name]?.()`) plus optional slot
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
prop, read in the body as `$props[name]?.()`) plus an optional `$children` slot
|
|
7
|
+
builder carrying the component's `<slot>` markup, mounting into the host element
|
|
8
|
+
it is handed.
|
|
9
9
|
*/
|
|
10
10
|
export type UiProps =
|
|
11
11
|
| Record<string, string>
|
|
12
12
|
| (Record<string, () => unknown> & {
|
|
13
13
|
$children?: (host: Element) => void
|
|
14
|
-
$slots?: Record<string, (host: Element) => void>
|
|
15
14
|
})
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
/* A second page — served at GET /about. Folder name becomes the URL segment
|
|
3
|
-
|
|
2
|
+
/* A second page — served at GET /about. Folder name becomes the URL segment; the
|
|
3
|
+
root layout.abide wraps it, same as the home page. */
|
|
4
4
|
</script>
|
|
5
5
|
|
|
6
|
-
<
|
|
7
|
-
|
|
8
|
-
<p>This is a barebones abide app.</p>
|
|
9
|
-
</Layout>
|
|
6
|
+
<h1>About</h1>
|
|
7
|
+
<p>This is a barebones abide app.</p>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
/*
|
|
3
|
+
Root layout. A layout.abide wraps every page at or below its folder — here
|
|
4
|
+
src/ui/pages/, so it wraps the whole app. The page lands in the <slot/> outlet;
|
|
5
|
+
the resolver finds it by filename (no per-page import), and the client router
|
|
6
|
+
keeps it mounted across navigation, so this chrome never re-mounts. A nested
|
|
7
|
+
layout.abide (in a subfolder) wraps inside this one. Runs on the server during
|
|
8
|
+
SSR and on the client after hydration.
|
|
9
|
+
*/
|
|
10
|
+
import '../app.css'
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<header>
|
|
14
|
+
<nav>
|
|
15
|
+
<a href="/">Home</a>
|
|
16
|
+
<a href="/about">About</a>
|
|
17
|
+
</nav>
|
|
18
|
+
</header>
|
|
19
|
+
<main>
|
|
20
|
+
<slot></slot>
|
|
21
|
+
</main>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
/*
|
|
3
3
|
Root page — served at GET /. Every folder under src/ui/pages/ that contains a
|
|
4
|
-
page.abide mounts at that folder's URL.
|
|
4
|
+
page.abide mounts at that folder's URL; the root layout.abide wraps it.
|
|
5
5
|
|
|
6
6
|
The blocking await-block below (the `then` attribute sits ON the <template await>)
|
|
7
7
|
resolves on the server during SSR and renders inline — no pending placeholder. The
|
|
@@ -11,12 +11,9 @@ instead would stream the resolution in out of order.
|
|
|
11
11
|
*/
|
|
12
12
|
import { cache } from '@abide/abide/shared/cache'
|
|
13
13
|
import { getHello } from '$server/rpc/getHello.ts'
|
|
14
|
-
import Layout from '../Layout.abide'
|
|
15
14
|
</script>
|
|
16
15
|
|
|
17
|
-
<
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<p>Edit <code>src/ui/pages/page.abide</code> and the page hot-reloads.</p>
|
|
22
|
-
</Layout>
|
|
16
|
+
<template await={cache(getHello)()} then="hello">
|
|
17
|
+
<h1>{hello.message}</h1>
|
|
18
|
+
</template>
|
|
19
|
+
<p>Edit <code>src/ui/pages/page.abide</code> and the page hot-reloads.</p>
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { staticAttrValue } from './staticAttrValue.ts'
|
|
2
|
-
import type { TemplateNode } from './types/TemplateNode.ts'
|
|
3
|
-
|
|
4
|
-
/* A component's slotted children split by destination slot. */
|
|
5
|
-
export type SlotGroups = {
|
|
6
|
-
default: TemplateNode[]
|
|
7
|
-
named: { name: string; nodes: TemplateNode[] }[]
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/*
|
|
11
|
-
Partitions a component's children by their `slot="name"` attribute: an element
|
|
12
|
-
carrying one goes to that named group (with the directive attr stripped so it
|
|
13
|
-
never renders as a real attribute), everything else forms the default slot. Both
|
|
14
|
-
back-ends partition identically, so SSR and client agree on which markup lands in
|
|
15
|
-
which `<slot>`.
|
|
16
|
-
*/
|
|
17
|
-
export function partitionSlots(children: TemplateNode[]): SlotGroups {
|
|
18
|
-
const defaults: TemplateNode[] = []
|
|
19
|
-
const named = new Map<string, TemplateNode[]>()
|
|
20
|
-
for (const child of children) {
|
|
21
|
-
const name = child.kind === 'element' ? staticAttrValue(child, 'slot') : undefined
|
|
22
|
-
if (child.kind !== 'element' || name === undefined) {
|
|
23
|
-
defaults.push(child)
|
|
24
|
-
continue
|
|
25
|
-
}
|
|
26
|
-
const stripped = {
|
|
27
|
-
...child,
|
|
28
|
-
attrs: child.attrs.filter((attr) => !(attr.kind === 'static' && attr.name === 'slot')),
|
|
29
|
-
}
|
|
30
|
-
named.set(name, [...(named.get(name) ?? []), stripped])
|
|
31
|
-
}
|
|
32
|
-
return {
|
|
33
|
-
default: defaults,
|
|
34
|
-
named: [...named].map(([name, nodes]) => ({ name, nodes })),
|
|
35
|
-
}
|
|
36
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { claimChild } from '../runtime/claimChild.ts'
|
|
2
|
-
import { RENDER } from '../runtime/RENDER.ts'
|
|
3
|
-
|
|
4
|
-
/*
|
|
5
|
-
Opens a child element of `parent`: creates and appends it (create mode), or claims
|
|
6
|
-
the existing server-rendered node at the parent's current build position (hydrate
|
|
7
|
-
mode), advancing the claim pointer. Returns the element so bindings and children
|
|
8
|
-
attach to it. The compiler emits this for every element so the same build code
|
|
9
|
-
serves both modes.
|
|
10
|
-
*/
|
|
11
|
-
// @readme plumbing
|
|
12
|
-
export function openChild(parent: Node, tag: string): Element {
|
|
13
|
-
const hydration = RENDER.hydration
|
|
14
|
-
if (hydration !== undefined) {
|
|
15
|
-
const current = claimChild(hydration, parent)
|
|
16
|
-
hydration.next.set(parent, current === null ? null : current.nextSibling)
|
|
17
|
-
return current as unknown as Element
|
|
18
|
-
}
|
|
19
|
-
const element = document.createElement(tag)
|
|
20
|
-
parent.appendChild(element)
|
|
21
|
-
return element
|
|
22
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
/*
|
|
3
|
-
Userland root layout. abide has no framework layout resolution — a layout is just
|
|
4
|
-
a component a page wraps its body in (<Layout>…</Layout>), and the body lands in
|
|
5
|
-
the <slot>. Import and wrap this from each page.abide to share chrome. Runs on the
|
|
6
|
-
server during SSR and on the client after hydration.
|
|
7
|
-
*/
|
|
8
|
-
import './app.css'
|
|
9
|
-
</script>
|
|
10
|
-
|
|
11
|
-
<header>
|
|
12
|
-
<nav>
|
|
13
|
-
<a href="/">Home</a>
|
|
14
|
-
<a href="/about">About</a>
|
|
15
|
-
</nav>
|
|
16
|
-
</header>
|
|
17
|
-
<main>
|
|
18
|
-
<slot></slot>
|
|
19
|
-
</main>
|