@getforma/core 0.2.0 → 0.3.1
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/README.md +265 -13
- package/dist/{chunk-VKKPX4YU.js → chunk-5H52PKGF.js} +62 -12
- package/dist/chunk-5H52PKGF.js.map +1 -0
- package/dist/{chunk-VP5EOGM5.cjs → chunk-TSQ7AKFT.cjs} +62 -11
- package/dist/chunk-TSQ7AKFT.cjs.map +1 -0
- package/dist/forma-runtime-csp.js +2 -0
- package/dist/forma-runtime.js +2 -0
- package/dist/formajs-runtime-hardened.global.js +1 -1
- package/dist/formajs-runtime-hardened.global.js.map +1 -1
- package/dist/formajs-runtime.global.js +1 -1
- package/dist/formajs-runtime.global.js.map +1 -1
- package/dist/formajs.global.js +1 -1
- package/dist/formajs.global.js.map +1 -1
- package/dist/index.cjs +100 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -27
- package/dist/index.d.ts +43 -27
- package/dist/index.js +57 -27
- package/dist/index.js.map +1 -1
- package/dist/runtime-hardened.cjs +166 -50
- package/dist/runtime-hardened.cjs.map +1 -1
- package/dist/runtime-hardened.d.cts +86 -0
- package/dist/runtime-hardened.d.ts +86 -0
- package/dist/runtime-hardened.js +166 -50
- package/dist/runtime-hardened.js.map +1 -1
- package/dist/runtime.cjs +188 -72
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +167 -51
- package/dist/runtime.js.map +1 -1
- package/dist/ssr/index.cjs +59 -71
- package/dist/ssr/index.cjs.map +1 -1
- package/dist/ssr/index.d.cts +4 -0
- package/dist/ssr/index.d.ts +4 -0
- package/dist/ssr/index.js +59 -71
- package/dist/ssr/index.js.map +1 -1
- package/package.json +26 -3
- package/dist/chunk-VKKPX4YU.js.map +0 -1
- package/dist/chunk-VP5EOGM5.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/@getforma/core)
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
|
|
7
|
-
Reactive DOM library with fine-grained signals
|
|
7
|
+
Reactive DOM library with fine-grained signals. No virtual DOM — `h()` creates real elements, signals update only what changed. ~15KB gzipped.
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
@@ -12,7 +12,71 @@ Reactive DOM library with fine-grained signals, islands architecture, and SSR hy
|
|
|
12
12
|
npm install @getforma/core
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Or use the CDN (no build step required):
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<script src="https://unpkg.com/@getforma/core/dist/formajs-runtime.global.js"></script>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Why FormaJS?
|
|
22
|
+
|
|
23
|
+
Most UI libraries make you choose: simple but limited (Alpine, htmx), or powerful but complex (React, Vue, Svelte). FormaJS gives you a single reactive core that scales from a CDN script tag to a full-stack Rust SSR pipeline.
|
|
24
|
+
|
|
25
|
+
**Design principles:**
|
|
26
|
+
|
|
27
|
+
- **Real DOM, not virtual DOM.** `h('div')` returns an actual `HTMLDivElement`. Signals mutate it directly. No diffing pass, no reconciliation overhead for simple updates. Inspired by [Solid](https://www.solidjs.com/).
|
|
28
|
+
- **Fine-grained reactivity.** Powered by [alien-signals](https://github.com/nicolo-ribaudo/alien-signals). When a signal changes, only the specific DOM text node or attribute that depends on it updates — not the whole component tree.
|
|
29
|
+
- **Three entry points, one engine.** HTML Runtime (like Alpine — zero build step), `h()` hyperscript (like Preact), or JSX. All share the same signal graph. Pick the right tool for the job, upgrade without rewriting.
|
|
30
|
+
- **CSP-safe capable.** The HTML Runtime includes a hand-written expression parser. The standard build enables `new Function()` as a fallback for complex expressions; the hardened build (`forma.hardened.js`) locks it off entirely for strict CSP environments.
|
|
31
|
+
- **Islands over SPAs.** `activateIslands()` hydrates independent regions of server-rendered HTML. Each island is self-contained. Ship less JavaScript, keep server-rendered content instant.
|
|
32
|
+
|
|
33
|
+
**What FormaJS is not:** It's not a framework with opinions about routing, data fetching, or state management patterns. It's a reactive DOM library. You bring the architecture.
|
|
34
|
+
|
|
35
|
+
## Three Ways to Use FormaJS
|
|
36
|
+
|
|
37
|
+
### 1. HTML Runtime (no build step)
|
|
38
|
+
|
|
39
|
+
Drop a script tag, write `data-*` attributes. Zero config, zero tooling.
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<script src="https://unpkg.com/@getforma/core/dist/formajs-runtime.global.js"></script>
|
|
43
|
+
|
|
44
|
+
<div data-forma-state='{"count": 0}'>
|
|
45
|
+
<p data-text="{count}"></p>
|
|
46
|
+
<button data-on:click="{count++}">+1</button>
|
|
47
|
+
<button data-on:click="{count = 0}">Reset</button>
|
|
48
|
+
</div>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
#### Supported Directives
|
|
52
|
+
|
|
53
|
+
| Directive | Description | Example |
|
|
54
|
+
|-----------|-------------|---------|
|
|
55
|
+
| `data-forma-state` | Declare reactive state (JSON) | `data-forma-state='{"count": 0}'` |
|
|
56
|
+
| `data-text` | Bind text content | `data-text="{count}"` |
|
|
57
|
+
| `data-show` | Toggle visibility (display) | `data-show="{isOpen}"` |
|
|
58
|
+
| `data-if` | Conditional render (add/remove from DOM) | `data-if="{loggedIn}"` |
|
|
59
|
+
| `data-model` | Two-way binding (inputs) | `data-model="{email}"` |
|
|
60
|
+
| `data-on:event` | Event handler | `data-on:click="{count++}"` |
|
|
61
|
+
| `data-class:name` | Conditional CSS class | `data-class:active="{isActive}"` |
|
|
62
|
+
| `data-bind:attr` | Dynamic attribute | `data-bind:href="{url}"` |
|
|
63
|
+
| `data-list` | List rendering with keyed reconciliation | `data-list="{items}"` |
|
|
64
|
+
| `data-computed` | Computed value | `data-computed="doubled = count * 2"` |
|
|
65
|
+
| `data-persist` | Persist state to localStorage | `data-persist="{count}"` |
|
|
66
|
+
| `data-fetch` | Fetch data from URL | `data-fetch="GET /api/items → items"` |
|
|
67
|
+
| `data-transition:*` | Enter/leave CSS transitions | `data-transition:enter="fade-in"` |
|
|
68
|
+
|
|
69
|
+
CSP-safe expression parser — no `eval()` or `new Function()` by default. For strict CSP environments, use the hardened build:
|
|
70
|
+
|
|
71
|
+
```html
|
|
72
|
+
<script src="https://unpkg.com/@getforma/core/dist/formajs-runtime-hardened.global.js"></script>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 2. Hyperscript — `h()`
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npm install @getforma/core
|
|
79
|
+
```
|
|
16
80
|
|
|
17
81
|
```typescript
|
|
18
82
|
import { createSignal, h, mount } from '@getforma/core';
|
|
@@ -27,21 +91,209 @@ mount(() =>
|
|
|
27
91
|
);
|
|
28
92
|
```
|
|
29
93
|
|
|
30
|
-
|
|
94
|
+
### 3. JSX
|
|
95
|
+
|
|
96
|
+
Same `h()` function, JSX syntax. Configure your bundler:
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
// tsconfig.json
|
|
100
|
+
{
|
|
101
|
+
"compilerOptions": {
|
|
102
|
+
"jsx": "react",
|
|
103
|
+
"jsxFactory": "h",
|
|
104
|
+
"jsxFragmentFactory": "Fragment"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
import { createSignal, h, Fragment, mount } from '@getforma/core';
|
|
111
|
+
|
|
112
|
+
const [count, setCount] = createSignal(0);
|
|
113
|
+
|
|
114
|
+
function Counter() {
|
|
115
|
+
return (
|
|
116
|
+
<>
|
|
117
|
+
<p>{() => `Count: ${count()}`}</p>
|
|
118
|
+
<button onClick={() => setCount(count() + 1)}>+1</button>
|
|
119
|
+
</>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
mount(() => <Counter />, '#app');
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## CDN Usage
|
|
127
|
+
|
|
128
|
+
### Standard (recommended)
|
|
129
|
+
```html
|
|
130
|
+
<script src="https://unpkg.com/@getforma/core/dist/forma-runtime.js"></script>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### CSP-Safe (strict Content-Security-Policy)
|
|
134
|
+
```html
|
|
135
|
+
<script src="https://unpkg.com/@getforma/core/dist/forma-runtime-csp.js"></script>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
> The CSP build uses a hand-written expression parser and never calls `new Function`.
|
|
139
|
+
> It supports most common patterns. See [examples/csp](./examples/csp) for a working demo.
|
|
140
|
+
|
|
141
|
+
## Core API
|
|
142
|
+
|
|
143
|
+
### Signals
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import { createSignal, createEffect, createComputed, batch } from '@getforma/core';
|
|
147
|
+
|
|
148
|
+
const [count, setCount] = createSignal(0);
|
|
149
|
+
const doubled = createComputed(() => count() * 2);
|
|
150
|
+
|
|
151
|
+
createEffect(() => console.log('count:', count()));
|
|
152
|
+
|
|
153
|
+
batch(() => {
|
|
154
|
+
setCount(1);
|
|
155
|
+
setCount(2); // effect fires once with value 2
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Conditional Rendering
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
import { createSignal, createShow, createSwitch, h } from '@getforma/core';
|
|
163
|
+
|
|
164
|
+
const [loggedIn, setLoggedIn] = createSignal(false);
|
|
165
|
+
|
|
166
|
+
// createShow — toggle between two branches
|
|
167
|
+
createShow(loggedIn,
|
|
168
|
+
() => h('p', null, 'Welcome back'),
|
|
169
|
+
() => h('p', null, 'Please sign in'),
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
// createSwitch — multi-branch with caching
|
|
173
|
+
const [view, setView] = createSignal('home');
|
|
174
|
+
createSwitch(view, [
|
|
175
|
+
{ match: 'home', render: () => h('div', null, 'Home') },
|
|
176
|
+
{ match: 'settings', render: () => h('div', null, 'Settings') },
|
|
177
|
+
], () => h('div', null, '404 Not Found'));
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### List Rendering
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
import { createSignal, createList, h } from '@getforma/core';
|
|
184
|
+
|
|
185
|
+
const [items, setItems] = createSignal([
|
|
186
|
+
{ id: 1, name: 'Alice' },
|
|
187
|
+
{ id: 2, name: 'Bob' },
|
|
188
|
+
]);
|
|
189
|
+
|
|
190
|
+
createList(
|
|
191
|
+
items,
|
|
192
|
+
(item) => item.id, // key function
|
|
193
|
+
(item) => h('li', null, item.name),
|
|
194
|
+
);
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Store (deep reactivity)
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
import { createStore } from '@getforma/core';
|
|
201
|
+
|
|
202
|
+
const [state, setState] = createStore({
|
|
203
|
+
user: { name: 'Alice', prefs: { theme: 'dark' } },
|
|
204
|
+
items: [1, 2, 3],
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Read reactively
|
|
208
|
+
state.user.name; // 'Alice'
|
|
209
|
+
|
|
210
|
+
// Mutate — only affected subscribers update
|
|
211
|
+
setState('user', 'name', 'Bob');
|
|
212
|
+
setState('items', items => [...items, 4]);
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Components
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
import { defineComponent, onMount, onUnmount, h } from '@getforma/core';
|
|
219
|
+
|
|
220
|
+
const Timer = defineComponent(() => {
|
|
221
|
+
const [seconds, setSeconds] = createSignal(0);
|
|
222
|
+
|
|
223
|
+
onMount(() => {
|
|
224
|
+
const id = setInterval(() => setSeconds(s => s + 1), 1000);
|
|
225
|
+
return () => clearInterval(id); // cleanup on unmount
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
return h('span', null, () => `${seconds()}s`);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
document.body.appendChild(Timer());
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Context (Dependency Injection)
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
import { createContext, provide, inject } from '@getforma/core';
|
|
238
|
+
|
|
239
|
+
const ThemeCtx = createContext('light');
|
|
240
|
+
|
|
241
|
+
provide(ThemeCtx, 'dark');
|
|
242
|
+
const theme = inject(ThemeCtx); // 'dark'
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Islands Architecture
|
|
246
|
+
|
|
247
|
+
For server-rendered HTML, activate independent interactive regions:
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import { activateIslands, createSignal, h } from '@getforma/core';
|
|
251
|
+
|
|
252
|
+
activateIslands({
|
|
253
|
+
Counter: (el, props) => {
|
|
254
|
+
const [count, setCount] = createSignal(props.initial ?? 0);
|
|
255
|
+
// Hydrate: attach reactivity to existing server-rendered DOM
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
```html
|
|
261
|
+
<!-- Server-rendered HTML -->
|
|
262
|
+
<div data-forma-island="Counter" data-forma-props='{"initial": 5}'>
|
|
263
|
+
<span>5</span>
|
|
264
|
+
<button>+1</button>
|
|
265
|
+
</div>
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Subpath Exports
|
|
269
|
+
|
|
270
|
+
| Import | Description |
|
|
271
|
+
|--------|-------------|
|
|
272
|
+
| `@getforma/core` | Signals, `h()`, `mount()`, lists, stores, components |
|
|
273
|
+
| `@getforma/core/runtime` | HTML Runtime — `initRuntime()`, `mount()`, `unmount()` |
|
|
274
|
+
| `@getforma/core/runtime/global` | HTML Runtime global build (IIFE, for `<script>` tags) |
|
|
275
|
+
| `@getforma/core/runtime-hardened` | Runtime with `new Function()` locked off (strict CSP) |
|
|
276
|
+
| `@getforma/core/runtime-csp` | Alias for `runtime-hardened` (CSP-safe build) |
|
|
277
|
+
| `@getforma/core/runtime-csp/global` | CSP-safe global build (IIFE, for `<script>` tags) |
|
|
278
|
+
| `@getforma/core/ssr` | Server-side rendering — `renderToString()`, `renderToStream()` |
|
|
279
|
+
| `@getforma/core/tc39` | TC39-compatible `Signal.State` and `Signal.Computed` classes |
|
|
280
|
+
|
|
281
|
+
## Examples
|
|
282
|
+
|
|
283
|
+
See the [`examples/`](./examples) directory:
|
|
31
284
|
|
|
32
|
-
- **
|
|
33
|
-
- **
|
|
34
|
-
- **
|
|
35
|
-
- **
|
|
36
|
-
- **
|
|
37
|
-
- **List rendering** — `createList()` with LIS-based keyed reconciliation, handles 50K+ rows.
|
|
38
|
-
- **State management** — `createStore()` with deep reactivity, history, persistence.
|
|
285
|
+
- **counter** — minimal `h()` counter
|
|
286
|
+
- **counter-jsx** — same counter with JSX syntax
|
|
287
|
+
- **csp** — CSP-safe runtime with strict Content-Security-Policy meta tag
|
|
288
|
+
- **todo** — todo list with `createList` and keyed reconciliation
|
|
289
|
+
- **data-table** — sortable table with `createList`
|
|
39
290
|
|
|
40
291
|
## Ecosystem
|
|
41
292
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
293
|
+
| Package | Description |
|
|
294
|
+
|---------|-------------|
|
|
295
|
+
| [@getforma/core](https://www.npmjs.com/package/@getforma/core) | This library — `npm install @getforma/core` |
|
|
296
|
+
| [forma](https://github.com/getforma-dev/forma) | Rust server framework (forma-ir + forma-server) |
|
|
45
297
|
|
|
46
298
|
## License
|
|
47
299
|
|
|
@@ -21,7 +21,7 @@ function createRoot(fn) {
|
|
|
21
21
|
return fn(dispose);
|
|
22
22
|
} finally {
|
|
23
23
|
currentRoot = rootStack.pop() ?? null;
|
|
24
|
-
if (rootStack.length === 0
|
|
24
|
+
if (rootStack.length === 0) {
|
|
25
25
|
rootStack.length = 0;
|
|
26
26
|
}
|
|
27
27
|
}
|
|
@@ -562,8 +562,47 @@ function handleEvent(el, key, value2) {
|
|
|
562
562
|
);
|
|
563
563
|
}
|
|
564
564
|
function handleInnerHTML(el, _key, value2) {
|
|
565
|
-
|
|
566
|
-
|
|
565
|
+
if (typeof value2 === "function") {
|
|
566
|
+
internalEffect(() => {
|
|
567
|
+
const resolved = value2();
|
|
568
|
+
if (resolved == null) {
|
|
569
|
+
el.innerHTML = "";
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
if (typeof resolved !== "object" || !("__html" in resolved)) {
|
|
573
|
+
throw new TypeError(
|
|
574
|
+
"dangerouslySetInnerHTML: expected { __html: string }, got " + typeof resolved
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
const html = resolved.__html;
|
|
578
|
+
if (typeof html !== "string") {
|
|
579
|
+
throw new TypeError(
|
|
580
|
+
"dangerouslySetInnerHTML: __html must be a string, got " + typeof html
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
const cache = getCache(el);
|
|
584
|
+
if (cache["innerHTML"] === html) return;
|
|
585
|
+
cache["innerHTML"] = html;
|
|
586
|
+
el.innerHTML = html;
|
|
587
|
+
});
|
|
588
|
+
} else {
|
|
589
|
+
if (value2 == null) {
|
|
590
|
+
el.innerHTML = "";
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
if (typeof value2 !== "object" || !("__html" in value2)) {
|
|
594
|
+
throw new TypeError(
|
|
595
|
+
"dangerouslySetInnerHTML: expected { __html: string }, got " + typeof value2
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
const html = value2.__html;
|
|
599
|
+
if (typeof html !== "string") {
|
|
600
|
+
throw new TypeError(
|
|
601
|
+
"dangerouslySetInnerHTML: __html must be a string, got " + typeof html
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
el.innerHTML = html;
|
|
605
|
+
}
|
|
567
606
|
}
|
|
568
607
|
function handleXLink(el, key, value2) {
|
|
569
608
|
const localName = key.slice(6);
|
|
@@ -685,7 +724,18 @@ function applyStaticProp(el, key, value2) {
|
|
|
685
724
|
return;
|
|
686
725
|
}
|
|
687
726
|
if (key === "dangerouslySetInnerHTML") {
|
|
688
|
-
|
|
727
|
+
if (typeof value2 !== "object" || !("__html" in value2)) {
|
|
728
|
+
throw new TypeError(
|
|
729
|
+
"dangerouslySetInnerHTML: expected { __html: string }, got " + typeof value2
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
const html = value2.__html;
|
|
733
|
+
if (typeof html !== "string") {
|
|
734
|
+
throw new TypeError(
|
|
735
|
+
"dangerouslySetInnerHTML: __html must be a string, got " + typeof html
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
el.innerHTML = html;
|
|
689
739
|
return;
|
|
690
740
|
}
|
|
691
741
|
if (key.charCodeAt(0) === 120 && key.startsWith("xlink:")) {
|
|
@@ -843,10 +893,10 @@ function createShow(when, thenFn, elseFn) {
|
|
|
843
893
|
let currentNode = null;
|
|
844
894
|
let lastTruthy = null;
|
|
845
895
|
let currentDispose = null;
|
|
846
|
-
const DEBUG_LABEL = thenFn.toString().slice(0, 60);
|
|
847
896
|
const showDispose = internalEffect(() => {
|
|
848
897
|
const truthy = !!when();
|
|
849
898
|
const DEBUG = typeof globalThis.__FORMA_DEBUG__ !== "undefined";
|
|
899
|
+
const DEBUG_LABEL = DEBUG ? thenFn.toString().slice(0, 60) : "";
|
|
850
900
|
if (truthy === lastTruthy) {
|
|
851
901
|
if (DEBUG) console.log("[forma:show] skip (same)", truthy, DEBUG_LABEL);
|
|
852
902
|
return;
|
|
@@ -903,13 +953,13 @@ function setHydrating(value2) {
|
|
|
903
953
|
hydrating = value2;
|
|
904
954
|
}
|
|
905
955
|
function isDescriptor(v) {
|
|
906
|
-
return v != null && typeof v === "object" && v.type === "element";
|
|
956
|
+
return v != null && typeof v === "object" && "type" in v && v.type === "element";
|
|
907
957
|
}
|
|
908
958
|
function isShowDescriptor(v) {
|
|
909
|
-
return v != null && typeof v === "object" && v.type === "show";
|
|
959
|
+
return v != null && typeof v === "object" && "type" in v && v.type === "show";
|
|
910
960
|
}
|
|
911
961
|
function isListDescriptor(v) {
|
|
912
|
-
return v != null && typeof v === "object" && v.type === "list";
|
|
962
|
+
return v != null && typeof v === "object" && "type" in v && v.type === "list";
|
|
913
963
|
}
|
|
914
964
|
function applyDynamicProps(el, props) {
|
|
915
965
|
if (!props) return;
|
|
@@ -1087,7 +1137,7 @@ function adoptBranchContent(desc, regionStart, regionEnd) {
|
|
|
1087
1137
|
}
|
|
1088
1138
|
function adoptNode(desc, ssrEl) {
|
|
1089
1139
|
if (!ssrEl || ssrEl.tagName !== desc.tag.toUpperCase()) {
|
|
1090
|
-
console.warn(`Hydration mismatch: expected <${desc.tag}>, got <${ssrEl?.tagName?.toLowerCase() ?? "nothing"}>`);
|
|
1140
|
+
if (__DEV__) console.warn(`Hydration mismatch: expected <${desc.tag}>, got <${ssrEl?.tagName?.toLowerCase() ?? "nothing"}>`);
|
|
1091
1141
|
const fresh = descriptorToElement(desc);
|
|
1092
1142
|
if (ssrEl) ssrEl.replaceWith(fresh);
|
|
1093
1143
|
return;
|
|
@@ -1754,6 +1804,6 @@ function createList(items, keyFn, renderFn, options) {
|
|
|
1754
1804
|
return fragment2;
|
|
1755
1805
|
}
|
|
1756
1806
|
|
|
1757
|
-
export { Fragment, batch, cleanup, createEffect, createList, createMemo, createReducer, createRef, createResource, createRoot, createShow, fragment, h, hydrateIsland, internalEffect, longestIncreasingSubsequence, on, onCleanup, onError, popSuspenseContext, pushSuspenseContext, reconcileList, untrack };
|
|
1758
|
-
//# sourceMappingURL=chunk-
|
|
1759
|
-
//# sourceMappingURL=chunk-
|
|
1807
|
+
export { Fragment, __DEV__, batch, cleanup, createEffect, createList, createMemo, createReducer, createRef, createResource, createRoot, createShow, fragment, h, hydrateIsland, internalEffect, longestIncreasingSubsequence, on, onCleanup, onError, popSuspenseContext, pushSuspenseContext, reconcileList, untrack };
|
|
1808
|
+
//# sourceMappingURL=chunk-5H52PKGF.js.map
|
|
1809
|
+
//# sourceMappingURL=chunk-5H52PKGF.js.map
|