@pyreon/preact-compat 0.1.1 → 0.2.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/README.md +53 -5
- package/lib/analysis/index.js.html +1 -1
- package/lib/index.js +16 -5
- package/lib/index.js.map +1 -1
- package/lib/types/index.d.ts +16 -3
- package/lib/types/index.d.ts.map +1 -1
- package/package.json +6 -7
- package/src/hooks.ts +2 -4
- package/src/index.ts +37 -7
package/README.md
CHANGED
|
@@ -10,11 +10,12 @@ bun add @pyreon/preact-compat
|
|
|
10
10
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
|
-
```
|
|
13
|
+
```tsx
|
|
14
14
|
// Replace:
|
|
15
|
-
// import {
|
|
15
|
+
// import { render } from "preact"
|
|
16
|
+
// import { useState, useEffect } from "preact/hooks"
|
|
16
17
|
// With:
|
|
17
|
-
import {
|
|
18
|
+
import { render } from "@pyreon/preact-compat"
|
|
18
19
|
import { useState, useEffect } from "@pyreon/preact-compat/hooks"
|
|
19
20
|
|
|
20
21
|
function Counter() {
|
|
@@ -22,10 +23,57 @@ function Counter() {
|
|
|
22
23
|
useEffect(() => {
|
|
23
24
|
console.log("count changed:", count())
|
|
24
25
|
})
|
|
25
|
-
return
|
|
26
|
+
return <button onClick={() => setCount((c) => c + 1)}>{count}</button>
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
render(
|
|
29
|
+
render(<Counter />, document.getElementById("app")!)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Signals
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import { signal, computed, effect } from "@pyreon/preact-compat/signals"
|
|
36
|
+
|
|
37
|
+
const count = signal(0)
|
|
38
|
+
const doubled = computed(() => count.value * 2)
|
|
39
|
+
|
|
40
|
+
function Display() {
|
|
41
|
+
effect(() => console.log("doubled:", doubled.value))
|
|
42
|
+
return (
|
|
43
|
+
<div>
|
|
44
|
+
<span>{doubled.value}</span>
|
|
45
|
+
<button onClick={() => count.value++}>+</button>
|
|
46
|
+
</div>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Class Components
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import { Component } from "@pyreon/preact-compat"
|
|
55
|
+
|
|
56
|
+
interface Props { name: string }
|
|
57
|
+
interface State { clicked: boolean }
|
|
58
|
+
|
|
59
|
+
class Greeting extends Component<Props, State> {
|
|
60
|
+
state = { clicked: false }
|
|
61
|
+
|
|
62
|
+
handleClick = () => {
|
|
63
|
+
this.setState({ clicked: true })
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
render() {
|
|
67
|
+
return (
|
|
68
|
+
<div>
|
|
69
|
+
<p>Hello, {this.props.name}!</p>
|
|
70
|
+
<button onClick={this.handleClick}>
|
|
71
|
+
{this.state.clicked ? "Clicked" : "Click me"}
|
|
72
|
+
</button>
|
|
73
|
+
</div>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
29
77
|
```
|
|
30
78
|
|
|
31
79
|
## Key Differences from Preact
|
|
@@ -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/index.ts","uid":"
|
|
5389
|
+
const data = {"version":2,"tree":{"name":"root","children":[{"name":"index.js","children":[{"name":"src/index.ts","uid":"17fbe9f7-1"}]}],"isRoot":true},"nodeParts":{"17fbe9f7-1":{"renderedLength":2842,"gzipLength":1179,"brotliLength":0,"metaUid":"17fbe9f7-0"}},"nodeMetas":{"17fbe9f7-0":{"id":"/src/index.ts","moduleParts":{"index.js":"17fbe9f7-1"},"imported":[{"uid":"17fbe9f7-2"},{"uid":"17fbe9f7-3"},{"uid":"17fbe9f7-4"}],"importedBy":[],"isEntry":true},"17fbe9f7-2":{"id":"@pyreon/core","moduleParts":{},"imported":[],"importedBy":[{"uid":"17fbe9f7-0"}]},"17fbe9f7-3":{"id":"@pyreon/reactivity","moduleParts":{},"imported":[],"importedBy":[{"uid":"17fbe9f7-0"}]},"17fbe9f7-4":{"id":"@pyreon/runtime-dom","moduleParts":{},"imported":[],"importedBy":[{"uid":"17fbe9f7-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 { Fragment, createContext, createRef, h as pyreonH, useContext } from "@pyreon/core";
|
|
1
|
+
import { Fragment, createContext as createContext$1, createRef, h as pyreonH, onUnmount, popContext, pushContext, useContext } from "@pyreon/core";
|
|
2
2
|
import { batch, signal } from "@pyreon/reactivity";
|
|
3
3
|
import { hydrateRoot, mount } from "@pyreon/runtime-dom";
|
|
4
4
|
|
|
@@ -20,6 +20,21 @@ function hydrate(vnode, container) {
|
|
|
20
20
|
hydrateRoot(container, vnode);
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
+
* Preact-compatible createContext — returns a context with a `.Provider` component.
|
|
24
|
+
*/
|
|
25
|
+
function createContext(defaultValue) {
|
|
26
|
+
const ctx = createContext$1(defaultValue);
|
|
27
|
+
const Provider = ((props) => {
|
|
28
|
+
pushContext(new Map([[ctx.id, props.value]]));
|
|
29
|
+
onUnmount(() => popContext());
|
|
30
|
+
return props.children;
|
|
31
|
+
});
|
|
32
|
+
return {
|
|
33
|
+
...ctx,
|
|
34
|
+
Provider
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
23
38
|
* Preact-compatible class-based Component.
|
|
24
39
|
*
|
|
25
40
|
* Wraps Pyreon's signal-based reactivity so `setState` triggers re-renders.
|
|
@@ -79,10 +94,6 @@ function cloneElement(vnode, props, ...children) {
|
|
|
79
94
|
key: props?.key ?? vnode.key
|
|
80
95
|
};
|
|
81
96
|
}
|
|
82
|
-
/**
|
|
83
|
-
* Flatten children into a flat array, filtering out null/undefined/boolean.
|
|
84
|
-
* Matches Preact's `toChildArray` utility.
|
|
85
|
-
*/
|
|
86
97
|
function toChildArray(children) {
|
|
87
98
|
const result = [];
|
|
88
99
|
flatten(children, result);
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @pyreon/preact-compat\n *\n * Preact-compatible API shim that runs on Pyreon's reactive engine.\n *\n * Provides the core Preact API surface: h, Fragment, render, hydrate,\n * Component class, createContext, createRef, cloneElement, toChildArray,\n * isValidElement, and the options hook object.\n *\n * For hooks, import from \"@pyreon/preact-compat/hooks\".\n * For signals, import from \"@pyreon/preact-compat/signals\".\n */\n\nimport type { Props, VNode, VNodeChild } from \"@pyreon/core\"\nimport {
|
|
1
|
+
{"version":3,"file":"index.js","names":["pyreonCreateContext"],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @pyreon/preact-compat\n *\n * Preact-compatible API shim that runs on Pyreon's reactive engine.\n *\n * Provides the core Preact API surface: h, Fragment, render, hydrate,\n * Component class, createContext, createRef, cloneElement, toChildArray,\n * isValidElement, and the options hook object.\n *\n * For hooks, import from \"@pyreon/preact-compat/hooks\".\n * For signals, import from \"@pyreon/preact-compat/signals\".\n */\n\nimport type { ComponentFn, Props, VNode, VNodeChild } from \"@pyreon/core\"\nimport {\n createRef,\n Fragment,\n onUnmount,\n popContext,\n pushContext,\n createContext as pyreonCreateContext,\n h as pyreonH,\n useContext,\n} from \"@pyreon/core\"\nimport { batch, signal } from \"@pyreon/reactivity\"\nimport { hydrateRoot, mount } from \"@pyreon/runtime-dom\"\n\n// ─── Core JSX ────────────────────────────────────────────────────────────────\n\n/** Preact's hyperscript function — maps directly to Pyreon's h() */\nexport { pyreonH as h }\n\n/** Alias: Preact also exports createElement */\nexport const createElement = pyreonH\n\nexport { Fragment }\n\n// ─── Render / Hydrate ────────────────────────────────────────────────────────\n\n/**\n * Preact's `render(vnode, container)`.\n * Maps to Pyreon's `mount(vnode, container)`.\n */\nexport function render(vnode: VNodeChild, container: Element): void {\n mount(vnode, container)\n}\n\n/**\n * Preact's `hydrate(vnode, container)`.\n * Maps to Pyreon's `hydrateRoot(container, vnode)`.\n */\nexport function hydrate(vnode: VNodeChild, container: Element): void {\n hydrateRoot(container, vnode as VNode)\n}\n\n// ─── Context ─────────────────────────────────────────────────────────────────\n\nexport interface PreactContext<T> {\n readonly id: symbol\n readonly defaultValue: T\n Provider: ComponentFn<{ value: T; children?: VNodeChild }>\n}\n\n/**\n * Preact-compatible createContext — returns a context with a `.Provider` component.\n */\nexport function createContext<T>(defaultValue: T): PreactContext<T> {\n const ctx = pyreonCreateContext<T>(defaultValue)\n const Provider = ((props: { value: T; children?: VNodeChild }) => {\n pushContext(new Map([[ctx.id, props.value]]))\n onUnmount(() => popContext())\n return props.children as VNode | null\n }) as ComponentFn<{ value: T; children?: VNodeChild }>\n return { ...ctx, Provider }\n}\n\nexport { useContext }\n\n// ─── Refs ────────────────────────────────────────────────────────────────────\n\nexport { createRef }\n\n// ─── Component class ─────────────────────────────────────────────────────────\n\n/**\n * Preact-compatible class-based Component.\n *\n * Wraps Pyreon's signal-based reactivity so `setState` triggers re-renders.\n * Usage: `class MyComp extends Component { render() { ... } }`\n */\nexport class Component<\n P extends Props = Props,\n S extends Record<string, unknown> = Record<string, unknown>,\n> {\n props: P\n state: S\n private _stateSignal: ReturnType<typeof signal<S>>\n\n constructor(props: P) {\n this.props = props\n this.state = {} as S\n this._stateSignal = signal<S>(this.state)\n }\n\n /**\n * Update state — accepts a partial state object or an updater function.\n * Merges into existing state (shallow merge, like Preact/React).\n */\n setState(partial: Partial<S> | ((prev: S) => Partial<S>)): void {\n batch(() => {\n const current = this._stateSignal()\n const update =\n typeof partial === \"function\" ? (partial as (prev: S) => Partial<S>)(current) : partial\n const next = { ...current, ...update } as S\n this.state = next\n this._stateSignal.set(next)\n })\n }\n\n /**\n * Force a re-render. In Pyreon this triggers the state signal to re-fire.\n */\n forceUpdate(): void {\n this._stateSignal.set({ ...this.state })\n }\n\n /**\n * Override in subclass to return VNode tree.\n */\n render(): VNodeChild {\n return null\n }\n}\n\n// ─── cloneElement ────────────────────────────────────────────────────────────\n\n/**\n * Clone a VNode with merged props (like Preact's cloneElement).\n */\nexport function cloneElement(vnode: VNode, props?: Props, ...children: VNodeChild[]): VNode {\n const mergedProps = { ...vnode.props, ...(props ?? {}) }\n const mergedChildren = children.length > 0 ? children : vnode.children\n return {\n type: vnode.type,\n props: mergedProps,\n children: mergedChildren,\n key: (props?.key as string | number | null) ?? vnode.key,\n }\n}\n\n// ─── toChildArray ────────────────────────────────────────────────────────────\n\n/**\n * Flatten children into a flat array, filtering out null/undefined/boolean.\n * Matches Preact's `toChildArray` utility.\n */\ntype NestedChildren = VNodeChild | NestedChildren[]\n\nexport function toChildArray(children: NestedChildren): VNodeChild[] {\n const result: VNodeChild[] = []\n flatten(children, result)\n return result\n}\n\nfunction flatten(value: NestedChildren, out: VNodeChild[]): void {\n if (value == null || typeof value === \"boolean\") return\n if (Array.isArray(value)) {\n for (const child of value) {\n flatten(child, out)\n }\n } else {\n out.push(value as VNodeChild)\n }\n}\n\n// ─── isValidElement ──────────────────────────────────────────────────────────\n\n/**\n * Check if a value is a VNode (like Preact's isValidElement).\n */\nexport function isValidElement(x: unknown): x is VNode {\n return (\n x !== null &&\n typeof x === \"object\" &&\n \"type\" in (x as Record<string, unknown>) &&\n \"props\" in (x as Record<string, unknown>) &&\n \"children\" in (x as Record<string, unknown>)\n )\n}\n\n// ─── options ─────────────────────────────────────────────────────────────────\n\n/**\n * Preact's plugin/hook system. Exposed as an empty object for compatibility\n * with libraries that check for `options._hook`, `options.vnode`, etc.\n */\nexport const options: Record<string, unknown> = {}\n"],"mappings":";;;;;;AAiCA,MAAa,gBAAgB;;;;;AAU7B,SAAgB,OAAO,OAAmB,WAA0B;AAClE,OAAM,OAAO,UAAU;;;;;;AAOzB,SAAgB,QAAQ,OAAmB,WAA0B;AACnE,aAAY,WAAW,MAAe;;;;;AAcxC,SAAgB,cAAiB,cAAmC;CAClE,MAAM,MAAMA,gBAAuB,aAAa;CAChD,MAAM,aAAa,UAA+C;AAChE,cAAY,IAAI,IAAI,CAAC,CAAC,IAAI,IAAI,MAAM,MAAM,CAAC,CAAC,CAAC;AAC7C,kBAAgB,YAAY,CAAC;AAC7B,SAAO,MAAM;;AAEf,QAAO;EAAE,GAAG;EAAK;EAAU;;;;;;;;AAiB7B,IAAa,YAAb,MAGE;CACA;CACA;CACA,AAAQ;CAER,YAAY,OAAU;AACpB,OAAK,QAAQ;AACb,OAAK,QAAQ,EAAE;AACf,OAAK,eAAe,OAAU,KAAK,MAAM;;;;;;CAO3C,SAAS,SAAuD;AAC9D,cAAY;GACV,MAAM,UAAU,KAAK,cAAc;GACnC,MAAM,SACJ,OAAO,YAAY,aAAc,QAAoC,QAAQ,GAAG;GAClF,MAAM,OAAO;IAAE,GAAG;IAAS,GAAG;IAAQ;AACtC,QAAK,QAAQ;AACb,QAAK,aAAa,IAAI,KAAK;IAC3B;;;;;CAMJ,cAAoB;AAClB,OAAK,aAAa,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC;;;;;CAM1C,SAAqB;AACnB,SAAO;;;;;;AASX,SAAgB,aAAa,OAAc,OAAe,GAAG,UAA+B;CAC1F,MAAM,cAAc;EAAE,GAAG,MAAM;EAAO,GAAI,SAAS,EAAE;EAAG;CACxD,MAAM,iBAAiB,SAAS,SAAS,IAAI,WAAW,MAAM;AAC9D,QAAO;EACL,MAAM,MAAM;EACZ,OAAO;EACP,UAAU;EACV,KAAM,OAAO,OAAkC,MAAM;EACtD;;AAWH,SAAgB,aAAa,UAAwC;CACnE,MAAM,SAAuB,EAAE;AAC/B,SAAQ,UAAU,OAAO;AACzB,QAAO;;AAGT,SAAS,QAAQ,OAAuB,KAAyB;AAC/D,KAAI,SAAS,QAAQ,OAAO,UAAU,UAAW;AACjD,KAAI,MAAM,QAAQ,MAAM,CACtB,MAAK,MAAM,SAAS,MAClB,SAAQ,OAAO,IAAI;KAGrB,KAAI,KAAK,MAAoB;;;;;AASjC,SAAgB,eAAe,GAAwB;AACrD,QACE,MAAM,QACN,OAAO,MAAM,YACb,UAAW,KACX,WAAY,KACZ,cAAe;;;;;;AAUnB,MAAa,UAAmC,EAAE"}
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Fragment, Props, VNode, VNodeChild,
|
|
1
|
+
import { ComponentFn, Fragment, Props, VNode, VNodeChild, createRef, h as pyreonH, useContext } from "@pyreon/core";
|
|
2
2
|
|
|
3
3
|
//#region src/index.d.ts
|
|
4
4
|
/** Alias: Preact also exports createElement */
|
|
@@ -13,6 +13,18 @@ declare function render(vnode: VNodeChild, container: Element): void;
|
|
|
13
13
|
* Maps to Pyreon's `hydrateRoot(container, vnode)`.
|
|
14
14
|
*/
|
|
15
15
|
declare function hydrate(vnode: VNodeChild, container: Element): void;
|
|
16
|
+
interface PreactContext<T> {
|
|
17
|
+
readonly id: symbol;
|
|
18
|
+
readonly defaultValue: T;
|
|
19
|
+
Provider: ComponentFn<{
|
|
20
|
+
value: T;
|
|
21
|
+
children?: VNodeChild;
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Preact-compatible createContext — returns a context with a `.Provider` component.
|
|
26
|
+
*/
|
|
27
|
+
declare function createContext<T>(defaultValue: T): PreactContext<T>;
|
|
16
28
|
/**
|
|
17
29
|
* Preact-compatible class-based Component.
|
|
18
30
|
*
|
|
@@ -46,7 +58,8 @@ declare function cloneElement(vnode: VNode, props?: Props, ...children: VNodeChi
|
|
|
46
58
|
* Flatten children into a flat array, filtering out null/undefined/boolean.
|
|
47
59
|
* Matches Preact's `toChildArray` utility.
|
|
48
60
|
*/
|
|
49
|
-
|
|
61
|
+
type NestedChildren = VNodeChild | NestedChildren[];
|
|
62
|
+
declare function toChildArray(children: NestedChildren): VNodeChild[];
|
|
50
63
|
/**
|
|
51
64
|
* Check if a value is a VNode (like Preact's isValidElement).
|
|
52
65
|
*/
|
|
@@ -57,5 +70,5 @@ declare function isValidElement(x: unknown): x is VNode;
|
|
|
57
70
|
*/
|
|
58
71
|
declare const options: Record<string, unknown>;
|
|
59
72
|
//#endregion
|
|
60
|
-
export { Component, Fragment, cloneElement, createContext, createElement, createRef, pyreonH as h, hydrate, isValidElement, options, render, toChildArray, useContext };
|
|
73
|
+
export { Component, Fragment, PreactContext, cloneElement, createContext, createElement, createRef, pyreonH as h, hydrate, isValidElement, options, render, toChildArray, useContext };
|
|
61
74
|
//# sourceMappingURL=index2.d.ts.map
|
package/lib/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index2.d.ts","names":[],"sources":["../../src/index.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"index2.d.ts","names":[],"sources":["../../src/index.ts"],"mappings":";;;;cAiCa,aAAA,SAAa,OAAA;;;;;iBAUV,MAAA,CAAO,KAAA,EAAO,UAAA,EAAY,SAAA,EAAW,OAAA;;;;;iBAQrC,OAAA,CAAQ,KAAA,EAAO,UAAA,EAAY,SAAA,EAAW,OAAA;AAAA,UAMrC,aAAA;EAAA,SACN,EAAA;EAAA,SACA,YAAA,EAAc,CAAA;EACvB,QAAA,EAAU,WAAA;IAAc,KAAA,EAAO,CAAA;IAAG,QAAA,GAAW,UAAA;EAAA;AAAA;;;;iBAM/B,aAAA,GAAA,CAAiB,YAAA,EAAc,CAAA,GAAI,aAAA,CAAc,CAAA;;;;;;;cAwBpD,SAAA,WACD,KAAA,GAAQ,KAAA,YACR,MAAA,oBAA0B,MAAA;EAEpC,KAAA,EAAO,CAAA;EACP,KAAA,EAAO,CAAA;EAAA,QACC,YAAA;cAEI,KAAA,EAAO,CAAA;EAhC8B;;;;EA0CjD,QAAA,CAAS,OAAA,EAAS,OAAA,CAAQ,CAAA,MAAO,IAAA,EAAM,CAAA,KAAM,OAAA,CAAQ,CAAA;EA1CtB;;;EAwD/B,WAAA,CAAA;EAxDgE;AAwBlE;;EAuCE,MAAA,CAAA,GAAU,UAAA;AAAA;;;;iBAUI,YAAA,CAAa,KAAA,EAAO,KAAA,EAAO,KAAA,GAAQ,KAAA,KAAU,QAAA,EAAU,UAAA,KAAe,KAAA;;;;;KAiBjF,cAAA,GAAiB,UAAA,GAAa,cAAA;AAAA,iBAEnB,YAAA,CAAa,QAAA,EAAU,cAAA,GAAiB,UAAA;;;;iBAsBxC,cAAA,CAAe,CAAA,YAAa,CAAA,IAAK,KAAA;;;;;cAgBpC,OAAA,EAAS,MAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/preact-compat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Preact-compatible API shim for Pyreon — write Preact-style code that runs on Pyreon's reactive engine",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -45,14 +45,13 @@
|
|
|
45
45
|
"dev": "vl_rolldown_build-watch",
|
|
46
46
|
"test": "vitest run",
|
|
47
47
|
"typecheck": "tsc --noEmit",
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"postpack": "bun run ../../scripts/resolve-workspace-deps.ts post"
|
|
48
|
+
"lint": "biome check .",
|
|
49
|
+
"prepublishOnly": "bun run build"
|
|
51
50
|
},
|
|
52
51
|
"dependencies": {
|
|
53
|
-
"@pyreon/core": "
|
|
54
|
-
"@pyreon/reactivity": "
|
|
55
|
-
"@pyreon/runtime-dom": "
|
|
52
|
+
"@pyreon/core": "workspace:*",
|
|
53
|
+
"@pyreon/reactivity": "workspace:*",
|
|
54
|
+
"@pyreon/runtime-dom": "workspace:*"
|
|
56
55
|
},
|
|
57
56
|
"devDependencies": {
|
|
58
57
|
"@happy-dom/global-registrator": "^20.8.3",
|
package/src/hooks.ts
CHANGED
|
@@ -73,10 +73,8 @@ export function useMemo<T>(fn: () => T, _deps?: unknown[]): () => T {
|
|
|
73
73
|
* Drop-in for Preact's `useCallback`.
|
|
74
74
|
* Components run once in Pyreon — returns `fn` as-is.
|
|
75
75
|
*/
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
_deps?: unknown[],
|
|
79
|
-
): T {
|
|
76
|
+
// biome-ignore lint/suspicious/noExplicitAny: any is needed for contravariant function params
|
|
77
|
+
export function useCallback<T extends (...args: any[]) => any>(fn: T, _deps?: unknown[]): T {
|
|
80
78
|
return fn
|
|
81
79
|
}
|
|
82
80
|
|
package/src/index.ts
CHANGED
|
@@ -11,8 +11,17 @@
|
|
|
11
11
|
* For signals, import from "@pyreon/preact-compat/signals".
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import type { Props, VNode, VNodeChild } from "@pyreon/core"
|
|
15
|
-
import {
|
|
14
|
+
import type { ComponentFn, Props, VNode, VNodeChild } from "@pyreon/core"
|
|
15
|
+
import {
|
|
16
|
+
createRef,
|
|
17
|
+
Fragment,
|
|
18
|
+
onUnmount,
|
|
19
|
+
popContext,
|
|
20
|
+
pushContext,
|
|
21
|
+
createContext as pyreonCreateContext,
|
|
22
|
+
h as pyreonH,
|
|
23
|
+
useContext,
|
|
24
|
+
} from "@pyreon/core"
|
|
16
25
|
import { batch, signal } from "@pyreon/reactivity"
|
|
17
26
|
import { hydrateRoot, mount } from "@pyreon/runtime-dom"
|
|
18
27
|
|
|
@@ -46,7 +55,26 @@ export function hydrate(vnode: VNodeChild, container: Element): void {
|
|
|
46
55
|
|
|
47
56
|
// ─── Context ─────────────────────────────────────────────────────────────────
|
|
48
57
|
|
|
49
|
-
export
|
|
58
|
+
export interface PreactContext<T> {
|
|
59
|
+
readonly id: symbol
|
|
60
|
+
readonly defaultValue: T
|
|
61
|
+
Provider: ComponentFn<{ value: T; children?: VNodeChild }>
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Preact-compatible createContext — returns a context with a `.Provider` component.
|
|
66
|
+
*/
|
|
67
|
+
export function createContext<T>(defaultValue: T): PreactContext<T> {
|
|
68
|
+
const ctx = pyreonCreateContext<T>(defaultValue)
|
|
69
|
+
const Provider = ((props: { value: T; children?: VNodeChild }) => {
|
|
70
|
+
pushContext(new Map([[ctx.id, props.value]]))
|
|
71
|
+
onUnmount(() => popContext())
|
|
72
|
+
return props.children as VNode | null
|
|
73
|
+
}) as ComponentFn<{ value: T; children?: VNodeChild }>
|
|
74
|
+
return { ...ctx, Provider }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export { useContext }
|
|
50
78
|
|
|
51
79
|
// ─── Refs ────────────────────────────────────────────────────────────────────
|
|
52
80
|
|
|
@@ -126,20 +154,22 @@ export function cloneElement(vnode: VNode, props?: Props, ...children: VNodeChil
|
|
|
126
154
|
* Flatten children into a flat array, filtering out null/undefined/boolean.
|
|
127
155
|
* Matches Preact's `toChildArray` utility.
|
|
128
156
|
*/
|
|
129
|
-
|
|
157
|
+
type NestedChildren = VNodeChild | NestedChildren[]
|
|
158
|
+
|
|
159
|
+
export function toChildArray(children: NestedChildren): VNodeChild[] {
|
|
130
160
|
const result: VNodeChild[] = []
|
|
131
161
|
flatten(children, result)
|
|
132
162
|
return result
|
|
133
163
|
}
|
|
134
164
|
|
|
135
|
-
function flatten(value:
|
|
165
|
+
function flatten(value: NestedChildren, out: VNodeChild[]): void {
|
|
136
166
|
if (value == null || typeof value === "boolean") return
|
|
137
167
|
if (Array.isArray(value)) {
|
|
138
168
|
for (const child of value) {
|
|
139
|
-
flatten(child
|
|
169
|
+
flatten(child, out)
|
|
140
170
|
}
|
|
141
171
|
} else {
|
|
142
|
-
out.push(value)
|
|
172
|
+
out.push(value as VNodeChild)
|
|
143
173
|
}
|
|
144
174
|
}
|
|
145
175
|
|