@pyreon/react-compat 0.2.1 → 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/lib/analysis/index.js.html +1 -1
- package/lib/index.js +159 -76
- package/lib/index.js.map +1 -1
- package/lib/types/index.d.ts +159 -74
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index2.d.ts +34 -41
- package/lib/types/index2.d.ts.map +1 -1
- package/package.json +14 -4
- package/src/index.ts +196 -175
- package/src/jsx-runtime.ts +169 -0
- package/src/tests/react-compat.test.ts +457 -290
|
@@ -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":"6c313ed2-1","name":"jsx-runtime.ts"},{"uid":"6c313ed2-3","name":"index.ts"}]}]}],"isRoot":true},"nodeParts":{"6c313ed2-1":{"renderedLength":186,"gzipLength":138,"brotliLength":0,"metaUid":"6c313ed2-0"},"6c313ed2-3":{"renderedLength":4990,"gzipLength":1386,"brotliLength":0,"metaUid":"6c313ed2-2"}},"nodeMetas":{"6c313ed2-0":{"id":"/src/jsx-runtime.ts","moduleParts":{"index.js":"6c313ed2-1"},"imported":[{"uid":"6c313ed2-4"},{"uid":"6c313ed2-5"}],"importedBy":[{"uid":"6c313ed2-2"}]},"6c313ed2-2":{"id":"/src/index.ts","moduleParts":{"index.js":"6c313ed2-3"},"imported":[{"uid":"6c313ed2-4"},{"uid":"6c313ed2-5"},{"uid":"6c313ed2-0"}],"importedBy":[],"isEntry":true},"6c313ed2-4":{"id":"@pyreon/core","moduleParts":{},"imported":[],"importedBy":[{"uid":"6c313ed2-2"},{"uid":"6c313ed2-0"}]},"6c313ed2-5":{"id":"@pyreon/reactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"6c313ed2-2"},{"uid":"6c313ed2-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,111 +1,206 @@
|
|
|
1
|
-
import { ErrorBoundary, Fragment, Portal, Suspense, createContext,
|
|
2
|
-
import { batch
|
|
1
|
+
import { ErrorBoundary, Fragment, Portal, Suspense, createContext, h, h as createElement, lazy, useContext } from "@pyreon/core";
|
|
2
|
+
import { batch } from "@pyreon/reactivity";
|
|
3
3
|
|
|
4
|
+
//#region src/jsx-runtime.ts
|
|
5
|
+
let _currentCtx = null;
|
|
6
|
+
let _hookIndex = 0;
|
|
7
|
+
function getCurrentCtx() {
|
|
8
|
+
return _currentCtx;
|
|
9
|
+
}
|
|
10
|
+
function getHookIndex() {
|
|
11
|
+
return _hookIndex++;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
//#endregion
|
|
4
15
|
//#region src/index.ts
|
|
16
|
+
function requireCtx() {
|
|
17
|
+
const ctx = getCurrentCtx();
|
|
18
|
+
if (!ctx) throw new Error("Hook called outside of a component render");
|
|
19
|
+
return ctx;
|
|
20
|
+
}
|
|
21
|
+
function depsChanged(a, b) {
|
|
22
|
+
if (a === void 0 || b === void 0) return true;
|
|
23
|
+
if (a.length !== b.length) return true;
|
|
24
|
+
for (let i = 0; i < a.length; i++) if (!Object.is(a[i], b[i])) return true;
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
5
27
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* Unlike React: the getter is a signal, so any component or effect that reads
|
|
10
|
-
* it will re-run automatically. No dep arrays needed.
|
|
28
|
+
* React-compatible `useState` — returns `[value, setter]`.
|
|
29
|
+
* Triggers a component re-render when the setter is called.
|
|
11
30
|
*/
|
|
12
31
|
function useState(initial) {
|
|
13
|
-
const
|
|
32
|
+
const ctx = requireCtx();
|
|
33
|
+
const idx = getHookIndex();
|
|
34
|
+
if (ctx.hooks.length <= idx) ctx.hooks.push(typeof initial === "function" ? initial() : initial);
|
|
35
|
+
const value = ctx.hooks[idx];
|
|
14
36
|
const setter = (v) => {
|
|
15
|
-
|
|
16
|
-
|
|
37
|
+
const current = ctx.hooks[idx];
|
|
38
|
+
const next = typeof v === "function" ? v(current) : v;
|
|
39
|
+
if (Object.is(current, next)) return;
|
|
40
|
+
ctx.hooks[idx] = next;
|
|
41
|
+
ctx.scheduleRerender();
|
|
17
42
|
};
|
|
18
|
-
return [
|
|
43
|
+
return [value, setter];
|
|
19
44
|
}
|
|
20
45
|
/**
|
|
21
|
-
*
|
|
46
|
+
* React-compatible `useReducer` — returns `[state, dispatch]`.
|
|
22
47
|
*/
|
|
23
48
|
function useReducer(reducer, initial) {
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
|
|
49
|
+
const ctx = requireCtx();
|
|
50
|
+
const idx = getHookIndex();
|
|
51
|
+
if (ctx.hooks.length <= idx) ctx.hooks.push(typeof initial === "function" ? initial() : initial);
|
|
52
|
+
const state = ctx.hooks[idx];
|
|
53
|
+
const dispatch = (action) => {
|
|
54
|
+
const current = ctx.hooks[idx];
|
|
55
|
+
const next = reducer(current, action);
|
|
56
|
+
if (Object.is(current, next)) return;
|
|
57
|
+
ctx.hooks[idx] = next;
|
|
58
|
+
ctx.scheduleRerender();
|
|
59
|
+
};
|
|
60
|
+
return [state, dispatch];
|
|
27
61
|
}
|
|
28
62
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* The `deps` array is IGNORED — Pyreon tracks reactive dependencies automatically.
|
|
32
|
-
* If `deps` is `[]` (mount-only), wrap the body in `runUntracked(() => ...)`.
|
|
33
|
-
*
|
|
34
|
-
* Returns a cleanup the same way React does (return a function from `fn`).
|
|
63
|
+
* React-compatible `useEffect` — runs after render when deps change.
|
|
64
|
+
* Returns cleanup on unmount and before re-running.
|
|
35
65
|
*/
|
|
36
66
|
function useEffect(fn, deps) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
67
|
+
const ctx = requireCtx();
|
|
68
|
+
const idx = getHookIndex();
|
|
69
|
+
if (ctx.hooks.length <= idx) {
|
|
70
|
+
const entry = {
|
|
71
|
+
fn,
|
|
72
|
+
deps,
|
|
73
|
+
cleanup: void 0
|
|
74
|
+
};
|
|
75
|
+
ctx.hooks.push(entry);
|
|
76
|
+
ctx.pendingEffects.push(entry);
|
|
77
|
+
} else {
|
|
78
|
+
const entry = ctx.hooks[idx];
|
|
79
|
+
if (depsChanged(entry.deps, deps)) {
|
|
80
|
+
entry.fn = fn;
|
|
81
|
+
entry.deps = deps;
|
|
82
|
+
ctx.pendingEffects.push(entry);
|
|
83
|
+
}
|
|
46
84
|
}
|
|
47
85
|
}
|
|
48
86
|
/**
|
|
49
|
-
*
|
|
50
|
-
* The `deps` array is IGNORED — Pyreon's `computed` tracks dependencies automatically.
|
|
51
|
-
* Returns a getter: call `value()` to read the memoized result.
|
|
87
|
+
* React-compatible `useLayoutEffect` — runs synchronously after DOM mutations.
|
|
52
88
|
*/
|
|
53
|
-
function
|
|
54
|
-
|
|
89
|
+
function useLayoutEffect(fn, deps) {
|
|
90
|
+
const ctx = requireCtx();
|
|
91
|
+
const idx = getHookIndex();
|
|
92
|
+
if (ctx.hooks.length <= idx) {
|
|
93
|
+
const entry = {
|
|
94
|
+
fn,
|
|
95
|
+
deps,
|
|
96
|
+
cleanup: void 0
|
|
97
|
+
};
|
|
98
|
+
ctx.hooks.push(entry);
|
|
99
|
+
ctx.pendingLayoutEffects.push(entry);
|
|
100
|
+
} else {
|
|
101
|
+
const entry = ctx.hooks[idx];
|
|
102
|
+
if (depsChanged(entry.deps, deps)) {
|
|
103
|
+
entry.fn = fn;
|
|
104
|
+
entry.deps = deps;
|
|
105
|
+
ctx.pendingLayoutEffects.push(entry);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
55
108
|
}
|
|
56
109
|
/**
|
|
57
|
-
*
|
|
58
|
-
* In Pyreon, components run once so callbacks are never recreated — returns `fn` as-is.
|
|
110
|
+
* React-compatible `useMemo` — returns the cached value, recomputed when deps change.
|
|
59
111
|
*/
|
|
60
|
-
function
|
|
61
|
-
|
|
112
|
+
function useMemo(fn, deps) {
|
|
113
|
+
const ctx = requireCtx();
|
|
114
|
+
const idx = getHookIndex();
|
|
115
|
+
if (ctx.hooks.length <= idx) {
|
|
116
|
+
const value = fn();
|
|
117
|
+
ctx.hooks.push({
|
|
118
|
+
value,
|
|
119
|
+
deps
|
|
120
|
+
});
|
|
121
|
+
return value;
|
|
122
|
+
}
|
|
123
|
+
const entry = ctx.hooks[idx];
|
|
124
|
+
if (depsChanged(entry.deps, deps)) {
|
|
125
|
+
entry.value = fn();
|
|
126
|
+
entry.deps = deps;
|
|
127
|
+
}
|
|
128
|
+
return entry.value;
|
|
62
129
|
}
|
|
63
130
|
/**
|
|
64
|
-
*
|
|
65
|
-
|
|
131
|
+
* React-compatible `useCallback` — returns the cached function when deps haven't changed.
|
|
132
|
+
*/
|
|
133
|
+
function useCallback(fn, deps) {
|
|
134
|
+
return useMemo(() => fn, deps);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* React-compatible `useRef` — returns `{ current }` persisted across re-renders.
|
|
66
138
|
*/
|
|
67
139
|
function useRef(initial) {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
140
|
+
const ctx = requireCtx();
|
|
141
|
+
const idx = getHookIndex();
|
|
142
|
+
if (ctx.hooks.length <= idx) {
|
|
143
|
+
const ref = { current: initial !== void 0 ? initial : null };
|
|
144
|
+
ctx.hooks.push(ref);
|
|
145
|
+
}
|
|
146
|
+
return ctx.hooks[idx];
|
|
71
147
|
}
|
|
148
|
+
let _idCounter = 0;
|
|
72
149
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* Uses the component's effectScope as the key so the counter starts at 0 for every
|
|
76
|
-
* component on both server and client — IDs are deterministic and hydration-safe.
|
|
150
|
+
* React-compatible `useId` — returns a stable unique string per hook call.
|
|
77
151
|
*/
|
|
78
|
-
const _idCounters = /* @__PURE__ */ new WeakMap();
|
|
79
152
|
function useId() {
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return `:r${count.toString(36)}:`;
|
|
153
|
+
const ctx = requireCtx();
|
|
154
|
+
const idx = getHookIndex();
|
|
155
|
+
if (ctx.hooks.length <= idx) ctx.hooks.push(`:r${(_idCounter++).toString(36)}:`);
|
|
156
|
+
return ctx.hooks[idx];
|
|
85
157
|
}
|
|
86
158
|
/**
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
* Kept for API compatibility when migrating React code.
|
|
159
|
+
* React-compatible `memo` — wraps a component to skip re-render when props
|
|
160
|
+
* are shallowly equal.
|
|
90
161
|
*/
|
|
91
|
-
function memo(component) {
|
|
92
|
-
|
|
162
|
+
function memo(component, areEqual) {
|
|
163
|
+
const compare = areEqual ?? ((a, b) => {
|
|
164
|
+
const keysA = Object.keys(a);
|
|
165
|
+
const keysB = Object.keys(b);
|
|
166
|
+
if (keysA.length !== keysB.length) return false;
|
|
167
|
+
for (const k of keysA) if (!Object.is(a[k], b[k])) return false;
|
|
168
|
+
return true;
|
|
169
|
+
});
|
|
170
|
+
let prevProps = null;
|
|
171
|
+
let prevResult = null;
|
|
172
|
+
return (props) => {
|
|
173
|
+
if (prevProps !== null && compare(prevProps, props)) return prevResult;
|
|
174
|
+
prevProps = props;
|
|
175
|
+
prevResult = component(props);
|
|
176
|
+
return prevResult;
|
|
177
|
+
};
|
|
93
178
|
}
|
|
94
179
|
/**
|
|
95
|
-
*
|
|
96
|
-
* Returns `[false, (fn) => fn()]` to keep code runnable without changes.
|
|
180
|
+
* React-compatible `useTransition` — no concurrent mode in Pyreon.
|
|
97
181
|
*/
|
|
98
182
|
function useTransition() {
|
|
99
183
|
return [false, (fn) => fn()];
|
|
100
184
|
}
|
|
101
185
|
/**
|
|
102
|
-
*
|
|
186
|
+
* React-compatible `useDeferredValue` — returns the value as-is.
|
|
103
187
|
*/
|
|
104
188
|
function useDeferredValue(value) {
|
|
105
189
|
return value;
|
|
106
190
|
}
|
|
107
191
|
/**
|
|
108
|
-
*
|
|
192
|
+
* React-compatible `useImperativeHandle`.
|
|
193
|
+
*/
|
|
194
|
+
function useImperativeHandle(ref, init, deps) {
|
|
195
|
+
useLayoutEffect(() => {
|
|
196
|
+
if (ref) ref.current = init();
|
|
197
|
+
return () => {
|
|
198
|
+
if (ref) ref.current = null;
|
|
199
|
+
};
|
|
200
|
+
}, deps);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* React-compatible `createPortal(children, target)`.
|
|
109
204
|
*/
|
|
110
205
|
function createPortal(children, target) {
|
|
111
206
|
return Portal({
|
|
@@ -113,19 +208,7 @@ function createPortal(children, target) {
|
|
|
113
208
|
children
|
|
114
209
|
});
|
|
115
210
|
}
|
|
116
|
-
/**
|
|
117
|
-
* Drop-in for React's `useImperativeHandle`.
|
|
118
|
-
* In Pyreon, expose methods via a ref prop directly — this is a compatibility shim.
|
|
119
|
-
*/
|
|
120
|
-
function useImperativeHandle(ref, init, _deps) {
|
|
121
|
-
onMount(() => {
|
|
122
|
-
if (ref) ref.current = init();
|
|
123
|
-
});
|
|
124
|
-
onUnmount(() => {
|
|
125
|
-
if (ref) ref.current = null;
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
211
|
|
|
129
212
|
//#endregion
|
|
130
|
-
export { ErrorBoundary, Fragment, Suspense, batch, createContext, createElement, createPortal,
|
|
213
|
+
export { ErrorBoundary, Fragment, Suspense, batch, createContext, createElement, createPortal, h, lazy, memo, useCallback, useContext, useDeferredValue, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState, useTransition };
|
|
131
214
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @pyreon/react-compat\n *\n * React-compatible hook API that runs on Pyreon's reactive engine.\n *\n * Allows you to write familiar React-style code while getting Pyreon's\n * fine-grained reactivity, built-in router/store, and superior performance.\n *\n * DIFFERENCES FROM REACT:\n * - No hooks rules: call these anywhere in a component, in loops, conditions, etc.\n * - useEffect deps array is IGNORED — Pyreon tracks dependencies automatically.\n * - useCallback/memo are identity functions — no re-renders means no stale closures.\n * - Components run ONCE (setup), not on every render.\n *\n * USAGE:\n * Replace `import { useState, useEffect } from \"react\"` with\n * `import { useState, useEffect } from \"@pyreon/react-compat\"`\n * Replace `import { createRoot } from \"react-dom/client\"` with\n * `import { createRoot } from \"@pyreon/react-compat/dom\"`\n */\n\nexport type { Props, VNode as ReactNode, VNodeChild } from \"@pyreon/core\"\n// Re-export Pyreon's JSX runtime so JSX transforms work the same way\n// Lifecycle\nexport { Fragment, h as createElement, h, onMount as useLayoutEffect } from \"@pyreon/core\"\n\nimport type { CleanupFn, VNodeChild } from \"@pyreon/core\"\nimport {\n createContext,\n createRef,\n ErrorBoundary,\n onErrorCaptured,\n onMount,\n onUnmount,\n onUpdate,\n Portal,\n Suspense,\n useContext,\n} from \"@pyreon/core\"\nimport {\n batch,\n computed,\n createSelector,\n effect,\n getCurrentScope,\n runUntracked,\n signal,\n} from \"@pyreon/reactivity\"\n\n// ─── State ────────────────────────────────────────────────────────────────────\n\n/**\n * Drop-in for React's `useState`.\n * Returns `[getter, setter]` — call `getter()` to read, `setter(v)` to write.\n *\n * Unlike React: the getter is a signal, so any component or effect that reads\n * it will re-run automatically. No dep arrays needed.\n */\nexport function useState<T>(initial: T | (() => T)): [() => T, (v: T | ((prev: T) => T)) => void] {\n const s = signal<T>(typeof initial === \"function\" ? (initial as () => T)() : initial)\n const setter = (v: T | ((prev: T) => T)) => {\n if (typeof v === \"function\") s.update(v as (prev: T) => T)\n else s.set(v)\n }\n return [s, setter]\n}\n\n// ─── Reducer ─────────────────────────────────────────────────────────────────\n\n/**\n * Drop-in for React's `useReducer`.\n */\nexport function useReducer<S, A>(\n reducer: (state: S, action: A) => S,\n initial: S | (() => S),\n): [() => S, (action: A) => void] {\n const s = signal<S>(typeof initial === \"function\" ? (initial as () => S)() : initial)\n const dispatch = (action: A) => s.update((prev) => reducer(prev, action))\n return [s, dispatch]\n}\n\n// ─── Effects ─────────────────────────────────────────────────────────────────\n\n/**\n * Drop-in for React's `useEffect`.\n *\n * The `deps` array is IGNORED — Pyreon tracks reactive dependencies automatically.\n * If `deps` is `[]` (mount-only), wrap the body in `runUntracked(() => ...)`.\n *\n * Returns a cleanup the same way React does (return a function from `fn`).\n */\n// biome-ignore lint/suspicious/noConfusingVoidType: void is intentional — callers may return void\nexport function useEffect(fn: () => CleanupFn | void, deps?: unknown[]): void {\n if (deps !== undefined && deps.length === 0) {\n // [] means \"run once on mount\" — use onMount instead of a tracking effect\n onMount((): undefined => {\n const cleanup = runUntracked(fn)\n if (typeof cleanup === \"function\") onUnmount(cleanup)\n })\n } else {\n // No deps or non-empty deps: run reactively (Pyreon auto-tracks).\n // effect() natively supports cleanup: if fn() returns a function,\n // it's called before re-runs and on dispose.\n const e = effect(fn)\n onUnmount(() => {\n e.dispose()\n })\n }\n}\n\n/**\n * Drop-in for React's `useLayoutEffect`.\n * In Pyreon there is no paint distinction — maps to `onMount` (same as useEffect).\n */\nexport { useEffect as useLayoutEffect_ }\n\n// ─── Memoization ─────────────────────────────────────────────────────────────\n\n/**\n * Drop-in for React's `useMemo`.\n * The `deps` array is IGNORED — Pyreon's `computed` tracks dependencies automatically.\n * Returns a getter: call `value()` to read the memoized result.\n */\nexport function useMemo<T>(fn: () => T, _deps?: unknown[]): () => T {\n return computed(fn)\n}\n\n/**\n * Drop-in for React's `useCallback`.\n * In Pyreon, components run once so callbacks are never recreated — returns `fn` as-is.\n */\n// biome-ignore lint/suspicious/noExplicitAny: any is needed for contravariant function params\nexport function useCallback<T extends (...args: any[]) => any>(fn: T, _deps?: unknown[]): T {\n return fn\n}\n\n// ─── Refs ─────────────────────────────────────────────────────────────────────\n\n/**\n * Drop-in for React's `useRef`.\n * Returns `{ current: T }` — same shape as React's ref object.\n */\nexport function useRef<T>(initial?: T): { current: T | null } {\n const ref = createRef<T>()\n if (initial !== undefined) ref.current = initial as T\n return ref\n}\n\n// ─── Context ─────────────────────────────────────────────────────────────────\n\n/**\n * Drop-in for React's `createContext` + `useContext`.\n * Usage mirrors React: `const Ctx = createContext(defaultValue)`.\n */\nexport { createContext, useContext }\n\n// ─── ID ───────────────────────────────────────────────────────────────────────\n\n/**\n * Drop-in for React's `useId` — returns a stable unique string per component instance.\n *\n * Uses the component's effectScope as the key so the counter starts at 0 for every\n * component on both server and client — IDs are deterministic and hydration-safe.\n */\nconst _idCounters = new WeakMap<object, number>()\n\nexport function useId(): string {\n const scope = getCurrentScope()\n if (!scope) return `:r${Math.random().toString(36).slice(2, 9)}:`\n const count = _idCounters.get(scope) ?? 0\n _idCounters.set(scope, count + 1)\n return `:r${count.toString(36)}:`\n}\n\n// ─── Optimization ─────────────────────────────────────────────────────────────\n\n/**\n * Drop-in for React's `memo` — wraps a component.\n * In Pyreon, components run once (no re-renders), so memoization is a no-op.\n * Kept for API compatibility when migrating React code.\n */\nexport function memo<P extends Record<string, unknown>>(\n component: (props: P) => VNodeChild,\n): (props: P) => VNodeChild {\n return component\n}\n\n/**\n * Drop-in for React's `useTransition` — no-op in Pyreon (no concurrent mode).\n * Returns `[false, (fn) => fn()]` to keep code runnable without changes.\n */\nexport function useTransition(): [boolean, (fn: () => void) => void] {\n return [false, (fn) => fn()]\n}\n\n/**\n * Drop-in for React's `useDeferredValue` — returns the value as-is in Pyreon.\n */\nexport function useDeferredValue<T>(value: T): T {\n return value\n}\n\n// ─── Batching ─────────────────────────────────────────────────────────────────\n\n/**\n * Drop-in for React's `unstable_batchedUpdates` / React 18's automatic batching.\n * Pyreon's `batch()` does the same thing.\n */\nexport { batch }\n\n// ─── Error boundaries ─────────────────────────────────────────────────────────\n\n/**\n * Drop-in for React's error boundary pattern.\n * Return `true` from `handler` to prevent error propagation (like `componentDidCatch`).\n */\nexport { onErrorCaptured as useErrorBoundary }\n\n// ─── Portals ─────────────────────────────────────────────────────────────────\n\n/**\n * Drop-in for React's `createPortal(children, target)`.\n */\nexport function createPortal(children: VNodeChild, target: Element): VNodeChild {\n return Portal({ target, children })\n}\n\n// ─── Imperative handle ────────────────────────────────────────────────────────\n\n/**\n * Drop-in for React's `useImperativeHandle`.\n * In Pyreon, expose methods via a ref prop directly — this is a compatibility shim.\n */\nexport function useImperativeHandle<T>(\n ref: { current: T | null } | null | undefined,\n init: () => T,\n _deps?: unknown[],\n): void {\n onMount((): undefined => {\n if (ref) ref.current = init()\n })\n onUnmount(() => {\n if (ref) ref.current = null\n })\n}\n\n// ─── Selector ─────────────────────────────────────────────────────────────────\n\n/**\n * Pyreon-specific: O(1) equality selector (no React equivalent).\n * Useful for large lists where only the selected item should re-render.\n * @see createSelector in @pyreon/reactivity\n */\nexport { createSelector }\n\n// ─── onUpdate ─────────────────────────────────────────────────────────────────\n\n/** Pyreon-specific lifecycle hook — runs after each reactive update. */\nexport { onMount, onUnmount, onUpdate }\n\n// ─── Suspense / lazy ──────────────────────────────────────────────────────────\n\n/**\n * Drop-in for React's `lazy()`.\n * Re-exported from `@pyreon/core` — wraps a dynamic import, renders null until\n * the module resolves. Pair with `<Suspense>` to show a fallback during loading.\n */\nexport { lazy } from \"@pyreon/core\"\n\n/**\n * Drop-in for React's `<Suspense>`.\n * Shows `fallback` while a `lazy()` child is still loading.\n */\nexport { ErrorBoundary, Suspense }\n"],"mappings":";;;;;;;;;;;AA0DA,SAAgB,SAAY,SAAsE;CAChG,MAAM,IAAI,OAAU,OAAO,YAAY,aAAc,SAAqB,GAAG,QAAQ;CACrF,MAAM,UAAU,MAA4B;AAC1C,MAAI,OAAO,MAAM,WAAY,GAAE,OAAO,EAAoB;MACrD,GAAE,IAAI,EAAE;;AAEf,QAAO,CAAC,GAAG,OAAO;;;;;AAQpB,SAAgB,WACd,SACA,SACgC;CAChC,MAAM,IAAI,OAAU,OAAO,YAAY,aAAc,SAAqB,GAAG,QAAQ;CACrF,MAAM,YAAY,WAAc,EAAE,QAAQ,SAAS,QAAQ,MAAM,OAAO,CAAC;AACzE,QAAO,CAAC,GAAG,SAAS;;;;;;;;;;AActB,SAAgB,UAAU,IAA4B,MAAwB;AAC5E,KAAI,SAAS,UAAa,KAAK,WAAW,EAExC,eAAyB;EACvB,MAAM,UAAU,aAAa,GAAG;AAChC,MAAI,OAAO,YAAY,WAAY,WAAU,QAAQ;GACrD;MACG;EAIL,MAAM,IAAI,OAAO,GAAG;AACpB,kBAAgB;AACd,KAAE,SAAS;IACX;;;;;;;;AAiBN,SAAgB,QAAW,IAAa,OAA4B;AAClE,QAAO,SAAS,GAAG;;;;;;AAQrB,SAAgB,YAA+C,IAAO,OAAsB;AAC1F,QAAO;;;;;;AAST,SAAgB,OAAU,SAAoC;CAC5D,MAAM,MAAM,WAAc;AAC1B,KAAI,YAAY,OAAW,KAAI,UAAU;AACzC,QAAO;;;;;;;;AAmBT,MAAM,8BAAc,IAAI,SAAyB;AAEjD,SAAgB,QAAgB;CAC9B,MAAM,QAAQ,iBAAiB;AAC/B,KAAI,CAAC,MAAO,QAAO,KAAK,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC;CAC/D,MAAM,QAAQ,YAAY,IAAI,MAAM,IAAI;AACxC,aAAY,IAAI,OAAO,QAAQ,EAAE;AACjC,QAAO,KAAK,MAAM,SAAS,GAAG,CAAC;;;;;;;AAUjC,SAAgB,KACd,WAC0B;AAC1B,QAAO;;;;;;AAOT,SAAgB,gBAAqD;AACnE,QAAO,CAAC,QAAQ,OAAO,IAAI,CAAC;;;;;AAM9B,SAAgB,iBAAoB,OAAa;AAC/C,QAAO;;;;;AAwBT,SAAgB,aAAa,UAAsB,QAA6B;AAC9E,QAAO,OAAO;EAAE;EAAQ;EAAU,CAAC;;;;;;AASrC,SAAgB,oBACd,KACA,MACA,OACM;AACN,eAAyB;AACvB,MAAI,IAAK,KAAI,UAAU,MAAM;GAC7B;AACF,iBAAgB;AACd,MAAI,IAAK,KAAI,UAAU;GACvB"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/jsx-runtime.ts","../src/index.ts"],"sourcesContent":["/**\n * Compat JSX runtime for React compatibility mode.\n *\n * When `jsxImportSource` is set to `@pyreon/react-compat` (via the vite plugin's\n * `compat: \"react\"` option), OXC rewrites JSX to import from this file:\n * <div className=\"x\" /> → jsx(\"div\", { className: \"x\" })\n *\n * For component VNodes, we wrap the component function so it returns a reactive\n * accessor — enabling React-style re-renders on state change while Pyreon's\n * existing renderer handles all DOM work.\n */\n\nimport type { ComponentFn, Props, VNode, VNodeChild } from \"@pyreon/core\"\nimport { Fragment, h } from \"@pyreon/core\"\nimport { 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}\n\nexport interface EffectEntry {\n // biome-ignore lint/suspicious/noConfusingVoidType: matches React'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(reactComponent: Function): ComponentFn {\n let wrapped = _wrapperCache.get(reactComponent)\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 }\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 // Return reactive accessor — Pyreon's mountChild calls mountReactive\n return () => {\n version() // tracked read — triggers re-execution when state changes\n beginRender(ctx)\n const result = (reactComponent 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(reactComponent, 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 React-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 // Map className → class for React compat\n if (typeof type === \"string\" && propsWithKey.className !== undefined) {\n propsWithKey.class = propsWithKey.className\n delete propsWithKey.className\n }\n\n return h(type, propsWithKey, ...(childArray as VNodeChild[]))\n}\n\nexport const jsxs = jsx\nexport const jsxDEV = jsx\n","/**\n * @pyreon/react-compat\n *\n * Fully React-compatible hook API powered by Pyreon's reactive engine.\n *\n * Components re-render on state change — just like React. Hooks return plain\n * values and use deps arrays for memoization. Existing React code works\n * unchanged when paired with `pyreon({ compat: \"react\" })` in your vite config.\n *\n * USAGE:\n * import { useState, useEffect } from \"react\" // aliased by vite plugin\n * import { createRoot } from \"react-dom/client\" // aliased by vite plugin\n */\n\nexport type { Props, VNode as ReactNode, VNodeChild } from \"@pyreon/core\"\nexport { Fragment, h as createElement, h } from \"@pyreon/core\"\n\nimport type { VNodeChild } from \"@pyreon/core\"\nimport { createContext, ErrorBoundary, Portal, Suspense, useContext } from \"@pyreon/core\"\nimport { batch } from \"@pyreon/reactivity\"\nimport type { EffectEntry } from \"./jsx-runtime\"\nimport { getCurrentCtx, getHookIndex } from \"./jsx-runtime\"\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction requireCtx() {\n const ctx = getCurrentCtx()\n if (!ctx) throw new Error(\"Hook called outside of a component render\")\n return ctx\n}\n\nfunction depsChanged(a: unknown[] | undefined, b: unknown[] | undefined): boolean {\n if (a === undefined || b === undefined) return true\n if (a.length !== b.length) return true\n for (let i = 0; i < a.length; i++) {\n if (!Object.is(a[i], b[i])) return true\n }\n return false\n}\n\n// ─── State ───────────────────────────────────────────────────────────────────\n\n/**\n * React-compatible `useState` — returns `[value, setter]`.\n * Triggers a component re-render when the setter is called.\n */\nexport function useState<T>(initial: T | (() => T)): [T, (v: T | ((prev: T) => T)) => void] {\n const ctx = requireCtx()\n const idx = getHookIndex()\n\n if (ctx.hooks.length <= idx) {\n ctx.hooks.push(typeof initial === \"function\" ? (initial as () => T)() : initial)\n }\n\n const value = ctx.hooks[idx] as T\n const setter = (v: T | ((prev: T) => T)) => {\n const current = ctx.hooks[idx] as T\n const next = typeof v === \"function\" ? (v as (prev: T) => T)(current) : v\n if (Object.is(current, next)) return\n ctx.hooks[idx] = next\n ctx.scheduleRerender()\n }\n\n return [value, setter]\n}\n\n// ─── Reducer ─────────────────────────────────────────────────────────────────\n\n/**\n * React-compatible `useReducer` — returns `[state, dispatch]`.\n */\nexport function useReducer<S, A>(\n reducer: (state: S, action: A) => S,\n initial: S | (() => S),\n): [S, (action: A) => void] {\n const ctx = requireCtx()\n const idx = getHookIndex()\n\n if (ctx.hooks.length <= idx) {\n ctx.hooks.push(typeof initial === \"function\" ? (initial as () => S)() : initial)\n }\n\n const state = ctx.hooks[idx] as S\n const dispatch = (action: A) => {\n const current = ctx.hooks[idx] as S\n const next = reducer(current, action)\n if (Object.is(current, next)) return\n ctx.hooks[idx] = next\n ctx.scheduleRerender()\n }\n\n return [state, dispatch]\n}\n\n// ─── Effects ─────────────────────────────────────────────────────────────────\n\n/**\n * React-compatible `useEffect` — runs after render when deps change.\n * Returns cleanup on unmount and before re-running.\n */\n// biome-ignore lint/suspicious/noConfusingVoidType: matches React's useEffect signature\nexport function useEffect(fn: () => (() => void) | void, deps?: unknown[]): void {\n const ctx = requireCtx()\n const idx = getHookIndex()\n\n if (ctx.hooks.length <= idx) {\n // First render — always run\n const entry: EffectEntry = { fn, deps, cleanup: undefined }\n ctx.hooks.push(entry)\n ctx.pendingEffects.push(entry)\n } else {\n const entry = ctx.hooks[idx] as EffectEntry\n if (depsChanged(entry.deps, deps)) {\n entry.fn = fn\n entry.deps = deps\n ctx.pendingEffects.push(entry)\n }\n }\n}\n\n/**\n * React-compatible `useLayoutEffect` — runs synchronously after DOM mutations.\n */\n// biome-ignore lint/suspicious/noConfusingVoidType: matches React's useLayoutEffect signature\nexport function useLayoutEffect(fn: () => (() => void) | void, deps?: unknown[]): void {\n const ctx = requireCtx()\n const idx = getHookIndex()\n\n if (ctx.hooks.length <= idx) {\n const entry: EffectEntry = { fn, deps, cleanup: undefined }\n ctx.hooks.push(entry)\n ctx.pendingLayoutEffects.push(entry)\n } else {\n const entry = ctx.hooks[idx] as EffectEntry\n if (depsChanged(entry.deps, deps)) {\n entry.fn = fn\n entry.deps = deps\n ctx.pendingLayoutEffects.push(entry)\n }\n }\n}\n\n// ─── Memoization ─────────────────────────────────────────────────────────────\n\n/**\n * React-compatible `useMemo` — returns the cached value, recomputed when deps change.\n */\nexport function useMemo<T>(fn: () => T, deps: unknown[]): T {\n const ctx = requireCtx()\n const idx = getHookIndex()\n\n if (ctx.hooks.length <= idx) {\n const value = fn()\n ctx.hooks.push({ value, deps })\n return value\n }\n\n const entry = ctx.hooks[idx] as { value: T; deps: unknown[] }\n if (depsChanged(entry.deps, deps)) {\n entry.value = fn()\n entry.deps = deps\n }\n return entry.value\n}\n\n/**\n * React-compatible `useCallback` — returns the cached function when deps haven't changed.\n */\nexport function useCallback<T extends (...args: never[]) => unknown>(fn: T, deps: unknown[]): T {\n return useMemo(() => fn, deps)\n}\n\n// ─── Refs ────────────────────────────────────────────────────────────────────\n\n/**\n * React-compatible `useRef` — returns `{ current }` persisted across re-renders.\n */\nexport function useRef<T>(initial?: T): { current: T | null } {\n const ctx = requireCtx()\n const idx = getHookIndex()\n\n if (ctx.hooks.length <= idx) {\n const ref = { current: initial !== undefined ? (initial as T) : null }\n ctx.hooks.push(ref)\n }\n\n return ctx.hooks[idx] as { current: T | null }\n}\n\n// ─── Context ─────────────────────────────────────────────────────────────────\n\nexport { createContext, useContext }\n\n// ─── ID ──────────────────────────────────────────────────────────────────────\n\nlet _idCounter = 0\n\n/**\n * React-compatible `useId` — returns a stable unique string per hook call.\n */\nexport function useId(): string {\n const ctx = requireCtx()\n const idx = getHookIndex()\n\n if (ctx.hooks.length <= idx) {\n ctx.hooks.push(`:r${(_idCounter++).toString(36)}:`)\n }\n\n return ctx.hooks[idx] as string\n}\n\n// ─── Optimization ────────────────────────────────────────────────────────────\n\n/**\n * React-compatible `memo` — wraps a component to skip re-render when props\n * are shallowly equal.\n */\nexport function memo<P extends Record<string, unknown>>(\n component: (props: P) => VNodeChild,\n areEqual?: (prevProps: P, nextProps: P) => boolean,\n): (props: P) => VNodeChild {\n const compare =\n areEqual ??\n ((a: P, b: P) => {\n const keysA = Object.keys(a)\n const keysB = Object.keys(b)\n if (keysA.length !== keysB.length) return false\n for (const k of keysA) {\n if (!Object.is(a[k], b[k])) return false\n }\n return true\n })\n\n let prevProps: P | null = null\n let prevResult: VNodeChild = null\n\n return (props: P) => {\n if (prevProps !== null && compare(prevProps, props)) {\n return prevResult\n }\n prevProps = props\n prevResult = (component as (p: P) => VNodeChild)(props)\n return prevResult\n }\n}\n\n/**\n * React-compatible `useTransition` — no concurrent mode in Pyreon.\n */\nexport function useTransition(): [boolean, (fn: () => void) => void] {\n return [false, (fn) => fn()]\n}\n\n/**\n * React-compatible `useDeferredValue` — returns the value as-is.\n */\nexport function useDeferredValue<T>(value: T): T {\n return value\n}\n\n// ─── Imperative handle ───────────────────────────────────────────────────────\n\n/**\n * React-compatible `useImperativeHandle`.\n */\nexport function useImperativeHandle<T>(\n ref: { current: T | null } | null | undefined,\n init: () => T,\n deps?: unknown[],\n): void {\n useLayoutEffect(() => {\n if (ref) ref.current = init()\n return () => {\n if (ref) ref.current = null\n }\n }, deps)\n}\n\n// ─── Batching ────────────────────────────────────────────────────────────────\n\nexport { batch }\n\n// ─── Portals ─────────────────────────────────────────────────────────────────\n\n/**\n * React-compatible `createPortal(children, target)`.\n */\nexport function createPortal(children: VNodeChild, target: Element): VNodeChild {\n return Portal({ target, children })\n}\n\n// ─── Suspense / lazy / ErrorBoundary ─────────────────────────────────────────\n\nexport { lazy } from \"@pyreon/core\"\nexport { ErrorBoundary, Suspense }\n"],"mappings":"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"}
|