@abide/abide 0.32.1 → 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 +93 -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 +3 -3
- package/src/lib/ui/compile/HTML_TAGS.ts +132 -0
- package/src/lib/ui/compile/UI_RUNTIME_IMPORTS.ts +4 -1
- package/src/lib/ui/compile/componentWrapperTag.ts +13 -10
- 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 +52 -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 +15 -24
- 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/templateFor.ts +28 -0
- 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/createDoc.ts +11 -8
- package/src/lib/ui/runtime/types/PathWalk.ts +10 -0
- package/src/lib/ui/runtime/types/UiProps.ts +3 -4
- package/src/lib/ui/runtime/walkPath.ts +27 -0
- 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/src/lib/ui/runtime/pathExists.ts +0 -23
- package/src/lib/ui/runtime/valueAtPath.ts +0 -18
- package/template/src/ui/Layout.abide +0 -19
|
@@ -18,22 +18,26 @@ for per-request data: the default keeps a per-user response from leaking across
|
|
|
18
18
|
requests. Write only `global: true`; there is no `false` form. On the client
|
|
19
19
|
there is a single tab store, so the flag is a no-op there.
|
|
20
20
|
|
|
21
|
-
`
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
21
|
+
`swr` is stale-while-revalidate: it changes what a `cache.invalidate` hit does
|
|
22
|
+
to this key. Without it, an invalidate drops the entry and the next read shows
|
|
23
|
+
`pending()`. With it, the entry is kept and refetched in the background — the
|
|
24
|
+
existing (stale) value stays visible and `refreshing()` reports the in-flight
|
|
25
|
+
reload — so the reader never blanks. It governs only the refetch-after-invalidate;
|
|
26
|
+
the first fetch and arg-change fetches stay immediate regardless.
|
|
27
|
+
|
|
28
|
+
`swr: true` refetches immediately on every invalidate. An optional window
|
|
29
|
+
coalesces a burst (e.g. a socket spraying `cache.invalidate`) into far fewer
|
|
30
|
+
calls: `swr: { throttle: N }` refetches on the leading edge then at most once
|
|
31
|
+
per N ms while invalidations keep arriving; `swr: { debounce: N }` refetches
|
|
32
|
+
only after N ms of quiet. `swr` declares the call safe to re-run unprompted:
|
|
33
|
+
cache() throws at wrap time on throttle+debounce set at once, on ttl: 0 (nothing
|
|
34
|
+
retained, nothing to revalidate), and on a non-replayable remote method
|
|
35
|
+
(replaying a write is a state change disguised as a refresh). Producers are
|
|
36
|
+
uncheckable — set `swr` only on a producer that is a pure read.
|
|
33
37
|
*/
|
|
34
38
|
export type CacheOptions = {
|
|
35
39
|
ttl?: number
|
|
36
40
|
scope?: string | string[]
|
|
37
41
|
global?: boolean
|
|
38
|
-
|
|
42
|
+
swr?: boolean | { throttle?: number; debounce?: number }
|
|
39
43
|
}
|
package/src/lib/ui/README.md
CHANGED
|
@@ -46,8 +46,8 @@ every page, and `.abide` files are the only component format.
|
|
|
46
46
|
`<template await>`/`then`/`catch`, `<template switch>`/`case`/`default`.
|
|
47
47
|
- **Components** are capitalised tags (`<Layout title="…">`); children fill the
|
|
48
48
|
child's `<slot>`. Props are reactive (passed as thunks).
|
|
49
|
-
- **Scoped styles**: a `<style>` block is scoped
|
|
50
|
-
|
|
49
|
+
- **Scoped styles**: a `<style>` block is scoped via a `[data-a-<hash>]`
|
|
50
|
+
attribute — per component, or per control-flow branch when nested in one.
|
|
51
51
|
|
|
52
52
|
## Substrate (why it's fast)
|
|
53
53
|
|
|
@@ -70,7 +70,7 @@ write-path microbench this runs ~20× faster than a deep-proxy signal baseline.
|
|
|
70
70
|
```
|
|
71
71
|
.abide → analyzeComponent (split script/style/template, desugar signals → doc,
|
|
72
72
|
lower data access, scope CSS)
|
|
73
|
-
→ generateBuild (client:
|
|
73
|
+
→ generateBuild (client: skeleton/cloneStatic/appendText/attr/on/each/when/…)
|
|
74
74
|
→ generateSSR (server: HTML-string back-end, await markers)
|
|
75
75
|
→ hoistCells (static paths → cells)
|
|
76
76
|
→ compileModule (ES module: default mount + render() for SSR)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Every standard HTML element name (lowercase), plus the two foreign-content roots
|
|
3
|
+
`svg`/`math`. A component wrapper tag that collides with one of these is a real
|
|
4
|
+
element with a content model and parser quirks — `<button>`/`<a>` reject interactive
|
|
5
|
+
descendants, table/list/select families foster or auto-close non-conforming children,
|
|
6
|
+
void elements self-close, and `<svg>`/`<math>` switch the parser into SVG/MathML
|
|
7
|
+
namespace so the wrapper's HTML children break out (foster-parent) of it entirely —
|
|
8
|
+
so the parser reparents the component's own markup out of the wrapper and hydration
|
|
9
|
+
claims `null`. `componentWrapperTag` remaps any such name to a transparent custom
|
|
10
|
+
element instead. Pure SVG/MathML descendant names (`circle`, `path`, `mrow`, …) are
|
|
11
|
+
NOT here: in HTML context they are inert unknown elements that hold children fine, so
|
|
12
|
+
a `<Circle>` component stays as-is like any non-element name. Superset of VOID_TAGS
|
|
13
|
+
(which the parser/SSR still use for self-closing); this set is only the
|
|
14
|
+
wrapper-safety check.
|
|
15
|
+
*/
|
|
16
|
+
export const HTML_TAGS: ReadonlySet<string> = new Set([
|
|
17
|
+
'a',
|
|
18
|
+
'abbr',
|
|
19
|
+
'address',
|
|
20
|
+
'area',
|
|
21
|
+
'article',
|
|
22
|
+
'aside',
|
|
23
|
+
'audio',
|
|
24
|
+
'b',
|
|
25
|
+
'base',
|
|
26
|
+
'bdi',
|
|
27
|
+
'bdo',
|
|
28
|
+
'blockquote',
|
|
29
|
+
'body',
|
|
30
|
+
'br',
|
|
31
|
+
'button',
|
|
32
|
+
'canvas',
|
|
33
|
+
'caption',
|
|
34
|
+
'cite',
|
|
35
|
+
'code',
|
|
36
|
+
'col',
|
|
37
|
+
'colgroup',
|
|
38
|
+
'data',
|
|
39
|
+
'datalist',
|
|
40
|
+
'dd',
|
|
41
|
+
'del',
|
|
42
|
+
'details',
|
|
43
|
+
'dfn',
|
|
44
|
+
'dialog',
|
|
45
|
+
'div',
|
|
46
|
+
'dl',
|
|
47
|
+
'dt',
|
|
48
|
+
'em',
|
|
49
|
+
'embed',
|
|
50
|
+
'fieldset',
|
|
51
|
+
'figcaption',
|
|
52
|
+
'figure',
|
|
53
|
+
'footer',
|
|
54
|
+
'form',
|
|
55
|
+
'h1',
|
|
56
|
+
'h2',
|
|
57
|
+
'h3',
|
|
58
|
+
'h4',
|
|
59
|
+
'h5',
|
|
60
|
+
'h6',
|
|
61
|
+
'head',
|
|
62
|
+
'header',
|
|
63
|
+
'hgroup',
|
|
64
|
+
'hr',
|
|
65
|
+
'html',
|
|
66
|
+
'i',
|
|
67
|
+
'iframe',
|
|
68
|
+
'img',
|
|
69
|
+
'input',
|
|
70
|
+
'ins',
|
|
71
|
+
'kbd',
|
|
72
|
+
'label',
|
|
73
|
+
'legend',
|
|
74
|
+
'li',
|
|
75
|
+
'link',
|
|
76
|
+
'main',
|
|
77
|
+
'map',
|
|
78
|
+
'mark',
|
|
79
|
+
'math',
|
|
80
|
+
'menu',
|
|
81
|
+
'meta',
|
|
82
|
+
'meter',
|
|
83
|
+
'nav',
|
|
84
|
+
'noscript',
|
|
85
|
+
'object',
|
|
86
|
+
'ol',
|
|
87
|
+
'optgroup',
|
|
88
|
+
'option',
|
|
89
|
+
'output',
|
|
90
|
+
'p',
|
|
91
|
+
'param',
|
|
92
|
+
'picture',
|
|
93
|
+
'pre',
|
|
94
|
+
'progress',
|
|
95
|
+
'q',
|
|
96
|
+
'rp',
|
|
97
|
+
'rt',
|
|
98
|
+
'ruby',
|
|
99
|
+
's',
|
|
100
|
+
'samp',
|
|
101
|
+
'script',
|
|
102
|
+
'search',
|
|
103
|
+
'section',
|
|
104
|
+
'select',
|
|
105
|
+
'slot',
|
|
106
|
+
'small',
|
|
107
|
+
'source',
|
|
108
|
+
'span',
|
|
109
|
+
'strong',
|
|
110
|
+
'style',
|
|
111
|
+
'sub',
|
|
112
|
+
'summary',
|
|
113
|
+
'sup',
|
|
114
|
+
'svg',
|
|
115
|
+
'table',
|
|
116
|
+
'tbody',
|
|
117
|
+
'td',
|
|
118
|
+
'template',
|
|
119
|
+
'textarea',
|
|
120
|
+
'tfoot',
|
|
121
|
+
'th',
|
|
122
|
+
'thead',
|
|
123
|
+
'time',
|
|
124
|
+
'title',
|
|
125
|
+
'tr',
|
|
126
|
+
'track',
|
|
127
|
+
'u',
|
|
128
|
+
'ul',
|
|
129
|
+
'var',
|
|
130
|
+
'video',
|
|
131
|
+
'wbr',
|
|
132
|
+
])
|
|
@@ -14,11 +14,13 @@ export const UI_RUNTIME_IMPORTS: { name: string; specifier: string }[] = [
|
|
|
14
14
|
{ name: 'derived', specifier: 'ui/derived' },
|
|
15
15
|
{ name: 'effect', specifier: 'ui/effect' },
|
|
16
16
|
{ name: 'mount', specifier: 'ui/dom/mount' },
|
|
17
|
-
{ name: 'openChild', specifier: 'ui/dom/openChild' },
|
|
18
17
|
{ name: 'appendText', specifier: 'ui/dom/appendText' },
|
|
18
|
+
{ name: 'appendTextAt', specifier: 'ui/dom/appendTextAt' },
|
|
19
19
|
{ name: 'appendSnippet', specifier: 'ui/dom/appendSnippet' },
|
|
20
20
|
{ name: 'appendStatic', specifier: 'ui/dom/appendStatic' },
|
|
21
21
|
{ name: 'cloneStatic', specifier: 'ui/dom/cloneStatic' },
|
|
22
|
+
{ name: 'skeleton', specifier: 'ui/dom/skeleton' },
|
|
23
|
+
{ name: 'anchorCursor', specifier: 'ui/dom/anchorCursor' },
|
|
22
24
|
{ name: 'attr', specifier: 'ui/dom/attr' },
|
|
23
25
|
{ name: 'on', specifier: 'ui/dom/on' },
|
|
24
26
|
{ name: 'attach', specifier: 'ui/dom/attach' },
|
|
@@ -28,6 +30,7 @@ export const UI_RUNTIME_IMPORTS: { name: string; specifier: string }[] = [
|
|
|
28
30
|
{ name: 'awaitBlock', specifier: 'ui/dom/awaitBlock' },
|
|
29
31
|
{ name: 'tryBlock', specifier: 'ui/dom/tryBlock' },
|
|
30
32
|
{ name: 'switchBlock', specifier: 'ui/dom/switchBlock' },
|
|
33
|
+
{ name: 'mountSlot', specifier: 'ui/dom/mountSlot' },
|
|
31
34
|
{ name: 'mountChild', specifier: 'ui/dom/mountChild' },
|
|
32
35
|
{ name: 'hydrate', specifier: 'ui/dom/hydrate' },
|
|
33
36
|
{ name: 'nextBlockId', specifier: 'ui/runtime/nextBlockId' },
|
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HTML_TAGS } from './HTML_TAGS.ts'
|
|
2
2
|
|
|
3
3
|
/*
|
|
4
4
|
The element tag a component instance mounts into. Normally the component name
|
|
5
5
|
lowercased — readable in devtools, a real box like any abide wrapper. But a name
|
|
6
|
-
that lowercases to a
|
|
7
|
-
wrapper
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
that lowercases to a real HTML element (`Button`→`button`, `Input`→`input`) yields a
|
|
7
|
+
wrapper with a content model the parser enforces: void elements self-close, and
|
|
8
|
+
`<button>`/`<a>`/table/list/select families reject or foster the component's own
|
|
9
|
+
markup as the wrapper's siblings — so on hydration the skeleton locates the wrapper
|
|
10
|
+
empty, claims `null`, and `attr` throws on it. Those names map to a hyphenated
|
|
11
|
+
custom-element tag (a custom element is never void and has no content model) made
|
|
12
|
+
layout-transparent with `display:contents`, so the component's real root still lays
|
|
13
|
+
out as a direct child of the parent the way the (parse-broken) wrapper would have.
|
|
14
|
+
A name that is NOT a known HTML element (the common case — `Card`, `Dropdown`) is an
|
|
15
|
+
inert unknown tag that holds any content untouched, so it stays as-is. Both back-ends
|
|
16
|
+
call this so the SSR string and the client build agree on the wrapper.
|
|
14
17
|
*/
|
|
15
18
|
export function componentWrapperTag(name: string): { tag: string; transparent: boolean } {
|
|
16
19
|
const lower = name.toLowerCase()
|
|
17
|
-
return
|
|
20
|
+
return HTML_TAGS.has(lower)
|
|
18
21
|
? { tag: `abide-${lower}`, transparent: true }
|
|
19
22
|
: { tag: lower, transparent: false }
|
|
20
23
|
}
|