@vanijs/vani 0.1.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/DOCS.md ADDED
@@ -0,0 +1,373 @@
1
+ # Vani Documentation
2
+
3
+ Vani is a small, dependency‑free UI runtime built around a simple idea:
4
+
5
+ > Rendering should be explicit, local, and predictable.
6
+
7
+ Vani is **not** a Virtual DOM, not reactive‑by‑default, and not compiler‑driven. Components own a
8
+ DOM subtree delimited by anchors and only update when you explicitly ask them to.
9
+
10
+ ---
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pnpm add vani
16
+ ```
17
+
18
+ ---
19
+
20
+ ## Quick Start (SPA)
21
+
22
+ ```ts
23
+ import { component, div, button, renderToDOM, type Handle } from '@vanijs/vani'
24
+
25
+ const Counter = component((_, handle: Handle) => {
26
+ let count = 0
27
+ return () =>
28
+ div(
29
+ `Count: ${count}`,
30
+ button(
31
+ {
32
+ onclick: () => {
33
+ count += 1
34
+ handle.update()
35
+ },
36
+ },
37
+ 'Inc',
38
+ ),
39
+ )
40
+ })
41
+
42
+ const appRoot = document.getElementById('app')
43
+ if (!appRoot) throw new Error('#app not found')
44
+
45
+ renderToDOM([Counter()], appRoot)
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Core Concepts
51
+
52
+ ### 1) Components are functions
53
+
54
+ Components are functions that return a render function:
55
+
56
+ ```ts
57
+ import { component, div } from '@vanijs/vani'
58
+
59
+ const Hello = component(() => {
60
+ return () => div('Hello Vani')
61
+ })
62
+ ```
63
+
64
+ ### 2) Explicit updates
65
+
66
+ Nothing re‑renders unless you call `handle.update()`:
67
+
68
+ ```ts
69
+ import { component, div, button, type Handle } from '@vanijs/vani'
70
+
71
+ const Clicker = component((_, handle: Handle) => {
72
+ let clicks = 0
73
+ return () =>
74
+ div(
75
+ `Clicks: ${clicks}`,
76
+ button(
77
+ {
78
+ onclick: () => {
79
+ clicks += 1
80
+ handle.update()
81
+ },
82
+ },
83
+ 'Click',
84
+ ),
85
+ )
86
+ })
87
+ ```
88
+
89
+ ### 3) Subtree ownership (anchors)
90
+
91
+ Each component owns a DOM range delimited by anchors:
92
+
93
+ ```html
94
+ <!--vani:start-->
95
+ ... subtree ...
96
+ <!--vani:end-->
97
+ ```
98
+
99
+ Updates replace only the DOM between anchors.
100
+
101
+ ---
102
+
103
+ ## API Reference (with examples)
104
+
105
+ ### `component(fn)`
106
+
107
+ Creates a component factory. The `fn` receives `props` and a `handle`.
108
+
109
+ ```ts
110
+ import { component, div, type Handle } from '@vanijs/vani'
111
+
112
+ const Card = component<{ title: string }>((props, handle: Handle) => {
113
+ handle.effect(() => {
114
+ console.log('Mounted:', props.title)
115
+ })
116
+
117
+ return () => div(props.title)
118
+ })
119
+ ```
120
+
121
+ Components can return other component instances directly:
122
+
123
+ ```ts
124
+ import { component } from '@vanijs/vani'
125
+ import * as h from 'vani/html'
126
+
127
+ const Hero = component(() => {
128
+ return () => h.h1('Hello')
129
+ })
130
+
131
+ const Page = component(() => {
132
+ return () => Hero()
133
+ })
134
+ ```
135
+
136
+ ### `renderToDOM(components, root)`
137
+
138
+ Mounts components to the DOM immediately.
139
+
140
+ ```ts
141
+ import { renderToDOM, component, div } from '@vanijs/vani'
142
+
143
+ const App = component(() => () => div('App'))
144
+ renderToDOM([App()], document.getElementById('app')!)
145
+ ```
146
+
147
+ ### `hydrateToDOM(components, root)`
148
+
149
+ Binds handles to existing DOM (SSR/SSG) without rendering. You must call `handle.update()` to
150
+ activate.
151
+
152
+ ```ts
153
+ import { hydrateToDOM } from '@vanijs/vani'
154
+ import { App } from './app'
155
+
156
+ const root = document.getElementById('app')!
157
+ const handles = hydrateToDOM([App()], root)
158
+ handles.forEach((handle) => handle.update())
159
+ ```
160
+
161
+ ### `renderToString(components)`
162
+
163
+ Server‑side render to HTML with anchors. Import from `vani/ssr`.
164
+
165
+ ```ts
166
+ import { component } from '@vanijs/vani'
167
+ import { renderToString } from 'vani/ssr'
168
+
169
+ const App = component(() => () => 'Hello SSR')
170
+ const html = await renderToString([App()])
171
+ ```
172
+
173
+ ### `mount(component, props)`
174
+
175
+ Low‑level helper for embedding raw component functions in the middle of a render tree. Use it when
176
+ you have an unwrapped component function.
177
+
178
+ ```ts
179
+ import { component, mount, div, type Component } from '@vanijs/vani'
180
+
181
+ const Footer: Component = () => () => div('Footer')
182
+
183
+ const Page = component(() => () => div('Body', mount(Footer, {})))
184
+ ```
185
+
186
+ ### `fragment(...children)`
187
+
188
+ Returns a fragment node. Useful for returning multiple siblings.
189
+
190
+ ```ts
191
+ import { fragment, div, component } from '@vanijs/vani'
192
+
193
+ const App = component(() => () => fragment(div('One'), div('Two')))
194
+ ```
195
+
196
+ ### Element helpers
197
+
198
+ Vani ships helpers for common elements:
199
+
200
+ ```ts
201
+ import { div, span, button, input } from '@vanijs/vani'
202
+
203
+ div(span('Label'), input({ type: 'text' }), button({ onclick: () => {} }, 'Submit'))
204
+ ```
205
+
206
+ ### `classNames(...classes)`
207
+
208
+ Utility for composing class names:
209
+
210
+ ```ts
211
+ import { classNames, div } from '@vanijs/vani'
212
+
213
+ div({
214
+ className: classNames('base', { active: true }, ['p-2', 'rounded']),
215
+ })
216
+ ```
217
+
218
+ ### Refs
219
+
220
+ DOM refs and component refs are supported:
221
+
222
+ ```ts
223
+ import { component, input, type DomRef, type ComponentRef } from '@vanijs/vani'
224
+
225
+ const Child = component((_, handle) => () => input({ ref: { current: null } }))
226
+
227
+ const Parent = component(() => {
228
+ const childRef: ComponentRef = { current: null }
229
+ return () => Child({ ref: childRef })
230
+ })
231
+ ```
232
+
233
+ ### Cleanup and effects
234
+
235
+ Effects are explicit and can return a cleanup function.
236
+
237
+ If you plan to use vani for a SSR/SSG application, you should use effects to run client-only code
238
+ such as accessing the window object, accessing the DOM, etc.
239
+
240
+ Effects are very simple, they don't have dependencies and are run once on mount and once on update.
241
+
242
+ ```ts
243
+ import { component, div } from '@vanijs/vani'
244
+
245
+ const Timer = component((_, handle) => {
246
+ handle.effect(() => {
247
+ const id = setInterval(() => console.log('tick'), 1000)
248
+ return () => clearInterval(id)
249
+ })
250
+ return () => div('Timer running…')
251
+ })
252
+ ```
253
+
254
+ ### Transitions
255
+
256
+ `startTransition` marks a group of updates as non-urgent, so they are deferred and batched
257
+ separately.
258
+
259
+ This is useful to avoid blocking the UI while expensive work happens, e.g. if you are filtering a
260
+ large list.
261
+
262
+ ```ts
263
+ import { component, button, div, startTransition, type Handle } from '@vanijs/vani'
264
+
265
+ const List = component((_, handle: Handle) => {
266
+ let items = [1, 2, 3]
267
+ return () =>
268
+ div(
269
+ button(
270
+ {
271
+ onclick: () => {
272
+ startTransition(() => {
273
+ items = items.slice().reverse()
274
+ handle.update()
275
+ })
276
+ },
277
+ },
278
+ 'Reverse',
279
+ ),
280
+ )
281
+ })
282
+ ```
283
+
284
+ ---
285
+
286
+ ## Async Components
287
+
288
+ Components may return a Promise of a render function. You can provide a `fallback`:
289
+
290
+ ```ts
291
+ import { component, div } from '@vanijs/vani'
292
+
293
+ const AsyncCard = component(async () => {
294
+ await new Promise((resolve) => setTimeout(resolve, 500))
295
+ return () => div('Loaded')
296
+ })
297
+
298
+ const App = component(
299
+ () => () =>
300
+ AsyncCard({
301
+ fallback: () => div('Loading…'),
302
+ }),
303
+ )
304
+ ```
305
+
306
+ They are awaited and the fallback is rendered until the component is ready.
307
+
308
+ ---
309
+
310
+ ## clientOnly Islands
311
+
312
+ Use `clientOnly: true` to skip SSR of a component and render it only on the client:
313
+
314
+ ```ts
315
+ import { component, div } from '@vanijs/vani'
316
+
317
+ const ClientWidget = component(() => () => div('Client‑only'))
318
+
319
+ const App = component(
320
+ () => () =>
321
+ ClientWidget({
322
+ clientOnly: true,
323
+ }),
324
+ )
325
+ ```
326
+
327
+ ---
328
+
329
+ ## SSR, SSG, and SPA
330
+
331
+ Vani supports all three patterns:
332
+
333
+ ### SPA
334
+
335
+ Use `renderToDOM()` to mount immediately on the client.
336
+
337
+ ### SSR
338
+
339
+ Use `renderToString()` on the server, then `hydrateToDOM()` on the client.
340
+
341
+ ### SSG
342
+
343
+ Use `renderToString()` at build time to generate a static `index.html`, then hydrate on the client.
344
+
345
+ **Important:** Hydration only binds to anchors. It does not render or start effects. Call
346
+ `handle.update()` to activate the UI.
347
+
348
+ ---
349
+
350
+ ## Hydration Warnings
351
+
352
+ In dev mode, Vani warns about structural mismatches only:
353
+
354
+ - Missing `<!--vani:start-->` or `<!--vani:end-->`
355
+ - Anchor order mismatch (end before start)
356
+ - Unused anchors after hydration
357
+
358
+ This avoids slow DOM diffing and keeps behavior explicit.
359
+
360
+ ---
361
+
362
+ ## Philosophy Summary
363
+
364
+ - Explicit updates only (`handle.update()`)
365
+ - Localized DOM updates (subtree‑only)
366
+ - No virtual DOM, no diffing, no magic
367
+ - Hydration is binding, not execution
368
+
369
+ ---
370
+
371
+ ## License
372
+
373
+ MIT
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Javier Aguilar (itsjavi.com)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,268 @@
1
+ # Vani
2
+
3
+ **Vani** is a small, dependency-free UI runtime built around a simple idea:
4
+
5
+ > **Rendering should be explicit, local, and predictable.**
6
+
7
+ Vani is not a Virtual DOM, not reactive-by-default, and not compiler-driven.
8
+ It is a **runtime-first**, **DOM-anchored**, **subtree-based** rendering system designed for
9
+ clarity, performance, and long-term maintainability.
10
+
11
+ Docs: [DOCS.md](https://github.com/itsjavi/vani/blob/main/DOCS.md)
12
+
13
+ ---
14
+
15
+ ## Philosophy
16
+
17
+ ### 1. Explicit intent over implicit magic
18
+
19
+ In Vani, **nothing re-renders unless you explicitly ask for it**.
20
+
21
+ ```ts
22
+ handle.update()
23
+ ```
24
+
25
+ That single call is the only thing that triggers a re-render.
26
+
27
+ There are:
28
+
29
+ - no dependency graphs
30
+ - no signals auto-tracking
31
+ - no state subscriptions
32
+ - no implicit invalidation
33
+
34
+ This makes performance characteristics **obvious and predictable**.
35
+
36
+ ---
37
+
38
+ ### 2. Subtree ownership, not tree reconciliation
39
+
40
+ Each component owns **exactly one DOM range**, delimited by stable anchors:
41
+
42
+ ```html
43
+ <!--vani:start-->
44
+ subtree
45
+ <!--vani:end-->
46
+ ```
47
+
48
+ Updates replace **only that subtree**, never parents or siblings.
49
+
50
+ This guarantees:
51
+
52
+ - leaf-only updates
53
+ - O(size of subtree) cost
54
+ - no accidental cascades
55
+
56
+ ---
57
+
58
+ ### 3. Runtime-first, not compiler-first
59
+
60
+ Vani requires:
61
+
62
+ - no JSX
63
+ - no compiler
64
+ - no build-time transforms
65
+ - no generated code
66
+
67
+ Components are plain TypeScript functions.
68
+
69
+ This keeps:
70
+
71
+ - debugging straightforward
72
+ - stack traces readable
73
+ - behavior inspectable at runtime
74
+
75
+ ---
76
+
77
+ ### 4. Hydration is binding, not execution
78
+
79
+ During SSR hydration:
80
+
81
+ - **no setup runs**
82
+ - **no effects start**
83
+ - **no DOM is mutated**
84
+
85
+ Hydration only **binds runtime handles to existing DOM**. Actual execution happens on the first
86
+ explicit update.
87
+
88
+ This avoids:
89
+
90
+ - double subscriptions
91
+ - zombie timers
92
+ - mismatched server/client behavior
93
+
94
+ ---
95
+
96
+ ### 5. Scheduling, not reactivity, handles performance
97
+
98
+ Performance is handled by:
99
+
100
+ - batching
101
+ - microtask queues
102
+ - transition scheduling
103
+
104
+ Not by reactive graphs or heuristics.
105
+
106
+ ```ts
107
+ startTransition(() => {
108
+ handle.update()
109
+ })
110
+ ```
111
+
112
+ This separates:
113
+
114
+ - **what** updates
115
+ - from **when** updates happen
116
+
117
+ ---
118
+
119
+ ## Core Design Principles
120
+
121
+ | Principle | Meaning |
122
+ | ----------------- | ----------------------------------------- |
123
+ | Explicit updates | Only `handle.update()` triggers rendering |
124
+ | Locality | Updates affect only the owning subtree |
125
+ | Determinism | Same inputs → same DOM mutations |
126
+ | No hidden work | No background tracking or diffing |
127
+ | Runtime clarity | Debuggable without tooling |
128
+ | Opt-in complexity | Advanced features are explicit |
129
+
130
+ ---
131
+
132
+ ## Goals
133
+
134
+ - Predictable performance at scale
135
+ - Leaf-only updates by default
136
+ - Zero runtime dependencies
137
+ - SSR without hydration heuristics
138
+ - Clear mental model for developers
139
+ - Long-term maintainability over convenience
140
+ - Web-standards-first
141
+ - ESM-first and designed to run in any modern environment
142
+ - Good and intuitive developer experience
143
+ - Reduce magic and complexity, give freedom back to the developers
144
+
145
+ ---
146
+
147
+ ## Ergonomic features
148
+
149
+ - Vani uses real HTML attribute names, with a small set of library-specific exceptions (`ref`,
150
+ `key`, `fallback`, `clientOnly`)
151
+ - Async components are supported, including fallbacks
152
+ - `className` accepts string, array, and object forms for ergonomic composition
153
+ - ESM-first and designed to run in any modern environment
154
+
155
+ ---
156
+
157
+ ## SSR (experimental)
158
+
159
+ Vani SSR is explicit and anchor-based. You call `renderToString`, and the output includes the same
160
+ `<!--vani:start-->` / `<!--vani:end-->` anchors used by the client runtime.
161
+
162
+ ```ts
163
+ import { renderToString } from './vani/ssr'
164
+ import { hydrateToDOM } from './vani/runtime'
165
+ import { getHydrationChildren } from './demos/hydration-app'
166
+
167
+ const html = await renderToString(getHydrationChildren())
168
+ // inject `html` into <div id="app"> on the server
169
+
170
+ // On the client
171
+ const root = document.getElementById('app')
172
+ const handles = hydrateToDOM(getHydrationChildren(), root!)
173
+ handles.forEach((handle) => handle.update())
174
+ ```
175
+
176
+ Notes:
177
+
178
+ - SSR does not use a virtual DOM or heuristics.
179
+ - Hydration binds to anchors only; it does not render.
180
+ - If SSR output does not match the client component tree, Vani warns (dev only).
181
+
182
+ ---
183
+
184
+ ## What Vani is NOT
185
+
186
+ - ❌ Not a Virtual DOM
187
+ - ❌ Not reactive-by-default
188
+ - ❌ Not JSX-based
189
+ - ❌ Not compiler-driven
190
+ - ❌ Not a template language
191
+ - ❌ Not a framework that guesses intent
192
+
193
+ ---
194
+
195
+ ## Comparison with Popular Frameworks
196
+
197
+ | Feature / Framework | Vani | React | Vue | Svelte | Solid |
198
+ | ---------------------- | ---- | ----- | --- | ------ | ----- |
199
+ | Virtual DOM | ❌ | ✅ | ✅ | ❌ | ❌ |
200
+ | Implicit reactivity | ❌ | ⚠️ | ✅ | ✅ | ✅ |
201
+ | Compiler required | ❌ | ❌ | ❌ | ✅ | ❌ |
202
+ | JSX required | ❌ | ✅ | ❌ | ❌ | ❌ |
203
+ | Explicit updates | ✅ | ❌ | ❌ | ❌ | ❌ |
204
+ | Leaf-only updates | ✅ | ❌ | ❌ | ❌ | ❌ |
205
+ | Runtime-only | ✅ | ⚠️ | ⚠️ | ❌ | ⚠️ |
206
+ | SSR without heuristics | ✅ | ❌ | ❌ | ❌ | ❌ |
207
+ | Dependency-free core | ✅ | ❌ | ❌ | ❌ | ❌ |
208
+
209
+ ⚠️ = partially / indirectly supported
210
+
211
+ ---
212
+
213
+ ## Mental Model
214
+
215
+ Think of Vani as:
216
+
217
+ > **“Manually invalidated, DOM-owned UI subtrees.”**
218
+
219
+ You decide:
220
+
221
+ - when something updates
222
+ - how much updates
223
+ - and why it updates
224
+
225
+ Nothing else happens behind your back.
226
+
227
+ ---
228
+
229
+ ## Who Vani is for
230
+
231
+ Vani is a good fit if you value:
232
+
233
+ - full control over rendering
234
+ - predictable performance
235
+ - small runtimes
236
+ - explicit data flow
237
+ - SSR without complexity
238
+ - understanding your tools deeply
239
+
240
+ It is **not** optimized for:
241
+
242
+ - rapid prototyping
243
+ - beginners
244
+ - implicit magic
245
+ - large teams that rely on conventions
246
+
247
+ ---
248
+
249
+ ## Status
250
+
251
+ Vani is experimental and evolving. The core architecture is intentionally small and stable.
252
+
253
+ Expect:
254
+
255
+ - iteration
256
+ - refinement
257
+ - careful additions
258
+
259
+ Not:
260
+
261
+ - rapid feature creep
262
+ - breaking conceptual changes
263
+
264
+ ---
265
+
266
+ ## License
267
+
268
+ MIT
@@ -0,0 +1,240 @@
1
+ //#region src/vani/runtime.d.ts
2
+ type SSRNode = {
3
+ type: 'element';
4
+ tag: string;
5
+ props: Record<string, any>;
6
+ children: SSRNode[];
7
+ } | {
8
+ type: 'text';
9
+ text: string;
10
+ } | {
11
+ type: 'comment';
12
+ text: string;
13
+ } | {
14
+ type: 'fragment';
15
+ children: SSRNode[];
16
+ } | {
17
+ type: 'component';
18
+ instance: ComponentInstance<any>;
19
+ };
20
+ type VNode = Node | SSRNode;
21
+ interface Handle {
22
+ /**
23
+ * Schedules a render for the component.
24
+ * This triggers a re-render on the next microtask.
25
+ */
26
+ update(): void;
27
+ /**
28
+ * Flushes the component render.
29
+ * This triggers a re-render immediately.
30
+ */
31
+ updateSync(): void;
32
+ /**
33
+ * Disposes the component: removes the component from the DOM and runs all cleanup functions.
34
+ */
35
+ dispose(): void;
36
+ /**
37
+ * Adds a cleanup function that is called when the component is disposed.
38
+ */
39
+ onCleanup(fn: () => void): void;
40
+ /**
41
+ * This is purely syntatic sugar, as it is basically the same as running the function
42
+ * on the setup phase and calling onCleanup to add a cleanup function.
43
+ *
44
+ * Using effects is necessary in SSR mode, for side effects to not run on the server
45
+ * (e.g. timers, subscriptions, DOM usage, etc.)
46
+ *
47
+ * Runs a side effect function when the component is mounted.
48
+ * The returning function may be a cleanup function that is called when the component is disposed.
49
+ *
50
+ */
51
+ effect(fn: () => void | (() => void)): void;
52
+ }
53
+ type RenderFn = () => VChild;
54
+ type Component<Props = any> = (props: Props, handle: Handle) => RenderFn | Promise<RenderFn>;
55
+ type ComponentInstance<Props = any> = {
56
+ $$vani: 'component';
57
+ component: Component<Props>;
58
+ props: Props;
59
+ /**
60
+ * A key is used to identify the component when it is re-rendered.
61
+ * If a key is provided, the component will be re-rendered only if the key changes.
62
+ */
63
+ key?: string | number;
64
+ /**
65
+ * A ref is used to get a reference to the component instance.
66
+ * The ref is set to the component instance when the component is mounted.
67
+ * The ref is set to null when the component is disposed.
68
+ */
69
+ ref?: ComponentRef;
70
+ clientOnly?: boolean;
71
+ };
72
+ type ComponentInput<Props> = Props & {
73
+ key?: string | number;
74
+ ref?: ComponentRef;
75
+ };
76
+ type ComponentMetaProps = {
77
+ key?: string | number;
78
+ ref?: ComponentRef;
79
+ fallback?: RenderFn;
80
+ clientOnly?: boolean;
81
+ };
82
+ type VChild = VNode | ComponentInstance<any> | string | number | null | undefined | false;
83
+ type DataAttribute = `data-${string}`;
84
+ type HtmlProps<T extends keyof HTMLElementTagNameMap> = Partial<Omit<HTMLElementTagNameMap[T], 'children' | 'className' | 'style'>> & {
85
+ className?: ClassName;
86
+ style?: string;
87
+ ref?: DomRef<HTMLElementTagNameMap[T]>;
88
+ } & { [key in DataAttribute]?: string | number | boolean | undefined | null };
89
+ type ClassName = string | undefined | null | {
90
+ [key: string]: boolean | undefined | null;
91
+ } | ClassName[];
92
+ type ComponentRef = {
93
+ current: Handle | null;
94
+ };
95
+ type DomRef<T extends HTMLElement = HTMLElement> = {
96
+ current: T | null;
97
+ };
98
+ type RenderMode = 'dom' | 'ssr';
99
+ declare function component(fn: Component<void>): (props?: ComponentMetaProps) => ComponentInstance<void>;
100
+ declare function component<Props>(fn: Component<Props>): (props: Props & ComponentMetaProps) => ComponentInstance<Props>;
101
+ declare function withRenderMode<T>(mode: RenderMode, fn: () => T): T;
102
+ declare function getRenderMode(): RenderMode;
103
+ declare function renderToDOM(components: Array<Component<any> | ComponentInstance<any>>, root: HTMLElement): Handle[];
104
+ declare function isComponentInstance(child: VChild): child is ComponentInstance<any>;
105
+ declare function classNames(...classes: ClassName[]): string;
106
+ declare function el<E extends keyof HTMLElementTagNameMap>(tag: E, props?: HtmlProps<E> | VChild | null, ...children: VChild[]): VNode;
107
+ declare const fragment: (...children: VChild[]) => {
108
+ type: "fragment";
109
+ children: SSRNode[];
110
+ } | DocumentFragment;
111
+ declare function mount<Props>(component: Component<Props>, props: Props): VNode;
112
+ /**
113
+ * Marks all updates triggered inside the callback as a "transition".
114
+ *
115
+ * A transition represents non-urgent UI work that can be deferred
116
+ * to keep the application responsive.
117
+ *
118
+ * Updates scheduled inside `startTransition`:
119
+ * - do NOT block user interactions
120
+ * - are batched separately from urgent updates
121
+ * - may be flushed later (e.g. after the current event or during idle time)
122
+ *
123
+ * Transitions are NOT animations.
124
+ * They do not control how updates look, only *when* they are applied.
125
+ *
126
+ * Typical use cases:
127
+ * - Filtering or sorting large lists
128
+ * - Rendering expensive subtrees
129
+ * - Applying async results that are not immediately visible
130
+ *
131
+ * Example:
132
+ * ```ts
133
+ * button({
134
+ * onclick: () => {
135
+ * // urgent update
136
+ * setOpen(true)
137
+ * handle.update()
138
+ *
139
+ * // non-urgent update
140
+ * startTransition(() => {
141
+ * setItems(filter(items))
142
+ * handle.update()
143
+ * })
144
+ * },
145
+ * })
146
+ * ```
147
+ *
148
+ * If multiple transitions are triggered, they are automatically batched.
149
+ * Transition updates never interrupt urgent updates.
150
+ */
151
+ declare function startTransition(fn: () => void): void;
152
+ declare function hydrateToDOM(components: Array<Component<any> | ComponentInstance<any>>, root: HTMLElement): Handle[];
153
+ declare function isDevMode(): boolean;
154
+ //#endregion
155
+ //#region src/vani/html.d.ts
156
+ declare const div: (propsOrChild?: VChild | HtmlProps<"div">, ...children: VChild[]) => VNode;
157
+ declare const span: (propsOrChild?: VChild | HtmlProps<"span">, ...children: VChild[]) => VNode;
158
+ declare const ul: (propsOrChild?: VChild | HtmlProps<"ul">, ...children: VChild[]) => VNode;
159
+ declare const li: (propsOrChild?: VChild | HtmlProps<"li">, ...children: VChild[]) => VNode;
160
+ declare const ol: (propsOrChild?: VChild | HtmlProps<"ol">, ...children: VChild[]) => VNode;
161
+ declare const dl: (propsOrChild?: VChild | HtmlProps<"dl">, ...children: VChild[]) => VNode;
162
+ declare const dt: (propsOrChild?: VChild | HtmlProps<"dt">, ...children: VChild[]) => VNode;
163
+ declare const dd: (propsOrChild?: VChild | HtmlProps<"dd">, ...children: VChild[]) => VNode;
164
+ declare const main: (propsOrChild?: VChild | HtmlProps<"main">, ...children: VChild[]) => VNode;
165
+ declare const header: (propsOrChild?: VChild | HtmlProps<"header">, ...children: VChild[]) => VNode;
166
+ declare const footer: (propsOrChild?: VChild | HtmlProps<"footer">, ...children: VChild[]) => VNode;
167
+ declare const section: (propsOrChild?: VChild | HtmlProps<"section">, ...children: VChild[]) => VNode;
168
+ declare const article: (propsOrChild?: VChild | HtmlProps<"article">, ...children: VChild[]) => VNode;
169
+ declare const aside: (propsOrChild?: VChild | HtmlProps<"aside">, ...children: VChild[]) => VNode;
170
+ declare const nav: (propsOrChild?: VChild | HtmlProps<"nav">, ...children: VChild[]) => VNode;
171
+ declare const details: (propsOrChild?: VChild | HtmlProps<"details">, ...children: VChild[]) => VNode;
172
+ declare const summary: (propsOrChild?: VChild | HtmlProps<"summary">, ...children: VChild[]) => VNode;
173
+ declare const a: (propsOrChild?: VChild | HtmlProps<"a">, ...children: VChild[]) => VNode;
174
+ declare const button: (propsOrChild?: VChild | HtmlProps<"button">, ...children: VChild[]) => VNode;
175
+ declare const input: (propsOrChild?: VChild | HtmlProps<"input">, ...children: VChild[]) => VNode;
176
+ declare const output: (propsOrChild?: VChild | HtmlProps<"output">, ...children: VChild[]) => VNode;
177
+ declare const textarea: (propsOrChild?: VChild | HtmlProps<"textarea">, ...children: VChild[]) => VNode;
178
+ declare const select: (propsOrChild?: VChild | HtmlProps<"select">, ...children: VChild[]) => VNode;
179
+ declare const option: (propsOrChild?: VChild | HtmlProps<"option">, ...children: VChild[]) => VNode;
180
+ declare const optgroup: (propsOrChild?: VChild | HtmlProps<"optgroup">, ...children: VChild[]) => VNode;
181
+ declare const label: (propsOrChild?: VChild | HtmlProps<"label">, ...children: VChild[]) => VNode;
182
+ declare const form: (propsOrChild?: VChild | HtmlProps<"form">, ...children: VChild[]) => VNode;
183
+ declare const progress: (propsOrChild?: VChild | HtmlProps<"progress">, ...children: VChild[]) => VNode;
184
+ declare const meter: (propsOrChild?: VChild | HtmlProps<"meter">, ...children: VChild[]) => VNode;
185
+ declare const fieldset: (propsOrChild?: VChild | HtmlProps<"fieldset">, ...children: VChild[]) => VNode;
186
+ declare const legend: (propsOrChild?: VChild | HtmlProps<"legend">, ...children: VChild[]) => VNode;
187
+ declare const datalist: (propsOrChild?: VChild | HtmlProps<"datalist">, ...children: VChild[]) => VNode;
188
+ declare const figure: (propsOrChild?: VChild | HtmlProps<"figure">, ...children: VChild[]) => VNode;
189
+ declare const figcaption: (propsOrChild?: VChild | HtmlProps<"figcaption">, ...children: VChild[]) => VNode;
190
+ declare const img: (propsOrChild?: VChild | HtmlProps<"img">, ...children: VChild[]) => VNode;
191
+ declare const picture: (propsOrChild?: VChild | HtmlProps<"picture">, ...children: VChild[]) => VNode;
192
+ declare const source: (propsOrChild?: VChild | HtmlProps<"source">, ...children: VChild[]) => VNode;
193
+ declare const video: (propsOrChild?: VChild | HtmlProps<"video">, ...children: VChild[]) => VNode;
194
+ declare const audio: (propsOrChild?: VChild | HtmlProps<"audio">, ...children: VChild[]) => VNode;
195
+ declare const iframe: (propsOrChild?: VChild | HtmlProps<"iframe">, ...children: VChild[]) => VNode;
196
+ declare const embed: (propsOrChild?: VChild | HtmlProps<"embed">, ...children: VChild[]) => VNode;
197
+ declare const time: (propsOrChild?: VChild | HtmlProps<"time">, ...children: VChild[]) => VNode;
198
+ declare const mark: (propsOrChild?: VChild | HtmlProps<"mark">, ...children: VChild[]) => VNode;
199
+ declare const p: (propsOrChild?: VChild | HtmlProps<"p">, ...children: VChild[]) => VNode;
200
+ declare const h1: (propsOrChild?: VChild | HtmlProps<"h1">, ...children: VChild[]) => VNode;
201
+ declare const h2: (propsOrChild?: VChild | HtmlProps<"h2">, ...children: VChild[]) => VNode;
202
+ declare const h3: (propsOrChild?: VChild | HtmlProps<"h3">, ...children: VChild[]) => VNode;
203
+ declare const h4: (propsOrChild?: VChild | HtmlProps<"h4">, ...children: VChild[]) => VNode;
204
+ declare const h5: (propsOrChild?: VChild | HtmlProps<"h5">, ...children: VChild[]) => VNode;
205
+ declare const h6: (propsOrChild?: VChild | HtmlProps<"h6">, ...children: VChild[]) => VNode;
206
+ declare const code: (propsOrChild?: VChild | HtmlProps<"code">, ...children: VChild[]) => VNode;
207
+ declare const pre: (propsOrChild?: VChild | HtmlProps<"pre">, ...children: VChild[]) => VNode;
208
+ declare const blockquote: (propsOrChild?: VChild | HtmlProps<"blockquote">, ...children: VChild[]) => VNode;
209
+ declare const var_: (propsOrChild?: VChild | HtmlProps<"var">, ...children: VChild[]) => VNode;
210
+ declare const kbd: (propsOrChild?: VChild | HtmlProps<"kbd">, ...children: VChild[]) => VNode;
211
+ declare const samp: (propsOrChild?: VChild | HtmlProps<"samp">, ...children: VChild[]) => VNode;
212
+ declare const cite: (propsOrChild?: VChild | HtmlProps<"cite">, ...children: VChild[]) => VNode;
213
+ declare const dfn: (propsOrChild?: VChild | HtmlProps<"dfn">, ...children: VChild[]) => VNode;
214
+ declare const abbr: (propsOrChild?: VChild | HtmlProps<"abbr">, ...children: VChild[]) => VNode;
215
+ declare const small: (propsOrChild?: VChild | HtmlProps<"small">, ...children: VChild[]) => VNode;
216
+ declare const strong: (propsOrChild?: VChild | HtmlProps<"strong">, ...children: VChild[]) => VNode;
217
+ declare const em: (propsOrChild?: VChild | HtmlProps<"em">, ...children: VChild[]) => VNode;
218
+ declare const br: (propsOrChild?: VChild | HtmlProps<"br">, ...children: VChild[]) => VNode;
219
+ declare const hr: (propsOrChild?: VChild | HtmlProps<"hr">, ...children: VChild[]) => VNode;
220
+ declare const table: (propsOrChild?: VChild | HtmlProps<"table">, ...children: VChild[]) => VNode;
221
+ declare const caption: (propsOrChild?: VChild | HtmlProps<"caption">, ...children: VChild[]) => VNode;
222
+ declare const colgroup: (propsOrChild?: VChild | HtmlProps<"colgroup">, ...children: VChild[]) => VNode;
223
+ declare const col: (propsOrChild?: VChild | HtmlProps<"col">, ...children: VChild[]) => VNode;
224
+ declare const tbody: (propsOrChild?: VChild | HtmlProps<"tbody">, ...children: VChild[]) => VNode;
225
+ declare const thead: (propsOrChild?: VChild | HtmlProps<"thead">, ...children: VChild[]) => VNode;
226
+ declare const tfoot: (propsOrChild?: VChild | HtmlProps<"tfoot">, ...children: VChild[]) => VNode;
227
+ declare const tr: (propsOrChild?: VChild | HtmlProps<"tr">, ...children: VChild[]) => VNode;
228
+ declare const td: (propsOrChild?: VChild | HtmlProps<"td">, ...children: VChild[]) => VNode;
229
+ declare const th: (propsOrChild?: VChild | HtmlProps<"th">, ...children: VChild[]) => VNode;
230
+ declare const style: (propsOrChild?: VChild | HtmlProps<"style">, ...children: VChild[]) => VNode;
231
+ declare const script: (propsOrChild?: VChild | HtmlProps<"script">, ...children: VChild[]) => VNode;
232
+ declare const noscript: (propsOrChild?: VChild | HtmlProps<"noscript">, ...children: VChild[]) => VNode;
233
+ declare const template: (propsOrChild?: VChild | HtmlProps<"template">, ...children: VChild[]) => VNode;
234
+ declare const slot: (propsOrChild?: VChild | HtmlProps<"slot">, ...children: VChild[]) => VNode;
235
+ //#endregion
236
+ //#region src/vani/ssr.d.ts
237
+ type Renderable = Component<any> | ComponentInstance<any>;
238
+ declare function renderToString(components: Renderable[]): Promise<string>;
239
+ //#endregion
240
+ export { ClassName, Component, ComponentInput, ComponentInstance, ComponentRef, DataAttribute, DomRef, Handle, HtmlProps, RenderFn, SSRNode, VChild, VNode, a, abbr, article, aside, audio, blockquote, br, button, caption, cite, classNames, code, col, colgroup, component, datalist, dd, details, dfn, div, dl, dt, el, em, embed, fieldset, figcaption, figure, footer, form, fragment, getRenderMode, h1, h2, h3, h4, h5, h6, header, hr, hydrateToDOM, iframe, img, input, isComponentInstance, isDevMode, kbd, label, legend, li, main, mark, meter, mount, nav, noscript, ol, optgroup, option, output, p, picture, pre, progress, renderToDOM, renderToString, samp, script, section, select, slot, small, source, span, startTransition, strong, style, summary, table, tbody, td, template, textarea, tfoot, th, thead, time, tr, ul, var_, video, withRenderMode };
@@ -0,0 +1 @@
1
+ function e(e){let t=e;return t.__vaniKeyed||=new Map,t.__vaniKeyed}function t(e){return t=>{let n,r,i=!1,a=t;if(t&&typeof t==`object`){let e=t;if(n=e.key,r=e.ref,i=e.clientOnly,`key`in e||`ref`in e){let{key:t,ref:n,clientOnly:r,...i}=e;a=i}}return{$$vani:`component`,component:e,props:a,key:n,ref:r,clientOnly:i}}}let n=`dom`;function r(e,t){let r=n;n=e;let i=t();return i&&typeof i.finally==`function`?i.finally(()=>{n=r}):(n=r,i)}function i(){return n}function a(e){return typeof e==`object`&&!!e&&`type`in e}function o(e){return a(e)&&e.type===`element`}function s(e){return a(e)&&e.type===`fragment`}function c(e){return n===`dom`?document.createElement(e):{type:`element`,tag:e,props:{},children:[]}}function l(e){return n===`dom`?document.createTextNode(e):{type:`text`,text:e}}function u(e,t){if(n===`dom`){e.appendChild(t);return}(o(e)||s(e))&&e.children.push(t)}function d(e,t){let n=e.nextSibling;for(;n&&n!==t;){let e=n.nextSibling,t=n;t.__vaniDomRef&&(t.__vaniDomRef.current=null),n.remove(),n=e}}function ee(e){if(e==null||e===!1)return document.createComment(`vani:empty`);if(m(e)){let t=document.createDocumentFragment(),n=f(e.component,h(e),t);return e.ref&&(e.ref.current=n),t}if(typeof e==`string`||typeof e==`number`)return document.createTextNode(String(e));if(e instanceof Node)return e;throw Error(`[vani] render returned an unsupported node type in DOM mode`)}function f(e,t,n){let r=[],i=!1,a,o,s=t?.clientOnly===!0;if(O){let e=A;A+=1,a=M(n,e),o=N(a,e)}else a=document.createComment(`vani:start`),o=document.createComment(`vani:end`),n.appendChild(a),n.appendChild(o);let c,l={update(){i||(S?w.has(l)||(T.add(l),D()):(T.delete(l),w.add(l),re()))},updateSync(){if(i)return;let e=a.parentNode;if(!e)return;d(a,o);let t=ee(c());e.insertBefore(t,o)},onCleanup(e){r.push(e)},dispose(){if(!i){i=!0,w.delete(l),T.delete(l);for(let e of r)e();r.length=0,d(a,o),a.remove(),o.remove(),c=(()=>document.createComment(`disposed`))}},effect(e){let t=e();typeof t==`function`&&r.push(t)}};if(O&&!s){let n=!1;return c=()=>{if(!n){n=!0;let r=e(t,l);c=r instanceof Promise?()=>document.createComment(`async`):r}return c()},l}let u=e(t,l);return u instanceof Promise?(c=t?.fallback||(()=>document.createComment(`vani:async`)),(!O||s)&&l.update(),u.then(e=>{i||(c=e,l.update())}),l):(c=u,(!O||s)&&l.update(),l)}function p(e,t){if(!t)throw Error(`[vani] root element not found`);let n=[];for(let r of e){if(typeof r==`function`){let e=f(r,{},t);n.push(e);continue}let e=f(r.component,h(r),t);n.push(e)}return n}function m(e){let t=typeof Node<`u`&&e instanceof Node;if(typeof e!=`object`||t)return!1;let n=e;return n.$$vani===`component`&&typeof n.component==`function`}function te(e){let t=typeof Node<`u`&&e instanceof Node;return typeof e==`object`&&!!e&&!t&&!m(e)}function h(e){return e.clientOnly?{...e.props??{},clientOnly:!0}:e.props}function g(t,r){if(n===`ssr`){for(let e of r)if(!(e==null||e===!1||e===void 0)){if(m(e)){u(t,{type:`component`,instance:e});continue}if(typeof e==`string`||typeof e==`number`){u(t,l(String(e)));continue}if(a(e)){u(t,e);continue}}return}let i=t;for(let t of r)if(!(t==null||t===!1||t===void 0)){if(m(t)){if(t.key!=null){let n=e(i),r=i.__vaniUsedKeys??=new Set,a=n.get(t.key);if(!a){let e=document.createDocumentFragment(),r=f(t.component,h(t),e);t.ref&&(t.ref.current=r),a={fragment:e,handle:r,ref:t.ref},n.set(t.key,a),t.ref&&(t.ref.current=r)}r.add(t.key),i.appendChild(a.fragment);continue}let n=document.createDocumentFragment(),r=f(t.component,h(t),n);t.ref&&(t.ref.current=r),i.appendChild(n);continue}if(typeof t==`string`||typeof t==`number`){i.appendChild(document.createTextNode(String(t)));continue}i.appendChild(t)}let o=i.__vaniKeyed,s=i.__vaniUsedKeys;if(o&&s){for(let[e,t]of o)s.has(e)||(t.handle.dispose(),t.ref&&(t.ref.current=null),o.delete(e));s.clear()}}function _(e,t){for(let n in t){let r=t[n];if(![`key`,`ref`].includes(n)){if(n===`className`){let t=v(r);o(e)?e.props.class=t:e.className=t;continue}if(n.startsWith(`on`)&&typeof r==`function`)o(e)||(e[n.toLowerCase()]=r);else if(r===!0)o(e)?e.props[n]=!0:e.setAttribute(n,``);else if(r===!1||r==null)continue;else o(e)?e.props[n]=String(r):e.setAttribute(n,String(r))}}}function v(...e){return e.map(e=>{if(!(e==null||e===``))return typeof e==`string`?e.trim():Array.isArray(e)?v(...e):Object.entries(e).filter(([e,t])=>t).map(([e])=>e.trim()).join(` `).trim()}).filter(Boolean).join(` `)}function y(e,t,...n){let r=c(e);return te(t)?(t.ref&&(o(r)?t.ref.current=null:(t.ref.current=r,r.__vaniDomRef=t.ref)),_(r,t),g(r,n),r):(g(r,[t,...n]),r)}const ne=(...e)=>{if(n===`ssr`){let t={type:`fragment`,children:[]};return g(t,e),t}let t=document.createDocumentFragment();return g(t,e),t};function b(e,t){if(n===`ssr`)return{type:`component`,instance:{$$vani:`component`,component:e,props:t}};let r=document.createDocumentFragment();return f(e,t,r),r}let x=!1,S=!1,C=!1;const w=new Set,T=new Set;function E(e){let t=S;S=!0;try{e()}finally{S=t,D()}}function D(){C||(C=!0,setTimeout(()=>{C=!1,ie()},0))}function re(){x||(x=!0,queueMicrotask(()=>{x=!1;for(let e of w)e.updateSync();w.clear()}))}function ie(){for(let e of T)e.updateSync();T.clear(),T.size>0&&D()}let O=!1,k=null,A=0;function j(e){F()&&console.warn(`[vani] hydration warning: ${e}`)}function M(e,t){let n=k;(!n||!e.contains(n))&&(n=e.firstChild);let r=!1;for(;n;){if(n.nodeType===Node.COMMENT_NODE&&n.nodeValue===`vani:start`)return r&&j(`Found <!--vani:end--> before <!--vani:start--> for component #${t}. This usually means the server HTML anchor order is incorrect.`),n;n.nodeType===Node.COMMENT_NODE&&n.nodeValue===`vani:end`&&(r=!0),n=n.nextSibling}throw j(`Expected <!--vani:start--> for component #${t}, but none was found. This usually means the server HTML does not match the client component tree.`),Error(`[vani] hydration failed: start anchor not found`)}function N(e,t){let n=e.nextSibling,r=0;for(;n;){if(n.nodeType===Node.COMMENT_NODE){if(n.nodeValue===`vani:start`)r+=1;else if(n.nodeValue===`vani:end`){if(r===0)return k=n.nextSibling,n;--r}}n=n.nextSibling}throw j(`Expected <!--vani:end--> for component #${t}, but none was found. This usually means the server HTML does not match the client component tree.`),Error(`[vani] hydration failed: end anchor not found`)}function P(e,t){let n=[];O=!0,k=t.firstChild,A=0;try{n=p(e,t)}catch(e){console.error(`[vani] hydration failed:`,e)}finally{if(F()&&k){let e=k,t=!1;for(;e;){if(e.nodeType===Node.COMMENT_NODE){let n=e.nodeValue;if(n===`vani:start`||n===`vani:end`){t=!0;break}}e=e.nextSibling}t&&j(`Unused SSR anchors detected after hydration. Some server-rendered DOM was not claimed by the client runtime.`)}O=!1,k=null,A=0}return n}function F(){return`__vaniDevMode`in globalThis?globalThis.__vaniDevMode===!0:import.meta.env?import.meta.env.DEV:typeof process<`u`&&process.env!==void 0?process.env.NODE_ENV===`development`:!1}function I(e){return(t,...n)=>y(e,t,...n)}const L=I(`div`),R=I(`span`),z=I(`ul`),B=I(`li`),V=I(`ol`),H=I(`dl`),U=I(`dt`),W=I(`dd`),G=I(`main`),K=I(`header`),q=I(`footer`),J=I(`section`),Y=I(`article`),ae=I(`aside`),oe=I(`nav`),se=I(`details`),ce=I(`summary`),le=I(`a`),ue=I(`button`),de=I(`input`),fe=I(`output`),pe=I(`textarea`),me=I(`select`),he=I(`option`),ge=I(`optgroup`),_e=I(`label`),ve=I(`form`),ye=I(`progress`),be=I(`meter`),xe=I(`fieldset`),Se=I(`legend`),Ce=I(`datalist`),we=I(`figure`),Te=I(`figcaption`),Ee=I(`img`),De=I(`picture`),Oe=I(`source`),ke=I(`video`),Ae=I(`audio`),je=I(`iframe`),Me=I(`embed`),Ne=I(`time`),Pe=I(`mark`),Fe=I(`p`),Ie=I(`h1`),Le=I(`h2`),Re=I(`h3`),ze=I(`h4`),Be=I(`h5`),Ve=I(`h6`),He=I(`code`),Ue=I(`pre`),We=I(`blockquote`),Ge=I(`var`),Ke=I(`kbd`),qe=I(`samp`),Je=I(`cite`),Ye=I(`dfn`),Xe=I(`abbr`),Ze=I(`small`),Qe=I(`strong`),$e=I(`em`),et=I(`br`),tt=I(`hr`),nt=I(`table`),rt=I(`caption`),it=I(`colgroup`),at=I(`col`),ot=I(`tbody`),st=I(`thead`),ct=I(`tfoot`),lt=I(`tr`),ut=I(`td`),dt=I(`th`),ft=I(`style`),pt=I(`script`),mt=I(`noscript`),ht=I(`template`),X=I(`slot`),gt=new Set([`area`,`base`,`br`,`col`,`embed`,`hr`,`img`,`input`,`link`,`meta`,`param`,`source`,`track`,`wbr`]);function _t(){return{update(){},updateSync(){},dispose(){},onCleanup(){},effect(){}}}function vt(e){return typeof e==`function`?{$$vani:`component`,component:e,props:{}}:e}function Z(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`).replace(/'/g,`&#39;`)}function yt(e){let t=[];for(let n of Object.keys(e)){let r=e[n];r==null||r===!1||n.startsWith(`on`)&&typeof r==`function`||(r===!0?t.push(n):t.push(`${n}="${Z(String(r))}"`))}return t.length>0?` ${t.join(` `)}`:``}function Q(e){if(e==null||e===!1)return{type:`fragment`,children:[]};if(typeof e==`string`||typeof e==`number`)return{type:`text`,text:String(e)};if(m(e))return{type:`component`,instance:e};if(typeof e==`object`&&`type`in e)return e;throw Error(`[vani] SSR received a DOM node. This is not supported.`)}async function bt(e){let t=`<!--vani:start-->`,n=`<!--vani:end-->`;if(e.clientOnly){let r=e.props?.fallback;return r?`${t}${await $(Q(r()))}${n}`:`${t}${n}`}let r=e.component(e.props,_t());return`${t}${await $(Q((r instanceof Promise?await r:r)()))}${n}`}async function $(e){switch(e.type){case`text`:return Z(e.text);case`comment`:return`<!--${e.text}-->`;case`fragment`:return(await Promise.all(e.children.map($))).join(``);case`component`:return bt(e.instance);case`element`:{let t=yt(e.props);if(gt.has(e.tag))return`<${e.tag}${t}>`;let n=(await Promise.all(e.children.map($))).join(``);return`<${e.tag}${t}>${n}</${e.tag}>`}}}async function xt(e){return r(`ssr`,async()=>{if(i()!==`ssr`)throw Error(`[vani] renderToString failed to set SSR render mode.`);let t=e.map(e=>({type:`component`,instance:vt(e)}));return(await Promise.all(t.map($))).join(``)})}export{le as a,Xe as abbr,Y as article,ae as aside,Ae as audio,We as blockquote,et as br,ue as button,rt as caption,Je as cite,v as classNames,He as code,at as col,it as colgroup,t as component,Ce as datalist,W as dd,se as details,Ye as dfn,L as div,H as dl,U as dt,y as el,$e as em,Me as embed,xe as fieldset,Te as figcaption,we as figure,q as footer,ve as form,ne as fragment,i as getRenderMode,Ie as h1,Le as h2,Re as h3,ze as h4,Be as h5,Ve as h6,K as header,tt as hr,P as hydrateToDOM,je as iframe,Ee as img,de as input,m as isComponentInstance,F as isDevMode,Ke as kbd,_e as label,Se as legend,B as li,G as main,Pe as mark,be as meter,b as mount,oe as nav,mt as noscript,V as ol,ge as optgroup,he as option,fe as output,Fe as p,De as picture,Ue as pre,ye as progress,p as renderToDOM,xt as renderToString,qe as samp,pt as script,J as section,me as select,X as slot,Ze as small,Oe as source,R as span,E as startTransition,Qe as strong,ft as style,ce as summary,nt as table,ot as tbody,ut as td,ht as template,pe as textarea,ct as tfoot,dt as th,st as thead,Ne as time,lt as tr,z as ul,Ge as var_,ke as video,r as withRenderMode};
package/llms.txt ADDED
@@ -0,0 +1,8 @@
1
+ # Vani project agent guide
2
+
3
+ Vani (from Vanilla) is a Web Standards-first framework for building web applications.
4
+
5
+ A fine-grained reactive open source framework for building lightning-fast apps — no virtual DOM, no
6
+ compiler, no JSX, no transpilation, zero dependencies, SSR support, imperative reactivity.
7
+
8
+ Check the DOCS.md and README.md files for more information.
package/package.json ADDED
@@ -0,0 +1,89 @@
1
+ {
2
+ "name": "@vanijs/vani",
3
+ "version": "0.1.0",
4
+ "description": "Web-standards-first UI runtime with explicit updates, fine-grained DOM ownership, and zero dependencies. No Virtual DOM, no JSX, no compiler. Built for SPA, SSR, and SSG.",
5
+ "keywords": [
6
+ "ui",
7
+ "frontend",
8
+ "framework",
9
+ "web-standards",
10
+ "dom",
11
+ "fine-grained",
12
+ "explicit-rendering",
13
+ "no-virtual-dom",
14
+ "no-jsx",
15
+ "no-compiler",
16
+ "ssr",
17
+ "ssg",
18
+ "hydration",
19
+ "signals",
20
+ "vanilla",
21
+ "vanilla-js",
22
+ "typescript",
23
+ "zero-dependencies"
24
+ ],
25
+ "homepage": "https://itsjavi.com/vani",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/itsjavi/vani.git"
29
+ },
30
+ "license": "MIT",
31
+ "author": {
32
+ "name": "Javier Aguilar"
33
+ },
34
+ "type": "module",
35
+ "exports": {
36
+ ".": {
37
+ "types": "./dist/lib/index.d.mts",
38
+ "default": "./dist/lib/index.mjs"
39
+ }
40
+ },
41
+ "module": "./dist/lib/index.mjs",
42
+ "types": "./dist/lib/index.d.mts",
43
+ "files": [
44
+ "dist/lib/**/*",
45
+ "package.json",
46
+ "llms.txt",
47
+ "DOCS.md",
48
+ "README.md",
49
+ "LICENSE"
50
+ ],
51
+ "dependencies": {},
52
+ "devDependencies": {
53
+ "@eslint/js": "^9.39.2",
54
+ "@shikijs/langs": "^3.21.0",
55
+ "@shikijs/themes": "^3.21.0",
56
+ "@tailwindcss/vite": "^4.1.18",
57
+ "@types/bun": "^1.3.6",
58
+ "@types/node": "^25.0.9",
59
+ "eslint": "^9.39.2",
60
+ "globals": "^17.0.0",
61
+ "lucide-react": "^0.562.0",
62
+ "prettier": "^3.8.0",
63
+ "prettier-plugin-tailwindcss": "^0.7.2",
64
+ "publint": "^0.3.16",
65
+ "shiki": "^3.21.0",
66
+ "tailwind-merge": "^3.4.0",
67
+ "tailwindcss": "^4.1.18",
68
+ "tsdown": "0.20.0-beta.4",
69
+ "tw-animate-css": "^1.4.0",
70
+ "typescript": "~5.9.3",
71
+ "typescript-eslint": "^8.53.1",
72
+ "vite": "npm:rolldown-vite@7.3.1"
73
+ },
74
+ "publishConfig": {
75
+ "access": "public"
76
+ },
77
+ "scripts": {
78
+ "build": "rm -rf dist && vite build && pnpm run build:lib",
79
+ "build:lib": "pnpm tsdown src/vani/index.ts --minify --out-dir dist/lib --dts",
80
+ "dev": "vite",
81
+ "format": "prettier --write . && pnpm dlx sort-package-json",
82
+ "lint": "pnpm run typecheck && pnpm run lint:eslint && pnpm run lint:circular",
83
+ "lint:circular": "pnpm dlx madge --circular --extensions ts src/",
84
+ "lint:eslint": "eslint .",
85
+ "lint:unused": "pnpm dlx knip --exclude exports",
86
+ "preview": "vite preview",
87
+ "typecheck": "tsc --noEmit"
88
+ }
89
+ }