@pyreon/react-compat 0.13.1 → 0.14.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/analysis/jsx-runtime.js.html +1 -1
- package/lib/index.js +356 -40
- package/lib/index.js.map +1 -1
- package/lib/jsx-runtime.js +57 -5
- package/lib/jsx-runtime.js.map +1 -1
- package/lib/types/index.d.ts +205 -4
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/jsx-runtime.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +551 -52
- package/src/jsx-runtime.ts +90 -2
- package/src/tests/compat-integration.test.tsx +1 -0
- package/src/tests/new-apis.test.ts +1519 -0
- package/src/tests/react-compat.test.ts +2 -0
|
@@ -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","children":[{"uid":"
|
|
5389
|
+
const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src","children":[{"uid":"671d1924-1","name":"jsx-runtime.ts"},{"uid":"671d1924-3","name":"index.ts"}]}]}],"isRoot":true},"nodeParts":{"671d1924-1":{"renderedLength":186,"gzipLength":138,"brotliLength":0,"metaUid":"671d1924-0"},"671d1924-3":{"renderedLength":16429,"gzipLength":4587,"brotliLength":0,"metaUid":"671d1924-2"}},"nodeMetas":{"671d1924-0":{"id":"/src/jsx-runtime.ts","moduleParts":{"index.js":"671d1924-1"},"imported":[{"uid":"671d1924-4"},{"uid":"671d1924-5"}],"importedBy":[{"uid":"671d1924-2"}]},"671d1924-2":{"id":"/src/index.ts","moduleParts":{"index.js":"671d1924-3"},"imported":[{"uid":"671d1924-4"},{"uid":"671d1924-5"},{"uid":"671d1924-0"}],"importedBy":[],"isEntry":true},"671d1924-4":{"id":"@pyreon/core","moduleParts":{},"imported":[],"importedBy":[{"uid":"671d1924-2"},{"uid":"671d1924-0"}]},"671d1924-5":{"id":"@pyreon/reactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"671d1924-2"},{"uid":"671d1924-0"}]}},"env":{"rollup":"4.23.0"},"options":{"gzip":true,"brotli":false,"sourcemap":false}};
|
|
5390
5390
|
|
|
5391
5391
|
const run = () => {
|
|
5392
5392
|
const width = window.innerWidth;
|
|
@@ -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":"jsx-runtime.js","children":[{"name":"src","children":[{"uid":"
|
|
5389
|
+
const data = {"version":2,"tree":{"name":"root","children":[{"name":"jsx-runtime.js","children":[{"name":"src","children":[{"uid":"ab213c8a-1","name":"jsx-runtime.ts"},{"uid":"ab213c8a-3","name":"jsx-dev-runtime.ts"}]}]}],"isRoot":true},"nodeParts":{"ab213c8a-1":{"renderedLength":4797,"gzipLength":1465,"brotliLength":0,"metaUid":"ab213c8a-0"},"ab213c8a-3":{"renderedLength":0,"gzipLength":0,"brotliLength":0,"metaUid":"ab213c8a-2"}},"nodeMetas":{"ab213c8a-0":{"id":"/src/jsx-runtime.ts","moduleParts":{"jsx-runtime.js":"ab213c8a-1"},"imported":[{"uid":"ab213c8a-4"},{"uid":"ab213c8a-5"}],"importedBy":[{"uid":"ab213c8a-2"}]},"ab213c8a-2":{"id":"/src/jsx-dev-runtime.ts","moduleParts":{"jsx-runtime.js":"ab213c8a-3"},"imported":[{"uid":"ab213c8a-0"}],"importedBy":[],"isEntry":true},"ab213c8a-4":{"id":"@pyreon/core","moduleParts":{},"imported":[],"importedBy":[{"uid":"ab213c8a-0"}]},"ab213c8a-5":{"id":"@pyreon/reactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"ab213c8a-0"}]}},"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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ErrorBoundary, Fragment, Portal, Suspense, createContext, h, h as createElement, h as h$1, lazy, useContext } from "@pyreon/core";
|
|
1
|
+
import { ErrorBoundary, Fragment, Portal, Suspense, createContext as createContext$1, createRef, h, h as createElement, h as h$1, lazy, provide, useContext as useContext$1 } from "@pyreon/core";
|
|
2
2
|
import { batch } from "@pyreon/reactivity";
|
|
3
3
|
|
|
4
4
|
//#region src/jsx-runtime.ts
|
|
@@ -31,33 +31,50 @@ function depsChanged(a, b) {
|
|
|
31
31
|
function useState(initial) {
|
|
32
32
|
const ctx = requireCtx();
|
|
33
33
|
const idx = getHookIndex();
|
|
34
|
-
if (ctx.hooks.length <= idx)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
if (ctx.hooks.length <= idx) {
|
|
35
|
+
const entry = {
|
|
36
|
+
value: typeof initial === "function" ? initial() : initial,
|
|
37
|
+
setter: null
|
|
38
|
+
};
|
|
39
|
+
entry.setter = (v) => {
|
|
40
|
+
const current = entry.value;
|
|
41
|
+
const next = typeof v === "function" ? v(current) : v;
|
|
42
|
+
if (Object.is(current, next)) return;
|
|
43
|
+
entry.value = next;
|
|
44
|
+
ctx.scheduleRerender();
|
|
45
|
+
};
|
|
46
|
+
ctx.hooks.push(entry);
|
|
47
|
+
}
|
|
48
|
+
const entry = ctx.hooks[idx];
|
|
49
|
+
return [entry.value, entry.setter];
|
|
44
50
|
}
|
|
45
51
|
/**
|
|
46
52
|
* React-compatible `useReducer` — returns `[state, dispatch]`.
|
|
53
|
+
* Supports the 3-argument form: `useReducer(reducer, initialArg, init)`.
|
|
47
54
|
*/
|
|
48
|
-
function useReducer(reducer,
|
|
55
|
+
function useReducer(reducer, initialArg, init) {
|
|
49
56
|
const ctx = requireCtx();
|
|
50
57
|
const idx = getHookIndex();
|
|
51
|
-
if (ctx.hooks.length <= idx)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
if (ctx.hooks.length <= idx) {
|
|
59
|
+
let initial;
|
|
60
|
+
if (init) initial = init(initialArg);
|
|
61
|
+
else if (typeof initialArg === "function") initial = initialArg();
|
|
62
|
+
else initial = initialArg;
|
|
63
|
+
const entry = {
|
|
64
|
+
value: initial,
|
|
65
|
+
dispatch: null
|
|
66
|
+
};
|
|
67
|
+
entry.dispatch = (action) => {
|
|
68
|
+
const current = entry.value;
|
|
69
|
+
const next = reducer(current, action);
|
|
70
|
+
if (Object.is(current, next)) return;
|
|
71
|
+
entry.value = next;
|
|
72
|
+
ctx.scheduleRerender();
|
|
73
|
+
};
|
|
74
|
+
ctx.hooks.push(entry);
|
|
75
|
+
}
|
|
76
|
+
const entry = ctx.hooks[idx];
|
|
77
|
+
return [entry.value, entry.dispatch];
|
|
61
78
|
}
|
|
62
79
|
/**
|
|
63
80
|
* React-compatible `useEffect` — runs after render when deps change.
|
|
@@ -107,6 +124,30 @@ function useLayoutEffect(fn, deps) {
|
|
|
107
124
|
}
|
|
108
125
|
}
|
|
109
126
|
/**
|
|
127
|
+
* React-compatible `useInsertionEffect` — runs synchronously before layout effects.
|
|
128
|
+
* Intended for CSS-in-JS libraries to inject styles before DOM reads.
|
|
129
|
+
*/
|
|
130
|
+
function useInsertionEffect(fn, deps) {
|
|
131
|
+
const ctx = requireCtx();
|
|
132
|
+
const idx = getHookIndex();
|
|
133
|
+
if (ctx.hooks.length <= idx) {
|
|
134
|
+
const entry = {
|
|
135
|
+
fn,
|
|
136
|
+
deps,
|
|
137
|
+
cleanup: void 0
|
|
138
|
+
};
|
|
139
|
+
ctx.hooks.push(entry);
|
|
140
|
+
ctx.pendingInsertionEffects.push(entry);
|
|
141
|
+
} else {
|
|
142
|
+
const entry = ctx.hooks[idx];
|
|
143
|
+
if (depsChanged(entry.deps, deps)) {
|
|
144
|
+
entry.fn = fn;
|
|
145
|
+
entry.deps = deps;
|
|
146
|
+
ctx.pendingInsertionEffects.push(entry);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
110
151
|
* React-compatible `useMemo` — returns the cached value, recomputed when deps change.
|
|
111
152
|
*/
|
|
112
153
|
function useMemo(fn, deps) {
|
|
@@ -145,6 +186,70 @@ function useRef(initial) {
|
|
|
145
186
|
}
|
|
146
187
|
return ctx.hooks[idx];
|
|
147
188
|
}
|
|
189
|
+
const COMPAT_CTX = Symbol.for("pyreon:compat-ctx");
|
|
190
|
+
const COMPAT_CTX_BRAND = COMPAT_CTX;
|
|
191
|
+
const NATIVE_COMPONENT = Symbol.for("pyreon:native-compat");
|
|
192
|
+
/**
|
|
193
|
+
* React-compatible `createContext` — creates a context with a Provider that
|
|
194
|
+
* supports nested Providers (inner overrides outer for its subtree) and
|
|
195
|
+
* notifies all `useContext` consumers when its value changes.
|
|
196
|
+
*/
|
|
197
|
+
function createContext(defaultValue) {
|
|
198
|
+
const pyreonCtx = createContext$1({
|
|
199
|
+
value: defaultValue,
|
|
200
|
+
subscribers: /* @__PURE__ */ new Set()
|
|
201
|
+
});
|
|
202
|
+
const defaultSubscribers = /* @__PURE__ */ new Set();
|
|
203
|
+
const Provider = (props) => {
|
|
204
|
+
const frame = {
|
|
205
|
+
value: props.value,
|
|
206
|
+
subscribers: /* @__PURE__ */ new Set()
|
|
207
|
+
};
|
|
208
|
+
provide(pyreonCtx, frame);
|
|
209
|
+
return () => {
|
|
210
|
+
const { value, children } = props;
|
|
211
|
+
if (!Object.is(frame.value, value)) {
|
|
212
|
+
frame.value = value;
|
|
213
|
+
for (const sub of frame.subscribers) sub();
|
|
214
|
+
}
|
|
215
|
+
return children ?? null;
|
|
216
|
+
};
|
|
217
|
+
};
|
|
218
|
+
Provider[NATIVE_COMPONENT] = true;
|
|
219
|
+
return {
|
|
220
|
+
[COMPAT_CTX_BRAND]: true,
|
|
221
|
+
_defaultValue: defaultValue,
|
|
222
|
+
_pyreonCtx: pyreonCtx,
|
|
223
|
+
_subscribers: defaultSubscribers,
|
|
224
|
+
Provider
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* React-compatible `useContext` — reads the current context value and
|
|
229
|
+
* subscribes the calling component to future value changes.
|
|
230
|
+
*
|
|
231
|
+
* Reads from Pyreon's tree-scoped context stack (correct nesting) and
|
|
232
|
+
* subscribes to the nearest Provider's subscriber set for re-rendering.
|
|
233
|
+
*
|
|
234
|
+
* Works with both compat contexts (from this module's `createContext`) and
|
|
235
|
+
* Pyreon native contexts (from `@pyreon/core`).
|
|
236
|
+
*/
|
|
237
|
+
function useContext(context) {
|
|
238
|
+
if (COMPAT_CTX in context) {
|
|
239
|
+
const frame = useContext$1(context._pyreonCtx);
|
|
240
|
+
const renderCtx = getCurrentCtx();
|
|
241
|
+
if (renderCtx) {
|
|
242
|
+
const idx = getHookIndex();
|
|
243
|
+
if (renderCtx.hooks.length <= idx) {
|
|
244
|
+
const sub = () => renderCtx.scheduleRerender();
|
|
245
|
+
frame.subscribers.add(sub);
|
|
246
|
+
renderCtx.hooks.push({ _contextUnsub: () => frame.subscribers.delete(sub) });
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return frame.value;
|
|
250
|
+
}
|
|
251
|
+
return useContext$1(context);
|
|
252
|
+
}
|
|
148
253
|
let _idCounter = 0;
|
|
149
254
|
/**
|
|
150
255
|
* React-compatible `useId` — returns a stable unique string per hook call.
|
|
@@ -155,26 +260,47 @@ function useId() {
|
|
|
155
260
|
if (ctx.hooks.length <= idx) ctx.hooks.push(`:r${(_idCounter++).toString(36)}:`);
|
|
156
261
|
return ctx.hooks[idx];
|
|
157
262
|
}
|
|
263
|
+
function shallowEqual(a, b) {
|
|
264
|
+
const keysA = Object.keys(a);
|
|
265
|
+
const keysB = Object.keys(b);
|
|
266
|
+
if (keysA.length !== keysB.length) return false;
|
|
267
|
+
for (const k of keysA) if (!Object.is(a[k], b[k])) return false;
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
158
270
|
/**
|
|
159
271
|
* React-compatible `memo` — wraps a component to skip re-render when props
|
|
160
272
|
* are shallowly equal.
|
|
273
|
+
*
|
|
274
|
+
* Each component INSTANCE gets its own props/result cache via a hook slot,
|
|
275
|
+
* so two `<MemoComp />` usages don't share memoization state.
|
|
161
276
|
*/
|
|
162
277
|
function memo(component, areEqual) {
|
|
163
|
-
const compare = areEqual ??
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
278
|
+
const compare = areEqual ?? shallowEqual;
|
|
279
|
+
const MEMO_MARKER = Symbol.for("pyreon:memo");
|
|
280
|
+
let _fallbackPrevProps = null;
|
|
281
|
+
let _fallbackPrevResult = null;
|
|
282
|
+
const memoized = (props) => {
|
|
283
|
+
const ctx = getCurrentCtx();
|
|
284
|
+
if (ctx) {
|
|
285
|
+
const idx = getHookIndex();
|
|
286
|
+
if (ctx.hooks.length <= idx) ctx.hooks.push({
|
|
287
|
+
prevProps: null,
|
|
288
|
+
prevResult: null
|
|
289
|
+
});
|
|
290
|
+
const cache = ctx.hooks[idx];
|
|
291
|
+
if (cache.prevProps !== null && compare(cache.prevProps, props)) return cache.prevResult;
|
|
292
|
+
cache.prevProps = props;
|
|
293
|
+
cache.prevResult = component(props);
|
|
294
|
+
return cache.prevResult;
|
|
295
|
+
}
|
|
296
|
+
if (_fallbackPrevProps !== null && compare(_fallbackPrevProps, props)) return _fallbackPrevResult;
|
|
297
|
+
_fallbackPrevProps = props;
|
|
298
|
+
_fallbackPrevResult = component(props);
|
|
299
|
+
return _fallbackPrevResult;
|
|
177
300
|
};
|
|
301
|
+
memoized[MEMO_MARKER] = true;
|
|
302
|
+
memoized.displayName = component.displayName || component.name || "Memo";
|
|
303
|
+
return memoized;
|
|
178
304
|
}
|
|
179
305
|
/**
|
|
180
306
|
* React-compatible `useTransition` — no concurrent mode in Pyreon.
|
|
@@ -214,10 +340,12 @@ function createPortal(children, target) {
|
|
|
214
340
|
* The render function receives (props, ref) — we merge ref into props.
|
|
215
341
|
*/
|
|
216
342
|
function forwardRef(render) {
|
|
217
|
-
|
|
343
|
+
const forwarded = (props) => {
|
|
218
344
|
const { ref, ...rest } = props;
|
|
219
345
|
return render(rest, ref ?? null);
|
|
220
346
|
};
|
|
347
|
+
forwarded.displayName = render.displayName || render.name || "ForwardRef";
|
|
348
|
+
return forwarded;
|
|
221
349
|
}
|
|
222
350
|
/**
|
|
223
351
|
* React-compatible `cloneElement` — creates a new VNode with merged props.
|
|
@@ -245,19 +373,27 @@ const Children = {
|
|
|
245
373
|
map(children, fn) {
|
|
246
374
|
const flat = flattenChildren(children);
|
|
247
375
|
const result = [];
|
|
376
|
+
let validIndex = 0;
|
|
248
377
|
for (let i = 0; i < flat.length; i++) {
|
|
249
378
|
const child = flat[i];
|
|
250
379
|
if (child == null || child === true || child === false) continue;
|
|
251
|
-
|
|
380
|
+
const mapped = fn(child, validIndex);
|
|
381
|
+
if (mapped && typeof mapped === "object" && "type" in mapped && "props" in mapped) {
|
|
382
|
+
const vnode = mapped;
|
|
383
|
+
if (vnode.key == null) vnode.key = `.${validIndex}`;
|
|
384
|
+
}
|
|
385
|
+
result.push(mapped);
|
|
386
|
+
validIndex++;
|
|
252
387
|
}
|
|
253
388
|
return result;
|
|
254
389
|
},
|
|
255
390
|
forEach(children, fn) {
|
|
256
391
|
const flat = flattenChildren(children);
|
|
392
|
+
let validIndex = 0;
|
|
257
393
|
for (let i = 0; i < flat.length; i++) {
|
|
258
394
|
const child = flat[i];
|
|
259
395
|
if (child == null || child === true || child === false) continue;
|
|
260
|
-
fn(child,
|
|
396
|
+
fn(child, validIndex++);
|
|
261
397
|
}
|
|
262
398
|
},
|
|
263
399
|
count(children) {
|
|
@@ -275,7 +411,187 @@ const Children = {
|
|
|
275
411
|
return arr[0];
|
|
276
412
|
}
|
|
277
413
|
};
|
|
414
|
+
/**
|
|
415
|
+
* React-compatible `useSyncExternalStore` — subscribes to an external store.
|
|
416
|
+
* Re-subscribes automatically when the `subscribe` function identity changes.
|
|
417
|
+
*/
|
|
418
|
+
function useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {
|
|
419
|
+
const ctx = requireCtx();
|
|
420
|
+
const idx = getHookIndex();
|
|
421
|
+
if (typeof window === "undefined" && getServerSnapshot) {
|
|
422
|
+
if (ctx.hooks.length <= idx) ctx.hooks.push({
|
|
423
|
+
subscribe,
|
|
424
|
+
unsubscribe: void 0,
|
|
425
|
+
snapshot: getServerSnapshot()
|
|
426
|
+
});
|
|
427
|
+
return ctx.hooks[idx].snapshot;
|
|
428
|
+
}
|
|
429
|
+
if (ctx.hooks.length <= idx) {
|
|
430
|
+
const snapshot = getSnapshot();
|
|
431
|
+
const entry = {
|
|
432
|
+
subscribe,
|
|
433
|
+
unsubscribe: void 0,
|
|
434
|
+
snapshot
|
|
435
|
+
};
|
|
436
|
+
const onChange = () => {
|
|
437
|
+
const next = getSnapshot();
|
|
438
|
+
if (!Object.is(entry.snapshot, next)) {
|
|
439
|
+
entry.snapshot = next;
|
|
440
|
+
ctx.scheduleRerender();
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
entry.unsubscribe = subscribe(onChange);
|
|
444
|
+
ctx.hooks.push(entry);
|
|
445
|
+
return snapshot;
|
|
446
|
+
}
|
|
447
|
+
const entry = ctx.hooks[idx];
|
|
448
|
+
if (entry.subscribe !== subscribe) {
|
|
449
|
+
if (entry.unsubscribe) entry.unsubscribe();
|
|
450
|
+
const onChange = () => {
|
|
451
|
+
const next = getSnapshot();
|
|
452
|
+
if (!Object.is(entry.snapshot, next)) {
|
|
453
|
+
entry.snapshot = next;
|
|
454
|
+
ctx.scheduleRerender();
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
entry.unsubscribe = subscribe(onChange);
|
|
458
|
+
entry.subscribe = subscribe;
|
|
459
|
+
}
|
|
460
|
+
entry.snapshot = getSnapshot();
|
|
461
|
+
return entry.snapshot;
|
|
462
|
+
}
|
|
463
|
+
const _promiseCache = /* @__PURE__ */ new WeakMap();
|
|
464
|
+
/**
|
|
465
|
+
* React-compatible `use` — reads a Context or suspends on a Promise.
|
|
466
|
+
* Can be called conditionally (unlike other hooks).
|
|
467
|
+
*
|
|
468
|
+
* IMPORTANT: Promises must have a stable identity across renders.
|
|
469
|
+
* Create promises outside the component or memoize them. Calling
|
|
470
|
+
* `use(fetch('/api'))` creates a new promise each render and will
|
|
471
|
+
* cause infinite suspension.
|
|
472
|
+
*/
|
|
473
|
+
function use(resource) {
|
|
474
|
+
if (resource && typeof resource === "object" && COMPAT_CTX in resource) return useContext(resource);
|
|
475
|
+
if (resource && typeof resource === "object" && "id" in resource && "defaultValue" in resource) return useContext$1(resource);
|
|
476
|
+
const promise = resource;
|
|
477
|
+
let entry = _promiseCache.get(promise);
|
|
478
|
+
if (!entry) {
|
|
479
|
+
entry = { status: "pending" };
|
|
480
|
+
_promiseCache.set(promise, entry);
|
|
481
|
+
promise.then((value) => {
|
|
482
|
+
entry.status = "resolved";
|
|
483
|
+
entry.value = value;
|
|
484
|
+
}, (error) => {
|
|
485
|
+
entry.status = "rejected";
|
|
486
|
+
entry.error = error;
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
if (entry.status === "resolved") return entry.value;
|
|
490
|
+
if (entry.status === "rejected") throw entry.error;
|
|
491
|
+
throw promise;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* React-compatible `useActionState` — manages async action state with pending indicator.
|
|
495
|
+
*/
|
|
496
|
+
function useActionState(action, initialState) {
|
|
497
|
+
const [state, setState] = useState(initialState);
|
|
498
|
+
const [isPending, setIsPending] = useState(false);
|
|
499
|
+
const dispatch = (payload) => {
|
|
500
|
+
setIsPending(true);
|
|
501
|
+
const result = action(state, payload);
|
|
502
|
+
if (result instanceof Promise) result.then((next) => {
|
|
503
|
+
setState(next);
|
|
504
|
+
setIsPending(false);
|
|
505
|
+
});
|
|
506
|
+
else {
|
|
507
|
+
setState(result);
|
|
508
|
+
setIsPending(false);
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
return [
|
|
512
|
+
state,
|
|
513
|
+
dispatch,
|
|
514
|
+
isPending
|
|
515
|
+
];
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* React-compatible `startTransition` — runs the callback synchronously.
|
|
519
|
+
* No concurrent mode in Pyreon, so transitions are immediate.
|
|
520
|
+
*/
|
|
521
|
+
function startTransition(fn) {
|
|
522
|
+
fn();
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* React-compatible `isValidElement` — checks if a value is a VNode.
|
|
526
|
+
*/
|
|
527
|
+
function isValidElement(value) {
|
|
528
|
+
return value != null && typeof value === "object" && "type" in value && "props" in value;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* React-compatible `useDebugValue` — no-op in Pyreon (no React DevTools integration).
|
|
532
|
+
*/
|
|
533
|
+
function useDebugValue(_value, _format) {}
|
|
534
|
+
/**
|
|
535
|
+
* React-compatible `flushSync` — runs the callback synchronously.
|
|
536
|
+
*
|
|
537
|
+
* BEHAVIORAL DIFFERENCE: In Pyreon's compat model, state updates are
|
|
538
|
+
* batched via microtask. flushSync runs the callback and returns its
|
|
539
|
+
* result, but the DOM updates triggered by state changes inside the
|
|
540
|
+
* callback still fire asynchronously. For DOM measurement after state
|
|
541
|
+
* updates, use `await act(() => setState(...))` in tests, or
|
|
542
|
+
* `requestAnimationFrame` in production code.
|
|
543
|
+
*/
|
|
544
|
+
function flushSync(fn) {
|
|
545
|
+
return fn();
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* React-compatible `act` — flushes pending microtasks for testing.
|
|
549
|
+
*/
|
|
550
|
+
async function act(fn) {
|
|
551
|
+
const result = fn();
|
|
552
|
+
if (result instanceof Promise) await result;
|
|
553
|
+
await new Promise((r) => queueMicrotask(r));
|
|
554
|
+
await new Promise((r) => queueMicrotask(r));
|
|
555
|
+
}
|
|
556
|
+
const version = "19.0.0-pyreon";
|
|
557
|
+
/**
|
|
558
|
+
* React-compatible `StrictMode` — pass-through in Pyreon (no double-invoke behavior).
|
|
559
|
+
*/
|
|
560
|
+
function StrictMode(props) {
|
|
561
|
+
return props.children ?? null;
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* React-compatible `Profiler` — pass-through in Pyreon (no profiling integration).
|
|
565
|
+
*/
|
|
566
|
+
function Profiler(props) {
|
|
567
|
+
return props.children ?? null;
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* React-compatible `Component` class stub.
|
|
571
|
+
* Class components are not fully supported — use function components with hooks.
|
|
572
|
+
*/
|
|
573
|
+
var Component = class {
|
|
574
|
+
props;
|
|
575
|
+
state;
|
|
576
|
+
constructor(props) {
|
|
577
|
+
this.props = props;
|
|
578
|
+
this.state = {};
|
|
579
|
+
}
|
|
580
|
+
setState(_partial) {
|
|
581
|
+
console.warn("[Pyreon] Class component setState is not supported. Use function components with hooks.");
|
|
582
|
+
}
|
|
583
|
+
forceUpdate() {
|
|
584
|
+
console.warn("[Pyreon] Class component forceUpdate is not supported. Use function components with hooks.");
|
|
585
|
+
}
|
|
586
|
+
render() {
|
|
587
|
+
return null;
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
/**
|
|
591
|
+
* React-compatible `PureComponent` class stub.
|
|
592
|
+
*/
|
|
593
|
+
var PureComponent = class extends Component {};
|
|
278
594
|
|
|
279
595
|
//#endregion
|
|
280
|
-
export { Children, ErrorBoundary, Fragment, Suspense, batch, cloneElement, createContext, createElement, createPortal, forwardRef, h, lazy, memo, useCallback, useContext, useDeferredValue, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState, useTransition };
|
|
596
|
+
export { Children, Component, ErrorBoundary, Fragment, Profiler, PureComponent, StrictMode, Suspense, act, batch, cloneElement, createContext, createElement, createPortal, createRef, flushSync, forwardRef, h, isValidElement, lazy, memo, startTransition, use, useActionState, useCallback, useContext, useDebugValue, useDeferredValue, useEffect, useId, useImperativeHandle, useInsertionEffect, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore, useTransition, version };
|
|
281
597
|
//# sourceMappingURL=index.js.map
|