@abide/abide 0.29.0 → 0.30.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 +2 -1
- package/CHANGELOG.md +12 -0
- package/package.json +1 -1
- package/src/lib/ui/compile/compileShadow.ts +25 -6
- package/src/lib/ui/router.ts +22 -5
package/AGENTS.md
CHANGED
|
@@ -178,9 +178,10 @@ Same selector grammar as `cache.invalidate`; also accept a `Subscribable`. See C
|
|
|
178
178
|
|
|
179
179
|
### `.abide` component format (see `src/lib/ui/README.md`)
|
|
180
180
|
Valid HTML with `<script>` + native `<template>` control flow + scoped `<style>`.
|
|
181
|
-
- **Bindings:** `{expr}` text, `name={expr}` attr, `onclick={fn}`, `bind:value={…}` / `bind:checked` / `bind:group
|
|
181
|
+
- **Bindings:** `{expr}` text, `name={expr}` attr, `onclick={fn}`, `bind:value={…}` / `bind:checked` / `bind:group`, `attach={fn}` (node-lifetime attachment — the dual of `on`; the `use:`-action / `{@attach}` equivalent, lowered to `ui/dom/attach`).
|
|
182
182
|
- **Control flow (native `<template>`):** `if`/`else`, `each={list} as="x" key="x.id"`, `await={p}`/`then`/`catch`, `switch`/`case`/`default`.
|
|
183
183
|
- **Components:** capitalised tags (`<Layout title=…>`); children fill `<slot/>`; props are reactive (passed as thunks). `prop('name')` reads a typed page param.
|
|
184
|
+
- **Snippets / named slots:** `<template name="x" args={…}>` declares a reusable named builder (the `snippet()` form), rendered like a function — covers named slots / `{@render}`.
|
|
184
185
|
- **Reactivity:** write plain assignment (`count += 1`, `items.push(x)`); the compiler lowers it to patches. Deep-field edits wake only that field.
|
|
185
186
|
- **SSR:** byte-identical HTML string; `renderToStream` ships the shell then streams `<template await>` fragments out of order; `hydrate` adopts static structure in place (control-flow blocks + child components fall back to `mount`/re-render — known gap).
|
|
186
187
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# abide
|
|
2
2
|
|
|
3
|
+
## 0.30.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`311f3e7`](https://github.com/briancray/abide/commit/311f3e76732eb2b98d2739f2f7eff0fbbb667645) - cross-fade navigations via the View Transition API ([`d3ea6c0`](https://github.com/briancray/abide/commit/d3ea6c001c47748dd2be3ae47dfc3624ae7c65ee))
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- [`311f3e7`](https://github.com/briancray/abide/commit/311f3e76732eb2b98d2739f2f7eff0fbbb667645) - surface attach= binding and named-template snippets in .abide grammar ([`51a37ee`](https://github.com/briancray/abide/commit/51a37ee0b0cef708d0571de272ace3584d45a8c0))
|
|
12
|
+
|
|
13
|
+
- [`311f3e7`](https://github.com/briancray/abide/commit/311f3e76732eb2b98d2739f2f7eff0fbbb667645) - hoist component-local types above \_\_Props so prop annotations resolve ([`8d674c5`](https://github.com/briancray/abide/commit/8d674c543be53ae5775124f392ee44fd8731204f))
|
|
14
|
+
|
|
3
15
|
## 0.29.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/package.json
CHANGED
|
@@ -46,11 +46,18 @@ export function compileShadow(source: string): CompiledShadow {
|
|
|
46
46
|
const scriptStart = leadingScript ? source.indexOf('>', leadingScript.index) + 1 : 0
|
|
47
47
|
const templateStart = leadingScript ? (leadingScript.index ?? 0) + leadingScript[0].length : 0
|
|
48
48
|
|
|
49
|
-
const { imports, scope, props } = analyzeScript(scriptBody, scriptStart)
|
|
49
|
+
const { imports, types, scope, props } = analyzeScript(scriptBody, scriptStart)
|
|
50
50
|
builder.raw(SHADOW_PREAMBLE)
|
|
51
51
|
for (const line of imports) {
|
|
52
52
|
builder.flush(line)
|
|
53
53
|
}
|
|
54
|
+
/* Component-local `type`/`interface` declarations are hoisted to module scope —
|
|
55
|
+
above `__Props` so prop annotations referencing them resolve, and still visible
|
|
56
|
+
inside the function body where the rest of the scope and template expressions use
|
|
57
|
+
them. (Emitting them as in-function scope lines would hide them from `__Props`.) */
|
|
58
|
+
for (const line of types) {
|
|
59
|
+
builder.flush(line)
|
|
60
|
+
}
|
|
54
61
|
builder.raw(`interface __Props {\n${props.join('\n')}\n}\n`)
|
|
55
62
|
/* async so `await` blocks are legal; never executed, so the return is void. */
|
|
56
63
|
builder.raw('export default async function (props: __Props): Promise<void> {\n')
|
|
@@ -117,17 +124,24 @@ function createBuilder(): Builder {
|
|
|
117
124
|
return builder
|
|
118
125
|
}
|
|
119
126
|
|
|
120
|
-
type ScriptAnalysis = {
|
|
127
|
+
type ScriptAnalysis = {
|
|
128
|
+
imports: ScopeLine[]
|
|
129
|
+
types: ScopeLine[]
|
|
130
|
+
scope: ScopeLine[]
|
|
131
|
+
props: string[]
|
|
132
|
+
}
|
|
121
133
|
|
|
122
134
|
/* Walks the leading `<script>` and produces the shadow's module imports, the
|
|
123
|
-
value-typed scope lines, and the Props
|
|
124
|
-
body's absolute offset in the source, so
|
|
135
|
+
module-scope type declarations, the value-typed scope lines, and the Props
|
|
136
|
+
interface fields. `scriptStart` is the body's absolute offset in the source, so
|
|
137
|
+
verbatim spans map back exactly. */
|
|
125
138
|
function analyzeScript(scriptBody: string, scriptStart: number): ScriptAnalysis {
|
|
126
139
|
const imports: ScopeLine[] = []
|
|
140
|
+
const types: ScopeLine[] = []
|
|
127
141
|
const scope: ScopeLine[] = []
|
|
128
142
|
const props: string[] = []
|
|
129
143
|
if (scriptBody.trim() === '') {
|
|
130
|
-
return { imports, scope, props }
|
|
144
|
+
return { imports, types, scope, props }
|
|
131
145
|
}
|
|
132
146
|
const file = ts.createSourceFile('script.ts', scriptBody, ts.ScriptTarget.Latest, true)
|
|
133
147
|
/* A verbatim span: original text + the segment mapping it back, relative to the
|
|
@@ -145,6 +159,11 @@ function analyzeScript(scriptBody: string, scriptStart: number): ScriptAnalysis
|
|
|
145
159
|
imports.push({ text: verbatim(statement), segments: [span(statement, 0)] })
|
|
146
160
|
continue
|
|
147
161
|
}
|
|
162
|
+
if (ts.isTypeAliasDeclaration(statement) || ts.isInterfaceDeclaration(statement)) {
|
|
163
|
+
/* Hoist to module scope (verbatim, mapped) so prop annotations resolve them. */
|
|
164
|
+
types.push({ text: verbatim(statement), segments: [span(statement, 0)] })
|
|
165
|
+
continue
|
|
166
|
+
}
|
|
148
167
|
const reactive = reactiveDeclarations(statement)
|
|
149
168
|
if (reactive === undefined) {
|
|
150
169
|
/* Plain statement (function, const, expression) — emit verbatim, mapped. */
|
|
@@ -155,7 +174,7 @@ function analyzeScript(scriptBody: string, scriptStart: number): ScriptAnalysis
|
|
|
155
174
|
scope.push(scopeLineFor(declaration, props, verbatim, span))
|
|
156
175
|
}
|
|
157
176
|
}
|
|
158
|
-
return { imports, scope, props }
|
|
177
|
+
return { imports, types, scope, props }
|
|
159
178
|
}
|
|
160
179
|
|
|
161
180
|
/* The `state`/`derived`/`prop` declarations in a variable statement, or undefined
|
package/src/lib/ui/router.ts
CHANGED
|
@@ -264,12 +264,29 @@ export function router(
|
|
|
264
264
|
const hydrating =
|
|
265
265
|
first && pageView?.hydratable === true && pageView.hydrate !== undefined
|
|
266
266
|
first = false
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
base
|
|
267
|
+
/* The DOM mutation a navigation makes: tear the divergent chain down,
|
|
268
|
+
clear its DOM (a fresh mount; hydration adopts in place), rebuild. */
|
|
269
|
+
const swap = (): void => {
|
|
270
|
+
const base = disposeFrom(divergence)
|
|
271
|
+
if (!hydrating) {
|
|
272
|
+
base.textContent = ''
|
|
273
|
+
}
|
|
274
|
+
buildFrom(base, divergence, chainKeys, layoutViews, pageView, params, hydrating)
|
|
275
|
+
}
|
|
276
|
+
/* Wrap the swap in a View Transition where the browser supports it, so
|
|
277
|
+
the page change cross-fades (and shared `view-transition-name` elements
|
|
278
|
+
morph) — the synchronous swap is exactly the mutation the API snapshots
|
|
279
|
+
around. Skipped while hydrating: the first paint adopts SSR DOM in place,
|
|
280
|
+
not animate. CSS owns opting out (e.g. prefers-reduced-motion). */
|
|
281
|
+
if (
|
|
282
|
+
!hydrating &&
|
|
283
|
+
typeof document !== 'undefined' &&
|
|
284
|
+
'startViewTransition' in document
|
|
285
|
+
) {
|
|
286
|
+
document.startViewTransition(swap)
|
|
287
|
+
} else {
|
|
288
|
+
swap()
|
|
271
289
|
}
|
|
272
|
-
buildFrom(base, divergence, chainKeys, layoutViews, pageView, params, hydrating)
|
|
273
290
|
})
|
|
274
291
|
})
|
|
275
292
|
})
|