@pyreon/vue-compat 0.2.0 → 0.3.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/lib/analysis/index.js.html +1 -1
- package/lib/index.js +261 -65
- package/lib/index.js.map +1 -1
- package/lib/types/index.d.ts +265 -64
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index2.d.ts +22 -49
- package/lib/types/index2.d.ts.map +1 -1
- package/package.json +14 -4
- package/src/index.ts +307 -81
- package/src/jsx-runtime.ts +178 -0
- package/src/tests/vue-compat.test.ts +348 -119
|
@@ -5386,7 +5386,7 @@ var drawChart = (function (exports) {
|
|
|
5386
5386
|
</script>
|
|
5387
5387
|
<script>
|
|
5388
5388
|
/*<!--*/
|
|
5389
|
-
const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src
|
|
5389
|
+
const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src","children":[{"uid":"d4e3dc19-1","name":"jsx-runtime.ts"},{"uid":"d4e3dc19-3","name":"index.ts"}]}]}],"isRoot":true},"nodeParts":{"d4e3dc19-1":{"renderedLength":186,"gzipLength":138,"brotliLength":0,"metaUid":"d4e3dc19-0"},"d4e3dc19-3":{"renderedLength":12003,"gzipLength":2957,"brotliLength":0,"metaUid":"d4e3dc19-2"}},"nodeMetas":{"d4e3dc19-0":{"id":"/src/jsx-runtime.ts","moduleParts":{"index.js":"d4e3dc19-1"},"imported":[{"uid":"d4e3dc19-4"},{"uid":"d4e3dc19-5"}],"importedBy":[{"uid":"d4e3dc19-2"}]},"d4e3dc19-2":{"id":"/src/index.ts","moduleParts":{"index.js":"d4e3dc19-3"},"imported":[{"uid":"d4e3dc19-4"},{"uid":"d4e3dc19-5"},{"uid":"d4e3dc19-6"},{"uid":"d4e3dc19-0"}],"importedBy":[],"isEntry":true},"d4e3dc19-4":{"id":"@pyreon/core","moduleParts":{},"imported":[],"importedBy":[{"uid":"d4e3dc19-2"},{"uid":"d4e3dc19-0"}]},"d4e3dc19-5":{"id":"@pyreon/reactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"d4e3dc19-2"},{"uid":"d4e3dc19-0"}]},"d4e3dc19-6":{"id":"@pyreon/runtime-dom","moduleParts":{},"imported":[],"importedBy":[{"uid":"d4e3dc19-2"}]}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
|
|
5390
5390
|
|
|
5391
5391
|
const run = () => {
|
|
5392
5392
|
const width = window.innerWidth;
|
package/lib/index.js
CHANGED
|
@@ -2,6 +2,17 @@ import { Fragment, createContext, h as pyreonH, onMount, onUnmount, onUpdate, po
|
|
|
2
2
|
import { batch, computed as computed$1, createStore, effect, nextTick as nextTick$1, signal } from "@pyreon/reactivity";
|
|
3
3
|
import { mount } from "@pyreon/runtime-dom";
|
|
4
4
|
|
|
5
|
+
//#region src/jsx-runtime.ts
|
|
6
|
+
let _currentCtx = null;
|
|
7
|
+
let _hookIndex = 0;
|
|
8
|
+
function getCurrentCtx() {
|
|
9
|
+
return _currentCtx;
|
|
10
|
+
}
|
|
11
|
+
function getHookIndex() {
|
|
12
|
+
return _hookIndex++;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
//#endregion
|
|
5
16
|
//#region src/index.ts
|
|
6
17
|
const V_IS_REF = Symbol("__v_isRef");
|
|
7
18
|
const V_IS_READONLY = Symbol("__v_isReadonly");
|
|
@@ -10,26 +21,45 @@ const V_RAW = Symbol("__v_raw");
|
|
|
10
21
|
* Creates a reactive ref wrapping the given value.
|
|
11
22
|
* Access via `.value` — reads track, writes trigger.
|
|
12
23
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
24
|
+
* Inside a component: hook-indexed. The setter also calls `scheduleRerender()`.
|
|
25
|
+
* Outside a component: creates a standalone reactive ref.
|
|
15
26
|
*/
|
|
16
27
|
function ref(value) {
|
|
28
|
+
const ctx = getCurrentCtx();
|
|
29
|
+
if (ctx) {
|
|
30
|
+
const idx = getHookIndex();
|
|
31
|
+
if (idx < ctx.hooks.length) return ctx.hooks[idx];
|
|
32
|
+
const s = signal(value);
|
|
33
|
+
const { scheduleRerender } = ctx;
|
|
34
|
+
const r = {
|
|
35
|
+
[V_IS_REF]: true,
|
|
36
|
+
get value() {
|
|
37
|
+
return s();
|
|
38
|
+
},
|
|
39
|
+
set value(v) {
|
|
40
|
+
s.set(v);
|
|
41
|
+
scheduleRerender();
|
|
42
|
+
},
|
|
43
|
+
_signal: s,
|
|
44
|
+
_scheduleRerender: scheduleRerender
|
|
45
|
+
};
|
|
46
|
+
ctx.hooks[idx] = r;
|
|
47
|
+
return r;
|
|
48
|
+
}
|
|
17
49
|
const s = signal(value);
|
|
18
50
|
return {
|
|
19
51
|
[V_IS_REF]: true,
|
|
20
52
|
get value() {
|
|
21
53
|
return s();
|
|
22
54
|
},
|
|
23
|
-
set value(
|
|
24
|
-
s.set(
|
|
55
|
+
set value(v) {
|
|
56
|
+
s.set(v);
|
|
25
57
|
},
|
|
26
58
|
_signal: s
|
|
27
59
|
};
|
|
28
60
|
}
|
|
29
61
|
/**
|
|
30
62
|
* Creates a shallow ref — same as `ref()` in Pyreon since signals are inherently shallow.
|
|
31
|
-
*
|
|
32
|
-
* Difference from Vue: identical to `ref()` — Pyreon signals don't perform deep conversion.
|
|
33
63
|
*/
|
|
34
64
|
function shallowRef(value) {
|
|
35
65
|
return ref(value);
|
|
@@ -44,6 +74,7 @@ function triggerRef(r) {
|
|
|
44
74
|
internal._signal.set(void 0);
|
|
45
75
|
internal._signal.set(current);
|
|
46
76
|
}
|
|
77
|
+
if (internal._scheduleRerender) internal._scheduleRerender();
|
|
47
78
|
}
|
|
48
79
|
/**
|
|
49
80
|
* Returns `true` if the value is a ref (created by `ref()` or `computed()`).
|
|
@@ -58,6 +89,28 @@ function unref(r) {
|
|
|
58
89
|
return isRef(r) ? r.value : r;
|
|
59
90
|
}
|
|
60
91
|
function computed(fnOrOptions) {
|
|
92
|
+
const ctx = getCurrentCtx();
|
|
93
|
+
if (ctx) {
|
|
94
|
+
const idx = getHookIndex();
|
|
95
|
+
if (idx < ctx.hooks.length) return ctx.hooks[idx];
|
|
96
|
+
const getter = typeof fnOrOptions === "function" ? fnOrOptions : fnOrOptions.get;
|
|
97
|
+
const setter = typeof fnOrOptions === "object" ? fnOrOptions.set : void 0;
|
|
98
|
+
const c = computed$1(getter);
|
|
99
|
+
const { scheduleRerender } = ctx;
|
|
100
|
+
const r = {
|
|
101
|
+
[V_IS_REF]: true,
|
|
102
|
+
get value() {
|
|
103
|
+
return c();
|
|
104
|
+
},
|
|
105
|
+
set value(v) {
|
|
106
|
+
if (!setter) throw new Error("Cannot set value of a computed ref — computed refs are readonly");
|
|
107
|
+
setter(v);
|
|
108
|
+
scheduleRerender();
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
ctx.hooks[idx] = r;
|
|
112
|
+
return r;
|
|
113
|
+
}
|
|
61
114
|
const getter = typeof fnOrOptions === "function" ? fnOrOptions : fnOrOptions.get;
|
|
62
115
|
const setter = typeof fnOrOptions === "object" ? fnOrOptions.set : void 0;
|
|
63
116
|
const c = computed$1(getter);
|
|
@@ -72,36 +125,65 @@ function computed(fnOrOptions) {
|
|
|
72
125
|
}
|
|
73
126
|
};
|
|
74
127
|
}
|
|
128
|
+
const rawMap = /* @__PURE__ */ new WeakMap();
|
|
75
129
|
/**
|
|
76
130
|
* Creates a deeply reactive proxy from a plain object.
|
|
77
131
|
* Backed by Pyreon's `createStore()`.
|
|
78
132
|
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
133
|
+
* Inside a component: hook-indexed. Proxy wrapper intercepts sets to
|
|
134
|
+
* call `scheduleRerender()`.
|
|
81
135
|
*/
|
|
82
136
|
function reactive(obj) {
|
|
137
|
+
const ctx = getCurrentCtx();
|
|
138
|
+
if (ctx) {
|
|
139
|
+
const idx = getHookIndex();
|
|
140
|
+
if (idx < ctx.hooks.length) return ctx.hooks[idx];
|
|
141
|
+
const proxy = createStore(obj);
|
|
142
|
+
rawMap.set(proxy, obj);
|
|
143
|
+
const { scheduleRerender } = ctx;
|
|
144
|
+
const wrapped = new Proxy(proxy, {
|
|
145
|
+
set(target, key, value, receiver) {
|
|
146
|
+
const result = Reflect.set(target, key, value, receiver);
|
|
147
|
+
scheduleRerender();
|
|
148
|
+
return result;
|
|
149
|
+
},
|
|
150
|
+
deleteProperty(target, key) {
|
|
151
|
+
const result = Reflect.deleteProperty(target, key);
|
|
152
|
+
scheduleRerender();
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
rawMap.set(wrapped, obj);
|
|
157
|
+
ctx.hooks[idx] = wrapped;
|
|
158
|
+
return wrapped;
|
|
159
|
+
}
|
|
83
160
|
const proxy = createStore(obj);
|
|
84
161
|
rawMap.set(proxy, obj);
|
|
85
162
|
return proxy;
|
|
86
163
|
}
|
|
87
164
|
/**
|
|
88
|
-
* Creates a shallow reactive proxy.
|
|
89
|
-
* In Pyreon, `createStore` is already per-property (not deeply recursive for primitives),
|
|
90
|
-
* but nested objects will be wrapped. For truly shallow behavior, use individual refs.
|
|
91
|
-
*
|
|
92
|
-
* Difference from Vue: backed by `createStore()` — same as `reactive()` in practice.
|
|
165
|
+
* Creates a shallow reactive proxy — same as `reactive()` in Pyreon.
|
|
93
166
|
*/
|
|
94
167
|
function shallowReactive(obj) {
|
|
95
168
|
return reactive(obj);
|
|
96
169
|
}
|
|
97
|
-
const rawMap = /* @__PURE__ */ new WeakMap();
|
|
98
170
|
/**
|
|
99
171
|
* Returns a readonly proxy that throws on mutation attempts.
|
|
100
172
|
*
|
|
101
|
-
*
|
|
102
|
-
* rather than Vue's full readonly reactive system.
|
|
173
|
+
* Inside a component: hook-indexed.
|
|
103
174
|
*/
|
|
104
175
|
function readonly(obj) {
|
|
176
|
+
const ctx = getCurrentCtx();
|
|
177
|
+
if (ctx) {
|
|
178
|
+
const idx = getHookIndex();
|
|
179
|
+
if (idx < ctx.hooks.length) return ctx.hooks[idx];
|
|
180
|
+
const proxy = _createReadonlyProxy(obj);
|
|
181
|
+
ctx.hooks[idx] = proxy;
|
|
182
|
+
return proxy;
|
|
183
|
+
}
|
|
184
|
+
return _createReadonlyProxy(obj);
|
|
185
|
+
}
|
|
186
|
+
function _createReadonlyProxy(obj) {
|
|
105
187
|
return new Proxy(obj, {
|
|
106
188
|
get(target, key) {
|
|
107
189
|
if (key === V_IS_READONLY) return true;
|
|
@@ -119,8 +201,6 @@ function readonly(obj) {
|
|
|
119
201
|
}
|
|
120
202
|
/**
|
|
121
203
|
* Returns the raw (unwrapped) object behind a reactive or readonly proxy.
|
|
122
|
-
*
|
|
123
|
-
* Difference from Vue: only works for objects created via `reactive()` or `readonly()`.
|
|
124
204
|
*/
|
|
125
205
|
function toRaw(proxy) {
|
|
126
206
|
const readonlyRaw = proxy[V_RAW];
|
|
@@ -130,8 +210,21 @@ function toRaw(proxy) {
|
|
|
130
210
|
/**
|
|
131
211
|
* Creates a ref linked to a property of a reactive object.
|
|
132
212
|
* Reading/writing the ref's `.value` reads/writes the original property.
|
|
213
|
+
*
|
|
214
|
+
* Inside a component: hook-indexed.
|
|
133
215
|
*/
|
|
134
216
|
function toRef(obj, key) {
|
|
217
|
+
const ctx = getCurrentCtx();
|
|
218
|
+
if (ctx) {
|
|
219
|
+
const idx = getHookIndex();
|
|
220
|
+
if (idx < ctx.hooks.length) return ctx.hooks[idx];
|
|
221
|
+
const r = _createToRef(obj, key);
|
|
222
|
+
ctx.hooks[idx] = r;
|
|
223
|
+
return r;
|
|
224
|
+
}
|
|
225
|
+
return _createToRef(obj, key);
|
|
226
|
+
}
|
|
227
|
+
function _createToRef(obj, key) {
|
|
135
228
|
return {
|
|
136
229
|
[V_IS_REF]: true,
|
|
137
230
|
get value() {
|
|
@@ -145,20 +238,61 @@ function toRef(obj, key) {
|
|
|
145
238
|
/**
|
|
146
239
|
* Converts all properties of a reactive object into individual refs.
|
|
147
240
|
* Each ref is linked to the original property (not a copy).
|
|
241
|
+
*
|
|
242
|
+
* Inside a component: hook-indexed (the entire result, not individual refs).
|
|
148
243
|
*/
|
|
149
244
|
function toRefs(obj) {
|
|
245
|
+
const ctx = getCurrentCtx();
|
|
246
|
+
if (ctx) {
|
|
247
|
+
const idx = getHookIndex();
|
|
248
|
+
if (idx < ctx.hooks.length) return ctx.hooks[idx];
|
|
249
|
+
const result = {};
|
|
250
|
+
for (const key of Object.keys(obj)) result[key] = _createToRef(obj, key);
|
|
251
|
+
ctx.hooks[idx] = result;
|
|
252
|
+
return result;
|
|
253
|
+
}
|
|
150
254
|
const result = {};
|
|
151
|
-
for (const key of Object.keys(obj)) result[key] =
|
|
255
|
+
for (const key of Object.keys(obj)) result[key] = _createToRef(obj, key);
|
|
152
256
|
return result;
|
|
153
257
|
}
|
|
154
258
|
/**
|
|
155
259
|
* Watches a reactive source and calls `cb` when it changes.
|
|
156
|
-
* Tracks old and new values.
|
|
157
260
|
*
|
|
158
|
-
*
|
|
159
|
-
* Returns a stop function to dispose the watcher.
|
|
261
|
+
* Inside a component: hook-indexed, created once. Disposed on unmount.
|
|
160
262
|
*/
|
|
161
263
|
function watch(source, cb, options) {
|
|
264
|
+
const ctx = getCurrentCtx();
|
|
265
|
+
if (ctx) {
|
|
266
|
+
const idx = getHookIndex();
|
|
267
|
+
if (idx < ctx.hooks.length) return ctx.hooks[idx];
|
|
268
|
+
const getter = isRef(source) ? () => source.value : source;
|
|
269
|
+
let oldValue;
|
|
270
|
+
let initialized = false;
|
|
271
|
+
if (options?.immediate) {
|
|
272
|
+
oldValue = void 0;
|
|
273
|
+
const current = getter();
|
|
274
|
+
cb(current, oldValue);
|
|
275
|
+
oldValue = current;
|
|
276
|
+
initialized = true;
|
|
277
|
+
}
|
|
278
|
+
let running = false;
|
|
279
|
+
const e = effect(() => {
|
|
280
|
+
if (running) return;
|
|
281
|
+
running = true;
|
|
282
|
+
try {
|
|
283
|
+
const newValue = getter();
|
|
284
|
+
if (initialized) cb(newValue, oldValue);
|
|
285
|
+
oldValue = newValue;
|
|
286
|
+
initialized = true;
|
|
287
|
+
} finally {
|
|
288
|
+
running = false;
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
const stop = () => e.dispose();
|
|
292
|
+
ctx.hooks[idx] = stop;
|
|
293
|
+
ctx.unmountCallbacks.push(stop);
|
|
294
|
+
return stop;
|
|
295
|
+
}
|
|
162
296
|
const getter = isRef(source) ? () => source.value : source;
|
|
163
297
|
let oldValue;
|
|
164
298
|
let initialized = false;
|
|
@@ -169,11 +303,18 @@ function watch(source, cb, options) {
|
|
|
169
303
|
oldValue = current;
|
|
170
304
|
initialized = true;
|
|
171
305
|
}
|
|
306
|
+
let running = false;
|
|
172
307
|
const e = effect(() => {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
308
|
+
if (running) return;
|
|
309
|
+
running = true;
|
|
310
|
+
try {
|
|
311
|
+
const newValue = getter();
|
|
312
|
+
if (initialized) cb(newValue, oldValue);
|
|
313
|
+
oldValue = newValue;
|
|
314
|
+
initialized = true;
|
|
315
|
+
} finally {
|
|
316
|
+
running = false;
|
|
317
|
+
}
|
|
177
318
|
});
|
|
178
319
|
return () => e.dispose();
|
|
179
320
|
}
|
|
@@ -181,61 +322,117 @@ function watch(source, cb, options) {
|
|
|
181
322
|
* Runs the given function reactively — re-executes whenever its tracked
|
|
182
323
|
* dependencies change.
|
|
183
324
|
*
|
|
184
|
-
*
|
|
185
|
-
* Returns a stop function.
|
|
325
|
+
* Inside a component: hook-indexed, created once. Disposed on unmount.
|
|
186
326
|
*/
|
|
187
327
|
function watchEffect(fn) {
|
|
188
|
-
const
|
|
328
|
+
const ctx = getCurrentCtx();
|
|
329
|
+
if (ctx) {
|
|
330
|
+
const idx = getHookIndex();
|
|
331
|
+
if (idx < ctx.hooks.length) return ctx.hooks[idx];
|
|
332
|
+
let running = false;
|
|
333
|
+
const e = effect(() => {
|
|
334
|
+
if (running) return;
|
|
335
|
+
running = true;
|
|
336
|
+
try {
|
|
337
|
+
fn();
|
|
338
|
+
} finally {
|
|
339
|
+
running = false;
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
const stop = () => e.dispose();
|
|
343
|
+
ctx.hooks[idx] = stop;
|
|
344
|
+
ctx.unmountCallbacks.push(stop);
|
|
345
|
+
return stop;
|
|
346
|
+
}
|
|
347
|
+
let running = false;
|
|
348
|
+
const e = effect(() => {
|
|
349
|
+
if (running) return;
|
|
350
|
+
running = true;
|
|
351
|
+
try {
|
|
352
|
+
fn();
|
|
353
|
+
} finally {
|
|
354
|
+
running = false;
|
|
355
|
+
}
|
|
356
|
+
});
|
|
189
357
|
return () => e.dispose();
|
|
190
358
|
}
|
|
191
359
|
/**
|
|
192
360
|
* Registers a callback to run after the component is mounted.
|
|
193
|
-
*
|
|
194
|
-
* Difference from Vue: maps directly to Pyreon's `onMount()`.
|
|
195
|
-
* In Pyreon there is no distinction between beforeMount and mounted.
|
|
361
|
+
* Hook-indexed: only registered on first render.
|
|
196
362
|
*/
|
|
197
363
|
function onMounted(fn) {
|
|
198
|
-
|
|
199
|
-
|
|
364
|
+
const ctx = getCurrentCtx();
|
|
365
|
+
if (!ctx) {
|
|
366
|
+
onMount(() => {
|
|
367
|
+
fn();
|
|
368
|
+
});
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
const idx = getHookIndex();
|
|
372
|
+
if (idx < ctx.hooks.length) return;
|
|
373
|
+
ctx.hooks[idx] = true;
|
|
374
|
+
ctx.pendingEffects.push({
|
|
375
|
+
fn: () => {
|
|
376
|
+
fn();
|
|
377
|
+
},
|
|
378
|
+
deps: [],
|
|
379
|
+
cleanup: void 0
|
|
200
380
|
});
|
|
201
381
|
}
|
|
202
382
|
/**
|
|
203
|
-
* Registers a callback to run
|
|
204
|
-
*
|
|
205
|
-
* Difference from Vue: maps to Pyreon's `onUnmount()`.
|
|
206
|
-
* In Pyreon there is no distinction between beforeUnmount and unmounted.
|
|
383
|
+
* Registers a callback to run when the component is unmounted.
|
|
384
|
+
* Hook-indexed: only registered on first render.
|
|
207
385
|
*/
|
|
208
386
|
function onUnmounted(fn) {
|
|
209
|
-
|
|
387
|
+
const ctx = getCurrentCtx();
|
|
388
|
+
if (!ctx) {
|
|
389
|
+
onUnmount(fn);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
const idx = getHookIndex();
|
|
393
|
+
if (idx < ctx.hooks.length) return;
|
|
394
|
+
ctx.hooks[idx] = true;
|
|
395
|
+
ctx.unmountCallbacks.push(fn);
|
|
210
396
|
}
|
|
211
397
|
/**
|
|
212
|
-
* Registers a callback to run after a reactive update.
|
|
213
|
-
*
|
|
214
|
-
* Difference from Vue: maps to Pyreon's `onUpdate()`.
|
|
398
|
+
* Registers a callback to run after a reactive update (not on initial mount).
|
|
399
|
+
* Hook-indexed: registered once, fires on each re-render.
|
|
215
400
|
*/
|
|
216
401
|
function onUpdated(fn) {
|
|
217
|
-
|
|
402
|
+
const ctx = getCurrentCtx();
|
|
403
|
+
if (!ctx) {
|
|
404
|
+
onUpdate(fn);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
const idx = getHookIndex();
|
|
408
|
+
if (idx >= ctx.hooks.length) {
|
|
409
|
+
ctx.hooks[idx] = true;
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
ctx.pendingEffects.push({
|
|
413
|
+
fn: () => {
|
|
414
|
+
fn();
|
|
415
|
+
},
|
|
416
|
+
deps: void 0,
|
|
417
|
+
cleanup: void 0
|
|
418
|
+
});
|
|
218
419
|
}
|
|
219
420
|
/**
|
|
220
421
|
* Registers a callback to run before mount.
|
|
221
|
-
* In Pyreon there is no pre-mount phase — maps to `
|
|
422
|
+
* In Pyreon there is no pre-mount phase — maps to `onMounted()`.
|
|
222
423
|
*/
|
|
223
424
|
function onBeforeMount(fn) {
|
|
224
|
-
|
|
225
|
-
fn();
|
|
226
|
-
});
|
|
425
|
+
onMounted(fn);
|
|
227
426
|
}
|
|
228
427
|
/**
|
|
229
428
|
* Registers a callback to run before unmount.
|
|
230
|
-
* In Pyreon there is no pre-unmount phase — maps to `
|
|
429
|
+
* In Pyreon there is no pre-unmount phase — maps to `onUnmounted()`.
|
|
231
430
|
*/
|
|
232
431
|
function onBeforeUnmount(fn) {
|
|
233
|
-
|
|
432
|
+
onUnmounted(fn);
|
|
234
433
|
}
|
|
235
434
|
/**
|
|
236
435
|
* Returns a Promise that resolves after all pending reactive updates have flushed.
|
|
237
|
-
*
|
|
238
|
-
* Difference from Vue: identical to Pyreon's `nextTick()`.
|
|
239
436
|
*/
|
|
240
437
|
function nextTick() {
|
|
241
438
|
return nextTick$1();
|
|
@@ -248,19 +445,24 @@ function getOrCreateContext(key, defaultValue) {
|
|
|
248
445
|
/**
|
|
249
446
|
* Provides a value to all descendant components.
|
|
250
447
|
*
|
|
251
|
-
*
|
|
252
|
-
* Must be called during component setup. The value is scoped to the component
|
|
253
|
-
* tree — not globally shared.
|
|
448
|
+
* Inside a component: hook-indexed, pushed once. Popped on unmount.
|
|
254
449
|
*/
|
|
255
450
|
function provide(key, value) {
|
|
256
|
-
const ctx =
|
|
257
|
-
|
|
258
|
-
|
|
451
|
+
const ctx = getCurrentCtx();
|
|
452
|
+
if (ctx) {
|
|
453
|
+
const idx = getHookIndex();
|
|
454
|
+
if (idx < ctx.hooks.length) return;
|
|
455
|
+
ctx.hooks[idx] = true;
|
|
456
|
+
const vueCtx = getOrCreateContext(key);
|
|
457
|
+
pushContext(new Map([[vueCtx.id, value]]));
|
|
458
|
+
ctx.unmountCallbacks.push(() => popContext());
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
const vueCtx = getOrCreateContext(key);
|
|
462
|
+
pushContext(new Map([[vueCtx.id, value]]));
|
|
259
463
|
}
|
|
260
464
|
/**
|
|
261
465
|
* Injects a value provided by an ancestor component.
|
|
262
|
-
*
|
|
263
|
-
* Difference from Vue: backed by Pyreon's context system (useContext).
|
|
264
466
|
*/
|
|
265
467
|
function inject(key, defaultValue) {
|
|
266
468
|
const value = useContext(getOrCreateContext(key));
|
|
@@ -269,9 +471,6 @@ function inject(key, defaultValue) {
|
|
|
269
471
|
/**
|
|
270
472
|
* Defines a component using Vue 3 Composition API style.
|
|
271
473
|
* Only supports the `setup()` function — Options API is not supported.
|
|
272
|
-
*
|
|
273
|
-
* Difference from Vue: returns a Pyreon `ComponentFn`. No template/render option —
|
|
274
|
-
* the setup function should return a render function or VNode directly.
|
|
275
474
|
*/
|
|
276
475
|
function defineComponent(options) {
|
|
277
476
|
if (typeof options === "function") return options;
|
|
@@ -285,9 +484,6 @@ function defineComponent(options) {
|
|
|
285
484
|
}
|
|
286
485
|
/**
|
|
287
486
|
* Creates a Pyreon application instance — Vue 3 `createApp()` compatible.
|
|
288
|
-
*
|
|
289
|
-
* Difference from Vue: does not support plugins, directives, or global config.
|
|
290
|
-
* The component receives `props` if provided.
|
|
291
487
|
*/
|
|
292
488
|
function createApp(component, props) {
|
|
293
489
|
return { mount(el) {
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["pyreonComputed","pyreonNextTick","pyreonMount"],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @pyreon/vue-compat\n *\n * Vue 3-compatible Composition API that runs on Pyreon's reactive engine.\n *\n * Allows you to write familiar Vue 3 Composition API code while getting Pyreon's\n * fine-grained reactivity and superior performance.\n *\n * DIFFERENCES FROM VUE 3:\n * - `deep` option in watch() is ignored — Pyreon tracks dependencies automatically.\n * - `shallowReactive` uses per-property signals (still shallow, but Pyreon-flavored).\n * - `readonly` returns a Proxy that throws on set (not Vue's readonly proxy).\n * - `defineComponent` only supports Composition API (setup function), not Options API.\n * - Components run ONCE (setup phase), not on every render.\n *\n * USAGE:\n * Replace `import { ref, computed, watch } from \"vue\"` with\n * `import { ref, computed, watch } from \"@pyreon/vue-compat\"`\n */\n\nimport type { ComponentFn, Props, VNodeChild } from \"@pyreon/core\"\nimport {\n createContext,\n Fragment,\n onMount,\n onUnmount,\n onUpdate,\n popContext,\n pushContext,\n h as pyreonH,\n useContext,\n} from \"@pyreon/core\"\nimport {\n createStore,\n effect,\n computed as pyreonComputed,\n nextTick as pyreonNextTick,\n type Signal,\n signal,\n} from \"@pyreon/reactivity\"\nimport { mount as pyreonMount } from \"@pyreon/runtime-dom\"\n\n// ─── Internal symbols ─────────────────────────────────────────────────────────\n\nconst V_IS_REF = Symbol(\"__v_isRef\")\nconst V_IS_READONLY = Symbol(\"__v_isReadonly\")\nconst V_RAW = Symbol(\"__v_raw\")\n\n// ─── Ref ──────────────────────────────────────────────────────────────────────\n\nexport interface Ref<T = unknown> {\n value: T\n readonly [V_IS_REF]: true\n}\n\n/**\n * Creates a reactive ref wrapping the given value.\n * Access via `.value` — reads track, writes trigger.\n *\n * Difference from Vue: backed by a Pyreon signal. No `__v_isShallow` distinction\n * at runtime since Pyreon signals are always shallow (deep reactivity is via stores).\n */\nexport function ref<T>(value: T): Ref<T> {\n const s = signal(value)\n const r = {\n [V_IS_REF]: true as const,\n get value(): T {\n return s()\n },\n set value(newValue: T) {\n s.set(newValue)\n },\n /** @internal — access underlying signal for triggerRef */\n _signal: s,\n }\n return r as Ref<T>\n}\n\n/**\n * Creates a shallow ref — same as `ref()` in Pyreon since signals are inherently shallow.\n *\n * Difference from Vue: identical to `ref()` — Pyreon signals don't perform deep conversion.\n */\nexport function shallowRef<T>(value: T): Ref<T> {\n return ref(value)\n}\n\n/**\n * Force trigger a ref's subscribers, even if the value hasn't changed.\n */\nexport function triggerRef<T>(r: Ref<T>): void {\n const internal = r as Ref<T> & { _signal: Signal<T> }\n if (internal._signal) {\n // Force notify by setting the same value with Object.is bypass\n const current = internal._signal.peek()\n internal._signal.set(undefined as T)\n internal._signal.set(current)\n }\n}\n\n/**\n * Returns `true` if the value is a ref (created by `ref()` or `computed()`).\n */\nexport function isRef(val: unknown): val is Ref {\n return (\n val !== null && typeof val === \"object\" && (val as Record<symbol, unknown>)[V_IS_REF] === true\n )\n}\n\n/**\n * Unwraps a ref: if it has `.value`, return `.value`; otherwise return as-is.\n */\nexport function unref<T>(r: T | Ref<T>): T {\n return isRef(r) ? r.value : r\n}\n\n// ─── Computed ─────────────────────────────────────────────────────────────────\n\nexport interface ComputedRef<T = unknown> extends Ref<T> {\n readonly value: T\n}\n\nexport interface WritableComputedRef<T = unknown> extends Ref<T> {\n value: T\n}\n\n/**\n * Creates a computed ref. Supports both readonly and writable forms:\n * - `computed(() => value)` — readonly ComputedRef\n * - `computed({ get, set })` — writable WritableComputedRef\n *\n * Backed by Pyreon's `computed()`, wrapped in a `.value` accessor.\n */\nexport function computed<T>(getter: () => T): ComputedRef<T>\nexport function computed<T>(options: {\n get: () => T\n set: (value: T) => void\n}): WritableComputedRef<T>\nexport function computed<T>(\n fnOrOptions: (() => T) | { get: () => T; set: (value: T) => void },\n): ComputedRef<T> | WritableComputedRef<T> {\n const getter = typeof fnOrOptions === \"function\" ? fnOrOptions : fnOrOptions.get\n const setter = typeof fnOrOptions === \"object\" ? fnOrOptions.set : undefined\n const c = pyreonComputed(getter)\n const r = {\n [V_IS_REF]: true as const,\n get value(): T {\n return c()\n },\n set value(v: T) {\n if (!setter) {\n throw new Error(\"Cannot set value of a computed ref — computed refs are readonly\")\n }\n setter(v)\n },\n }\n return r as ComputedRef<T>\n}\n\n// ─── Reactive / Readonly ──────────────────────────────────────────────────────\n\n/**\n * Creates a deeply reactive proxy from a plain object.\n * Backed by Pyreon's `createStore()`.\n *\n * Difference from Vue: uses Pyreon's fine-grained per-property signals.\n * Direct mutation triggers only affected signals.\n */\nexport function reactive<T extends object>(obj: T): T {\n const proxy = createStore(obj)\n // Store raw reference for toRaw()\n rawMap.set(proxy as object, obj)\n return proxy\n}\n\n/**\n * Creates a shallow reactive proxy.\n * In Pyreon, `createStore` is already per-property (not deeply recursive for primitives),\n * but nested objects will be wrapped. For truly shallow behavior, use individual refs.\n *\n * Difference from Vue: backed by `createStore()` — same as `reactive()` in practice.\n */\nexport function shallowReactive<T extends object>(obj: T): T {\n return reactive(obj)\n}\n\n// WeakMap to track raw objects behind reactive proxies\nconst rawMap = new WeakMap<object, object>()\n\n/**\n * Returns a readonly proxy that throws on mutation attempts.\n *\n * Difference from Vue: uses a simple Proxy with a set trap that throws,\n * rather than Vue's full readonly reactive system.\n */\nexport function readonly<T extends object>(obj: T): Readonly<T> {\n const proxy = new Proxy(obj, {\n get(target, key) {\n if (key === V_IS_READONLY) return true\n if (key === V_RAW) return target\n return Reflect.get(target, key)\n },\n set(_target, key) {\n // Internal symbols used for identification are allowed\n if (key === V_IS_READONLY || key === V_RAW) return true\n throw new Error(`Cannot set property \"${String(key)}\" on a readonly object`)\n },\n deleteProperty(_target, key) {\n throw new Error(`Cannot delete property \"${String(key)}\" from a readonly object`)\n },\n })\n return proxy as Readonly<T>\n}\n\n/**\n * Returns the raw (unwrapped) object behind a reactive or readonly proxy.\n *\n * Difference from Vue: only works for objects created via `reactive()` or `readonly()`.\n */\nexport function toRaw<T extends object>(proxy: T): T {\n // Check readonly first\n const readonlyRaw = (proxy as Record<symbol, unknown>)[V_RAW]\n if (readonlyRaw) return readonlyRaw as T\n // Check reactive\n const raw = rawMap.get(proxy as object)\n return (raw as T) ?? proxy\n}\n\n// ─── toRef / toRefs ───────────────────────────────────────────────────────────\n\n/**\n * Creates a ref linked to a property of a reactive object.\n * Reading/writing the ref's `.value` reads/writes the original property.\n */\nexport function toRef<T extends object, K extends keyof T>(obj: T, key: K): Ref<T[K]> {\n const r = {\n [V_IS_REF]: true as const,\n get value(): T[K] {\n return obj[key]\n },\n set value(newValue: T[K]) {\n obj[key] = newValue\n },\n }\n return r as Ref<T[K]>\n}\n\n/**\n * Converts all properties of a reactive object into individual refs.\n * Each ref is linked to the original property (not a copy).\n */\nexport function toRefs<T extends object>(obj: T): { [K in keyof T]: Ref<T[K]> } {\n const result = {} as { [K in keyof T]: Ref<T[K]> }\n for (const key of Object.keys(obj) as (keyof T)[]) {\n result[key] = toRef(obj, key)\n }\n return result\n}\n\n// ─── Watch ────────────────────────────────────────────────────────────────────\n\nexport interface WatchOptions {\n /** Call the callback immediately with current value. Default: false */\n immediate?: boolean\n /** Ignored in Pyreon — dependencies are tracked automatically. */\n deep?: boolean\n}\n\ntype WatchSource<T> = Ref<T> | (() => T)\n\n/**\n * Watches a reactive source and calls `cb` when it changes.\n * Tracks old and new values.\n *\n * Difference from Vue: `deep` option is ignored — Pyreon tracks dependencies automatically.\n * Returns a stop function to dispose the watcher.\n */\nexport function watch<T>(\n source: WatchSource<T>,\n cb: (newValue: T, oldValue: T | undefined) => void,\n options?: WatchOptions,\n): () => void {\n const getter = isRef(source) ? () => source.value : (source as () => T)\n let oldValue: T | undefined\n let initialized = false\n\n if (options?.immediate) {\n oldValue = undefined\n const current = getter()\n cb(current, oldValue)\n oldValue = current\n initialized = true\n }\n\n const e = effect(() => {\n const newValue = getter()\n if (initialized) {\n // Only call cb if value actually changed (or on first tracked run)\n cb(newValue, oldValue)\n }\n oldValue = newValue\n initialized = true\n })\n\n return () => e.dispose()\n}\n\n/**\n * Runs the given function reactively — re-executes whenever its tracked\n * dependencies change.\n *\n * Difference from Vue: identical to Pyreon's `effect()`.\n * Returns a stop function.\n */\nexport function watchEffect(fn: () => void): () => void {\n const e = effect(fn)\n return () => e.dispose()\n}\n\n// ─── Lifecycle ────────────────────────────────────────────────────────────────\n\n/**\n * Registers a callback to run after the component is mounted.\n *\n * Difference from Vue: maps directly to Pyreon's `onMount()`.\n * In Pyreon there is no distinction between beforeMount and mounted.\n */\nexport function onMounted(fn: () => void): void {\n onMount(() => {\n fn()\n return undefined\n })\n}\n\n/**\n * Registers a callback to run before the component is unmounted.\n *\n * Difference from Vue: maps to Pyreon's `onUnmount()`.\n * In Pyreon there is no distinction between beforeUnmount and unmounted.\n */\nexport function onUnmounted(fn: () => void): void {\n onUnmount(fn)\n}\n\n/**\n * Registers a callback to run after a reactive update.\n *\n * Difference from Vue: maps to Pyreon's `onUpdate()`.\n */\nexport function onUpdated(fn: () => void): void {\n onUpdate(fn)\n}\n\n/**\n * Registers a callback to run before mount.\n * In Pyreon there is no pre-mount phase — maps to `onMount()`.\n */\nexport function onBeforeMount(fn: () => void): void {\n onMount(() => {\n fn()\n return undefined\n })\n}\n\n/**\n * Registers a callback to run before unmount.\n * In Pyreon there is no pre-unmount phase — maps to `onUnmount()`.\n */\nexport function onBeforeUnmount(fn: () => void): void {\n onUnmount(fn)\n}\n\n// ─── nextTick ─────────────────────────────────────────────────────────────────\n\n/**\n * Returns a Promise that resolves after all pending reactive updates have flushed.\n *\n * Difference from Vue: identical to Pyreon's `nextTick()`.\n */\nexport function nextTick(): Promise<void> {\n return pyreonNextTick()\n}\n\n// ─── Provide / Inject ─────────────────────────────────────────────────────────\n\n// Registry of string/symbol keys to Pyreon context objects (created lazily)\nconst _contextRegistry = new Map<string | symbol, ReturnType<typeof createContext>>()\n\nfunction getOrCreateContext<T>(key: string | symbol, defaultValue?: T) {\n if (!_contextRegistry.has(key)) {\n _contextRegistry.set(key, createContext<T>(defaultValue as T))\n }\n return _contextRegistry.get(key) as ReturnType<typeof createContext<T>>\n}\n\n/**\n * Provides a value to all descendant components.\n *\n * Difference from Vue: backed by Pyreon's context stack (pushContext/popContext).\n * Must be called during component setup. The value is scoped to the component\n * tree — not globally shared.\n */\nexport function provide<T>(key: string | symbol, value: T): void {\n const ctx = getOrCreateContext<T>(key)\n pushContext(new Map([[ctx.id, value]]))\n onUnmount(() => popContext())\n}\n\n/**\n * Injects a value provided by an ancestor component.\n *\n * Difference from Vue: backed by Pyreon's context system (useContext).\n */\nexport function inject<T>(key: string | symbol, defaultValue?: T): T | undefined {\n const ctx = getOrCreateContext<T>(key)\n const value = useContext(ctx)\n return value !== undefined ? value : defaultValue\n}\n\n// ─── defineComponent ──────────────────────────────────────────────────────────\n\ninterface ComponentOptions<P extends Props = Props> {\n /** The setup function — called once during component initialization. */\n setup: (props: P) => (() => VNodeChild) | VNodeChild\n /** Optional name for debugging. */\n name?: string\n}\n\n/**\n * Defines a component using Vue 3 Composition API style.\n * Only supports the `setup()` function — Options API is not supported.\n *\n * Difference from Vue: returns a Pyreon `ComponentFn`. No template/render option —\n * the setup function should return a render function or VNode directly.\n */\nexport function defineComponent<P extends Props = Props>(\n options: ComponentOptions<P> | ((props: P) => VNodeChild),\n): ComponentFn<P> {\n if (typeof options === \"function\") {\n return options as ComponentFn<P>\n }\n const comp = (props: P) => {\n const result = options.setup(props)\n if (typeof result === \"function\") {\n return (result as () => VNodeChild)()\n }\n return result\n }\n if (options.name) {\n Object.defineProperty(comp, \"name\", { value: options.name })\n }\n return comp as ComponentFn<P>\n}\n\n// ─── h ────────────────────────────────────────────────────────────────────────\n\n/**\n * Re-export of Pyreon's `h()` function for creating VNodes.\n */\nexport { Fragment, pyreonH as h }\n\n// ─── createApp ────────────────────────────────────────────────────────────────\n\ninterface App {\n /** Mount the application into a DOM element. Returns an unmount function. */\n mount(el: string | Element): () => void\n}\n\n/**\n * Creates a Pyreon application instance — Vue 3 `createApp()` compatible.\n *\n * Difference from Vue: does not support plugins, directives, or global config.\n * The component receives `props` if provided.\n */\nexport function createApp(component: ComponentFn, props?: Props): App {\n return {\n mount(el: string | Element): () => void {\n const container = typeof el === \"string\" ? document.querySelector(el) : el\n if (!container) {\n throw new Error(`Cannot find mount target: ${el}`)\n }\n const vnode = pyreonH(component, props ?? null)\n return pyreonMount(vnode, container)\n },\n }\n}\n\n// ─── Additional re-exports ────────────────────────────────────────────────────\n\nexport { batch } from \"@pyreon/reactivity\"\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["pyreonComputed","pyreonNextTick","pyreonMount"],"sources":["../src/jsx-runtime.ts","../src/index.ts"],"sourcesContent":["/**\n * Compat JSX runtime for Vue compatibility mode.\n *\n * When `jsxImportSource` is redirected to `@pyreon/vue-compat` (via the vite\n * plugin's `compat: \"vue\"` option), OXC rewrites JSX to import from this file.\n *\n * For component VNodes, we wrap the component function so it returns a reactive\n * accessor — enabling Vue-style re-renders on state change while Pyreon's\n * existing renderer handles all DOM work.\n *\n * Key difference from react/preact compat: the component body runs inside\n * `runUntracked` to prevent `.value` reads (which access underlying signals)\n * from being tracked by the reactive accessor. Only the version signal\n * triggers re-renders.\n */\n\nimport type { ComponentFn, Props, VNode, VNodeChild } from \"@pyreon/core\"\nimport { Fragment, h, onUnmount } from \"@pyreon/core\"\nimport { runUntracked, signal } from \"@pyreon/reactivity\"\n\nexport { Fragment }\n\n// ─── Render context (used by hooks) ──────────────────────────────────────────\n\nexport interface RenderContext {\n hooks: unknown[]\n scheduleRerender: () => void\n /** Effect entries pending execution after render */\n pendingEffects: EffectEntry[]\n /** Layout effect entries pending execution after render */\n pendingLayoutEffects: EffectEntry[]\n /** Set to true when the component is unmounted */\n unmounted: boolean\n /** Callbacks to run on unmount (lifecycle + effect cleanups) */\n unmountCallbacks: (() => void)[]\n}\n\nexport interface EffectEntry {\n // biome-ignore lint/suspicious/noConfusingVoidType: matches Vue's effect signature\n fn: () => (() => void) | void\n deps: unknown[] | undefined\n cleanup: (() => void) | undefined\n}\n\nlet _currentCtx: RenderContext | null = null\nlet _hookIndex = 0\n\nexport function getCurrentCtx(): RenderContext | null {\n return _currentCtx\n}\n\nexport function getHookIndex(): number {\n return _hookIndex++\n}\n\nexport function beginRender(ctx: RenderContext): void {\n _currentCtx = ctx\n _hookIndex = 0\n ctx.pendingEffects = []\n ctx.pendingLayoutEffects = []\n}\n\nexport function endRender(): void {\n _currentCtx = null\n _hookIndex = 0\n}\n\n// ─── Effect runners ──────────────────────────────────────────────────────────\n\nfunction runLayoutEffects(entries: EffectEntry[]): void {\n for (const entry of entries) {\n if (entry.cleanup) entry.cleanup()\n const cleanup = entry.fn()\n entry.cleanup = typeof cleanup === \"function\" ? cleanup : undefined\n }\n}\n\nfunction scheduleEffects(ctx: RenderContext, entries: EffectEntry[]): void {\n if (entries.length === 0) return\n queueMicrotask(() => {\n for (const entry of entries) {\n if (ctx.unmounted) return\n if (entry.cleanup) entry.cleanup()\n const cleanup = entry.fn()\n entry.cleanup = typeof cleanup === \"function\" ? cleanup : undefined\n }\n })\n}\n\n// ─── Component wrapping ──────────────────────────────────────────────────────\n\n// biome-ignore lint/complexity/noBannedTypes: Function is needed for generic component wrapping\nconst _wrapperCache = new WeakMap<Function, ComponentFn>()\n\n// biome-ignore lint/complexity/noBannedTypes: Function is needed for generic component wrapping\nfunction wrapCompatComponent(vueComponent: Function): ComponentFn {\n let wrapped = _wrapperCache.get(vueComponent)\n if (wrapped) return wrapped\n\n // The wrapper returns a reactive accessor (() => VNodeChild) which Pyreon's\n // mountChild treats as a reactive expression via mountReactive.\n wrapped = ((props: Props) => {\n const ctx: RenderContext = {\n hooks: [],\n scheduleRerender: () => {\n // Will be replaced below after version signal is created\n },\n pendingEffects: [],\n pendingLayoutEffects: [],\n unmounted: false,\n unmountCallbacks: [],\n }\n\n const version = signal(0)\n let updateScheduled = false\n\n ctx.scheduleRerender = () => {\n if (ctx.unmounted || updateScheduled) return\n updateScheduled = true\n queueMicrotask(() => {\n updateScheduled = false\n if (!ctx.unmounted) version.set(version.peek() + 1)\n })\n }\n\n // Register cleanup when component unmounts\n onUnmount(() => {\n ctx.unmounted = true\n for (const cb of ctx.unmountCallbacks) cb()\n })\n\n // Return reactive accessor — Pyreon's mountChild calls mountReactive\n return () => {\n version() // tracked read — triggers re-execution when state changes\n beginRender(ctx)\n // runUntracked prevents .value signal reads from being tracked by this accessor —\n // only the version signal should trigger re-renders\n const result = runUntracked(() => (vueComponent as ComponentFn)(props))\n const layoutEffects = ctx.pendingLayoutEffects\n const effects = ctx.pendingEffects\n endRender()\n\n runLayoutEffects(layoutEffects)\n scheduleEffects(ctx, effects)\n\n return result\n }\n }) as unknown as ComponentFn\n\n _wrapperCache.set(vueComponent, wrapped)\n return wrapped\n}\n\n// ─── JSX functions ───────────────────────────────────────────────────────────\n\nexport function jsx(\n type: string | ComponentFn | symbol,\n props: Props & { children?: VNodeChild | VNodeChild[] },\n key?: string | number | null,\n): VNode {\n const { children, ...rest } = props\n const propsWithKey = (key != null ? { ...rest, key } : rest) as Props\n\n if (typeof type === \"function\") {\n // Wrap Vue-style component for re-render support\n const wrapped = wrapCompatComponent(type)\n const componentProps = children !== undefined ? { ...propsWithKey, children } : propsWithKey\n return h(wrapped, componentProps)\n }\n\n // DOM element or symbol (Fragment): children go in vnode.children\n const childArray = children === undefined ? [] : Array.isArray(children) ? children : [children]\n\n return h(type, propsWithKey, ...(childArray as VNodeChild[]))\n}\n\nexport const jsxs = jsx\nexport const jsxDEV = jsx\n","/**\n * @pyreon/vue-compat\n *\n * Vue 3-compatible Composition API that runs on Pyreon's reactive engine,\n * using a hook-indexed re-render model.\n *\n * All stateful APIs (ref, computed, reactive, watch, lifecycle hooks, etc.)\n * use hook-indexing to persist state across re-renders. The component body\n * re-executes when state changes (driven by a version signal in the JSX\n * runtime), and hook-indexed calls return the same objects across renders.\n *\n * DIFFERENCES FROM VUE 3:\n * - `deep` option in watch() is ignored — Pyreon tracks dependencies automatically.\n * - `shallowReactive` uses per-property signals (still shallow, but Pyreon-flavored).\n * - `readonly` returns a Proxy that throws on set (not Vue's readonly proxy).\n * - `defineComponent` only supports Composition API (setup function), not Options API.\n * - Components re-execute their body on state change (hook-indexed re-render model).\n *\n * USAGE:\n * Replace `import { ref, computed, watch } from \"vue\"` with\n * `import { ref, computed, watch } from \"@pyreon/vue-compat\"`\n */\n\nimport type { ComponentFn, Props, VNodeChild } from \"@pyreon/core\"\nimport {\n createContext,\n Fragment,\n onMount,\n onUnmount,\n onUpdate,\n popContext,\n pushContext,\n h as pyreonH,\n useContext,\n} from \"@pyreon/core\"\nimport {\n createStore,\n effect,\n computed as pyreonComputed,\n nextTick as pyreonNextTick,\n type Signal,\n signal,\n} from \"@pyreon/reactivity\"\nimport { mount as pyreonMount } from \"@pyreon/runtime-dom\"\nimport { getCurrentCtx, getHookIndex } from \"./jsx-runtime\"\n\n// ─── Internal symbols ─────────────────────────────────────────────────────────\n\nconst V_IS_REF = Symbol(\"__v_isRef\")\nconst V_IS_READONLY = Symbol(\"__v_isReadonly\")\nconst V_RAW = Symbol(\"__v_raw\")\n\n// ─── Ref ──────────────────────────────────────────────────────────────────────\n\nexport interface Ref<T = unknown> {\n value: T\n readonly [V_IS_REF]: true\n}\n\n/**\n * Creates a reactive ref wrapping the given value.\n * Access via `.value` — reads track, writes trigger.\n *\n * Inside a component: hook-indexed. The setter also calls `scheduleRerender()`.\n * Outside a component: creates a standalone reactive ref.\n */\nexport function ref<T>(value: T): Ref<T> {\n const ctx = getCurrentCtx()\n if (ctx) {\n const idx = getHookIndex()\n if (idx < ctx.hooks.length) return ctx.hooks[idx] as Ref<T>\n\n const s = signal(value)\n const { scheduleRerender } = ctx\n const r = {\n [V_IS_REF]: true as const,\n get value(): T {\n return s()\n },\n set value(v: T) {\n s.set(v)\n scheduleRerender()\n },\n /** @internal — access underlying signal for triggerRef */\n _signal: s,\n _scheduleRerender: scheduleRerender,\n }\n ctx.hooks[idx] = r\n return r as Ref<T>\n }\n\n // Outside component\n const s = signal(value)\n const r = {\n [V_IS_REF]: true as const,\n get value(): T {\n return s()\n },\n set value(v: T) {\n s.set(v)\n },\n /** @internal — access underlying signal for triggerRef */\n _signal: s,\n }\n return r as Ref<T>\n}\n\n/**\n * Creates a shallow ref — same as `ref()` in Pyreon since signals are inherently shallow.\n */\nexport function shallowRef<T>(value: T): Ref<T> {\n return ref(value)\n}\n\n/**\n * Force trigger a ref's subscribers, even if the value hasn't changed.\n */\nexport function triggerRef<T>(r: Ref<T>): void {\n const internal = r as Ref<T> & { _signal: Signal<T>; _scheduleRerender?: () => void }\n if (internal._signal) {\n // Force notify by setting the same value with Object.is bypass\n const current = internal._signal.peek()\n internal._signal.set(undefined as T)\n internal._signal.set(current)\n }\n if (internal._scheduleRerender) {\n internal._scheduleRerender()\n }\n}\n\n/**\n * Returns `true` if the value is a ref (created by `ref()` or `computed()`).\n */\nexport function isRef(val: unknown): val is Ref {\n return (\n val !== null && typeof val === \"object\" && (val as Record<symbol, unknown>)[V_IS_REF] === true\n )\n}\n\n/**\n * Unwraps a ref: if it has `.value`, return `.value`; otherwise return as-is.\n */\nexport function unref<T>(r: T | Ref<T>): T {\n return isRef(r) ? r.value : r\n}\n\n// ─── Computed ─────────────────────────────────────────────────────────────────\n\nexport interface ComputedRef<T = unknown> extends Ref<T> {\n readonly value: T\n}\n\nexport interface WritableComputedRef<T = unknown> extends Ref<T> {\n value: T\n}\n\n/**\n * Creates a computed ref. Supports both readonly and writable forms.\n *\n * Inside a component: hook-indexed.\n */\nexport function computed<T>(getter: () => T): ComputedRef<T>\nexport function computed<T>(options: {\n get: () => T\n set: (value: T) => void\n}): WritableComputedRef<T>\nexport function computed<T>(\n fnOrOptions: (() => T) | { get: () => T; set: (value: T) => void },\n): ComputedRef<T> | WritableComputedRef<T> {\n const ctx = getCurrentCtx()\n if (ctx) {\n const idx = getHookIndex()\n if (idx < ctx.hooks.length) return ctx.hooks[idx] as ComputedRef<T>\n\n const getter = typeof fnOrOptions === \"function\" ? fnOrOptions : fnOrOptions.get\n const setter = typeof fnOrOptions === \"object\" ? fnOrOptions.set : undefined\n const c = pyreonComputed(getter)\n const { scheduleRerender } = ctx\n const r = {\n [V_IS_REF]: true as const,\n get value(): T {\n return c()\n },\n set value(v: T) {\n if (!setter) {\n throw new Error(\"Cannot set value of a computed ref — computed refs are readonly\")\n }\n setter(v)\n scheduleRerender()\n },\n }\n ctx.hooks[idx] = r\n return r as ComputedRef<T>\n }\n\n // Outside component\n const getter = typeof fnOrOptions === \"function\" ? fnOrOptions : fnOrOptions.get\n const setter = typeof fnOrOptions === \"object\" ? fnOrOptions.set : undefined\n const c = pyreonComputed(getter)\n const r = {\n [V_IS_REF]: true as const,\n get value(): T {\n return c()\n },\n set value(v: T) {\n if (!setter) {\n throw new Error(\"Cannot set value of a computed ref — computed refs are readonly\")\n }\n setter(v)\n },\n }\n return r as ComputedRef<T>\n}\n\n// ─── Reactive / Readonly ──────────────────────────────────────────────────────\n\n// WeakMap to track raw objects behind reactive proxies\nconst rawMap = new WeakMap<object, object>()\n\n/**\n * Creates a deeply reactive proxy from a plain object.\n * Backed by Pyreon's `createStore()`.\n *\n * Inside a component: hook-indexed. Proxy wrapper intercepts sets to\n * call `scheduleRerender()`.\n */\nexport function reactive<T extends object>(obj: T): T {\n const ctx = getCurrentCtx()\n if (ctx) {\n const idx = getHookIndex()\n if (idx < ctx.hooks.length) return ctx.hooks[idx] as T\n\n const proxy = createStore(obj)\n rawMap.set(proxy as object, obj)\n const { scheduleRerender } = ctx\n const wrapped = new Proxy(proxy, {\n set(target, key, value, receiver) {\n const result = Reflect.set(target, key, value, receiver)\n scheduleRerender()\n return result\n },\n deleteProperty(target, key) {\n const result = Reflect.deleteProperty(target, key)\n scheduleRerender()\n return result\n },\n })\n rawMap.set(wrapped as object, obj)\n ctx.hooks[idx] = wrapped\n return wrapped as T\n }\n\n // Outside component\n const proxy = createStore(obj)\n rawMap.set(proxy as object, obj)\n return proxy\n}\n\n/**\n * Creates a shallow reactive proxy — same as `reactive()` in Pyreon.\n */\nexport function shallowReactive<T extends object>(obj: T): T {\n return reactive(obj)\n}\n\n/**\n * Returns a readonly proxy that throws on mutation attempts.\n *\n * Inside a component: hook-indexed.\n */\nexport function readonly<T extends object>(obj: T): Readonly<T> {\n const ctx = getCurrentCtx()\n if (ctx) {\n const idx = getHookIndex()\n if (idx < ctx.hooks.length) return ctx.hooks[idx] as Readonly<T>\n\n const proxy = _createReadonlyProxy(obj)\n ctx.hooks[idx] = proxy\n return proxy\n }\n\n return _createReadonlyProxy(obj)\n}\n\nfunction _createReadonlyProxy<T extends object>(obj: T): Readonly<T> {\n const proxy = new Proxy(obj, {\n get(target, key) {\n if (key === V_IS_READONLY) return true\n if (key === V_RAW) return target\n return Reflect.get(target, key)\n },\n set(_target, key) {\n // Internal symbols used for identification are allowed\n if (key === V_IS_READONLY || key === V_RAW) return true\n throw new Error(`Cannot set property \"${String(key)}\" on a readonly object`)\n },\n deleteProperty(_target, key) {\n throw new Error(`Cannot delete property \"${String(key)}\" from a readonly object`)\n },\n })\n return proxy as Readonly<T>\n}\n\n/**\n * Returns the raw (unwrapped) object behind a reactive or readonly proxy.\n */\nexport function toRaw<T extends object>(proxy: T): T {\n // Check readonly first\n const readonlyRaw = (proxy as Record<symbol, unknown>)[V_RAW]\n if (readonlyRaw) return readonlyRaw as T\n // Check reactive\n const raw = rawMap.get(proxy as object)\n return (raw as T) ?? proxy\n}\n\n// ─── toRef / toRefs ───────────────────────────────────────────────────────────\n\n/**\n * Creates a ref linked to a property of a reactive object.\n * Reading/writing the ref's `.value` reads/writes the original property.\n *\n * Inside a component: hook-indexed.\n */\nexport function toRef<T extends object, K extends keyof T>(obj: T, key: K): Ref<T[K]> {\n const ctx = getCurrentCtx()\n if (ctx) {\n const idx = getHookIndex()\n if (idx < ctx.hooks.length) return ctx.hooks[idx] as Ref<T[K]>\n\n const r = _createToRef(obj, key)\n ctx.hooks[idx] = r\n return r\n }\n\n return _createToRef(obj, key)\n}\n\nfunction _createToRef<T extends object, K extends keyof T>(obj: T, key: K): Ref<T[K]> {\n const r = {\n [V_IS_REF]: true as const,\n get value(): T[K] {\n return obj[key]\n },\n set value(newValue: T[K]) {\n obj[key] = newValue\n },\n }\n return r as Ref<T[K]>\n}\n\n/**\n * Converts all properties of a reactive object into individual refs.\n * Each ref is linked to the original property (not a copy).\n *\n * Inside a component: hook-indexed (the entire result, not individual refs).\n */\nexport function toRefs<T extends object>(obj: T): { [K in keyof T]: Ref<T[K]> } {\n const ctx = getCurrentCtx()\n if (ctx) {\n const idx = getHookIndex()\n if (idx < ctx.hooks.length) return ctx.hooks[idx] as { [K in keyof T]: Ref<T[K]> }\n\n const result = {} as { [K in keyof T]: Ref<T[K]> }\n for (const key of Object.keys(obj) as (keyof T)[]) {\n // Create refs directly (not via exported toRef) to avoid extra hook index consumption\n result[key] = _createToRef(obj, key)\n }\n ctx.hooks[idx] = result\n return result\n }\n\n const result = {} as { [K in keyof T]: Ref<T[K]> }\n for (const key of Object.keys(obj) as (keyof T)[]) {\n result[key] = _createToRef(obj, key)\n }\n return result\n}\n\n// ─── Watch ────────────────────────────────────────────────────────────────────\n\nexport interface WatchOptions {\n /** Call the callback immediately with current value. Default: false */\n immediate?: boolean\n /** Ignored in Pyreon — dependencies are tracked automatically. */\n deep?: boolean\n}\n\ntype WatchSource<T> = Ref<T> | (() => T)\n\n/**\n * Watches a reactive source and calls `cb` when it changes.\n *\n * Inside a component: hook-indexed, created once. Disposed on unmount.\n */\nexport function watch<T>(\n source: WatchSource<T>,\n cb: (newValue: T, oldValue: T | undefined) => void,\n options?: WatchOptions,\n): () => void {\n const ctx = getCurrentCtx()\n if (ctx) {\n const idx = getHookIndex()\n if (idx < ctx.hooks.length) return ctx.hooks[idx] as () => void\n\n const getter = isRef(source) ? () => source.value : (source as () => T)\n let oldValue: T | undefined\n let initialized = false\n\n if (options?.immediate) {\n oldValue = undefined\n const current = getter()\n cb(current, oldValue)\n oldValue = current\n initialized = true\n }\n\n let running = false\n const e = effect(() => {\n if (running) return\n running = true\n try {\n const newValue = getter()\n if (initialized) {\n cb(newValue, oldValue)\n }\n oldValue = newValue\n initialized = true\n } finally {\n running = false\n }\n })\n\n const stop = () => e.dispose()\n ctx.hooks[idx] = stop\n ctx.unmountCallbacks.push(stop)\n return stop\n }\n\n // Outside component\n const getter = isRef(source) ? () => source.value : (source as () => T)\n let oldValue: T | undefined\n let initialized = false\n\n if (options?.immediate) {\n oldValue = undefined\n const current = getter()\n cb(current, oldValue)\n oldValue = current\n initialized = true\n }\n\n let running = false\n const e = effect(() => {\n if (running) return\n running = true\n try {\n const newValue = getter()\n if (initialized) {\n cb(newValue, oldValue)\n }\n oldValue = newValue\n initialized = true\n } finally {\n running = false\n }\n })\n\n return () => e.dispose()\n}\n\n/**\n * Runs the given function reactively — re-executes whenever its tracked\n * dependencies change.\n *\n * Inside a component: hook-indexed, created once. Disposed on unmount.\n */\nexport function watchEffect(fn: () => void): () => void {\n const ctx = getCurrentCtx()\n if (ctx) {\n const idx = getHookIndex()\n if (idx < ctx.hooks.length) return ctx.hooks[idx] as () => void\n\n let running = false\n const e = effect(() => {\n if (running) return\n running = true\n try {\n fn()\n } finally {\n running = false\n }\n })\n const stop = () => e.dispose()\n ctx.hooks[idx] = stop\n ctx.unmountCallbacks.push(stop)\n return stop\n }\n\n let running = false\n const e = effect(() => {\n if (running) return\n running = true\n try {\n fn()\n } finally {\n running = false\n }\n })\n return () => e.dispose()\n}\n\n// ─── Lifecycle ────────────────────────────────────────────────────────────────\n\n/**\n * Registers a callback to run after the component is mounted.\n * Hook-indexed: only registered on first render.\n */\nexport function onMounted(fn: () => void): void {\n const ctx = getCurrentCtx()\n if (!ctx) {\n // Fallback: use Pyreon's lifecycle directly (e.g., inside defineComponent without jsx-runtime)\n onMount(() => {\n fn()\n return undefined\n })\n return\n }\n const idx = getHookIndex()\n if (idx < ctx.hooks.length) return // Already registered\n ctx.hooks[idx] = true\n // Schedule to run after render via microtask\n ctx.pendingEffects.push({\n fn: () => {\n fn()\n return undefined\n },\n deps: [],\n cleanup: undefined,\n })\n}\n\n/**\n * Registers a callback to run when the component is unmounted.\n * Hook-indexed: only registered on first render.\n */\nexport function onUnmounted(fn: () => void): void {\n const ctx = getCurrentCtx()\n if (!ctx) {\n onUnmount(fn)\n return\n }\n const idx = getHookIndex()\n if (idx < ctx.hooks.length) return // Already registered\n ctx.hooks[idx] = true\n ctx.unmountCallbacks.push(fn)\n}\n\n/**\n * Registers a callback to run after a reactive update (not on initial mount).\n * Hook-indexed: registered once, fires on each re-render.\n */\nexport function onUpdated(fn: () => void): void {\n const ctx = getCurrentCtx()\n if (!ctx) {\n onUpdate(fn)\n return\n }\n const idx = getHookIndex()\n if (idx >= ctx.hooks.length) {\n // First render — just mark as registered, don't fire\n ctx.hooks[idx] = true\n return\n }\n // Re-render — schedule the callback\n ctx.pendingEffects.push({\n fn: () => {\n fn()\n return undefined\n },\n deps: undefined,\n cleanup: undefined,\n })\n}\n\n/**\n * Registers a callback to run before mount.\n * In Pyreon there is no pre-mount phase — maps to `onMounted()`.\n */\nexport function onBeforeMount(fn: () => void): void {\n onMounted(fn)\n}\n\n/**\n * Registers a callback to run before unmount.\n * In Pyreon there is no pre-unmount phase — maps to `onUnmounted()`.\n */\nexport function onBeforeUnmount(fn: () => void): void {\n onUnmounted(fn)\n}\n\n// ─── nextTick ─────────────────────────────────────────────────────────────────\n\n/**\n * Returns a Promise that resolves after all pending reactive updates have flushed.\n */\nexport function nextTick(): Promise<void> {\n return pyreonNextTick()\n}\n\n// ─── Provide / Inject ─────────────────────────────────────────────────────────\n\n// Registry of string/symbol keys to Pyreon context objects (created lazily)\nconst _contextRegistry = new Map<string | symbol, ReturnType<typeof createContext>>()\n\nfunction getOrCreateContext<T>(key: string | symbol, defaultValue?: T) {\n if (!_contextRegistry.has(key)) {\n _contextRegistry.set(key, createContext<T>(defaultValue as T))\n }\n return _contextRegistry.get(key) as ReturnType<typeof createContext<T>>\n}\n\n/**\n * Provides a value to all descendant components.\n *\n * Inside a component: hook-indexed, pushed once. Popped on unmount.\n */\nexport function provide<T>(key: string | symbol, value: T): void {\n const ctx = getCurrentCtx()\n if (ctx) {\n const idx = getHookIndex()\n if (idx < ctx.hooks.length) return // Already provided\n ctx.hooks[idx] = true\n const vueCtx = getOrCreateContext<T>(key)\n pushContext(new Map([[vueCtx.id, value]]))\n ctx.unmountCallbacks.push(() => popContext())\n return\n }\n // Outside component — use Pyreon's provide directly\n const vueCtx = getOrCreateContext<T>(key)\n pushContext(new Map([[vueCtx.id, value]]))\n}\n\n/**\n * Injects a value provided by an ancestor component.\n */\nexport function inject<T>(key: string | symbol, defaultValue?: T): T | undefined {\n const ctx = getOrCreateContext<T>(key)\n const value = useContext(ctx)\n return value !== undefined ? value : defaultValue\n}\n\n// ─── defineComponent ──────────────────────────────────────────────────────────\n\ninterface ComponentOptions<P extends Props = Props> {\n /** The setup function — called once during component initialization. */\n setup: (props: P) => (() => VNodeChild) | VNodeChild\n /** Optional name for debugging. */\n name?: string\n}\n\n/**\n * Defines a component using Vue 3 Composition API style.\n * Only supports the `setup()` function — Options API is not supported.\n */\nexport function defineComponent<P extends Props = Props>(\n options: ComponentOptions<P> | ((props: P) => VNodeChild),\n): ComponentFn<P> {\n if (typeof options === \"function\") {\n return options as ComponentFn<P>\n }\n const comp = (props: P) => {\n const result = options.setup(props)\n if (typeof result === \"function\") {\n return (result as () => VNodeChild)()\n }\n return result\n }\n if (options.name) {\n Object.defineProperty(comp, \"name\", { value: options.name })\n }\n return comp as ComponentFn<P>\n}\n\n// ─── h ────────────────────────────────────────────────────────────────────────\n\n/**\n * Re-export of Pyreon's `h()` function for creating VNodes.\n */\nexport { Fragment, pyreonH as h }\n\n// ─── createApp ────────────────────────────────────────────────────────────────\n\ninterface App {\n /** Mount the application into a DOM element. Returns an unmount function. */\n mount(el: string | Element): () => void\n}\n\n/**\n * Creates a Pyreon application instance — Vue 3 `createApp()` compatible.\n */\nexport function createApp(component: ComponentFn, props?: Props): App {\n return {\n mount(el: string | Element): () => void {\n const container = typeof el === \"string\" ? document.querySelector(el) : el\n if (!container) {\n throw new Error(`Cannot find mount target: ${el}`)\n }\n const vnode = pyreonH(component, props ?? null)\n return pyreonMount(vnode, container)\n },\n }\n}\n\n// ─── Additional re-exports ────────────────────────────────────────────────────\n\nexport { batch } from \"@pyreon/reactivity\"\n"],"mappings":"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"}
|