@preact/signals 1.0.2 → 1.0.3
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/CHANGELOG.md +6 -0
- package/README.md +112 -0
- package/dist/signals.js +2 -1
- package/dist/signals.js.map +1 -1
- package/dist/signals.min.js +2 -1
- package/dist/signals.min.js.map +1 -1
- package/dist/signals.mjs +2 -1
- package/dist/signals.mjs.map +1 -1
- package/dist/signals.module.js +2 -1
- package/dist/signals.module.js.map +1 -1
- package/package.json +5 -2
- package/src/index.ts +12 -6
- package/test/index.test.ts +49 -22
package/CHANGELOG.md
CHANGED
package/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
|
|
2
|
+
# Signals
|
|
3
|
+
|
|
4
|
+
Signals is a performant state management library with two primary goals:
|
|
5
|
+
|
|
6
|
+
1. Make it as easy as possible to write business logic for small up to complex apps. No matter how complex your logic is, your app updates should stay fast without you needing to think about it. Signals automatically optimize state updates behind the scenes to trigger the fewest updates necessary. They are lazy by default and automatically skip signals that no one listens to.
|
|
7
|
+
2. Integrate into frameworks as if they were native built-in primitives. You don't need any selectors, wrapper functions, or anything else. Signals can be accessed directly and your component will automatically re-render when the signal's value changes.
|
|
8
|
+
|
|
9
|
+
Read the [announcement post](https://preactjs.com/blog/introducing-signals/) to learn more about which problems signals solves and how it came to be.
|
|
10
|
+
|
|
11
|
+
## Installation:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm install @preact/signals
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
- [Guide / API](../../README.md#guide--api)
|
|
18
|
+
- [`signal(initialValue)`](../../README.md#signalinitialvalue)
|
|
19
|
+
- [`signal.peek()`](../../README.md#signalpeek)
|
|
20
|
+
- [`computed(fn)`](../../README.md#computedfn)
|
|
21
|
+
- [`effect(fn)`](../../README.md#effectfn)
|
|
22
|
+
- [`batch(fn)`](../../README.md#batchfn)
|
|
23
|
+
- [Preact Integration](#preact-integration)
|
|
24
|
+
- [Hooks](#hooks)
|
|
25
|
+
- [Rendering optimizations](#rendering-optimizations)
|
|
26
|
+
- [Attribute optimization (experimental)](#attribute-optimization-experimental)
|
|
27
|
+
- [License](#license)
|
|
28
|
+
|
|
29
|
+
## Preact Integration
|
|
30
|
+
|
|
31
|
+
The Preact integration can be installed via:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
npm install @preact/signals
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
It allows you to access signals as if they were native to Preact. Whenever you read a signal inside a component we'll automatically subscribe the component to that. When you update the signal we'll know that this component needs to be updated and will do that for you.
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
// The Preact adapter re-exports the core library
|
|
41
|
+
import { signal } from "@preact/signals";
|
|
42
|
+
|
|
43
|
+
const count = signal(0);
|
|
44
|
+
|
|
45
|
+
function CounterValue() {
|
|
46
|
+
// Whenever the `count` signal is updated, we'll
|
|
47
|
+
// re-render this component automatically for you
|
|
48
|
+
return <p>Value: {count.value}</p>;
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Hooks
|
|
53
|
+
|
|
54
|
+
If you need to instantiate new signals inside your components, you can use the `useSignal` or `useComputed` hook.
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
import { useSignal, useComputed } from "@preact/signals";
|
|
58
|
+
|
|
59
|
+
function Counter() {
|
|
60
|
+
const count = useSignal(0);
|
|
61
|
+
const double = useComputed(() => count.value * 2);
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<button onClick={() => count.value++}>
|
|
65
|
+
Value: {count.value}, value x 2 = {double.value}
|
|
66
|
+
</button>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Rendering optimizations
|
|
72
|
+
|
|
73
|
+
The Preact adapter ships with several optimizations it can apply out of the box to skip virtual-dom rendering entirely. If you pass a signal directly into JSX, it will bind directly to the DOM `Text` node that is created and update that whenever the signal changes.
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
import { signal } from "@preact/signals";
|
|
77
|
+
|
|
78
|
+
const count = signal(0);
|
|
79
|
+
|
|
80
|
+
// Unoptimized: Will trigger the surrounding
|
|
81
|
+
// component to re-render
|
|
82
|
+
function Counter() {
|
|
83
|
+
return <p>Value: {count.value}</p>;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Optimized: Will update the text node directly
|
|
87
|
+
function Counter() {
|
|
88
|
+
return <p>Value: {count}</p>;
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
To opt into this optimization, simply pass the signal directly instead of accessing the `.value` property.
|
|
93
|
+
|
|
94
|
+
#### Attribute optimization (experimental)
|
|
95
|
+
|
|
96
|
+
We can also pass signals directly as an attribute to an HTML element node.
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
import { signal } from "@preact/signals";
|
|
100
|
+
|
|
101
|
+
const inputValue = signal("foobar");
|
|
102
|
+
|
|
103
|
+
function Person() {
|
|
104
|
+
return <input value={inputValue} />;
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
This way we'll bypass checking the virtual-dom and update the DOM property directly.
|
|
109
|
+
|
|
110
|
+
## License
|
|
111
|
+
|
|
112
|
+
`MIT`, see the [LICENSE](../../LICENSE) file.
|
package/dist/signals.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
var n,r,t,
|
|
1
|
+
var n,r,t,e=require("preact"),i=require("preact/hooks"),u=require("@preact/signals-core"),o=new WeakSet,f=new WeakSet,c=new WeakSet;function a(n,r){e.options[n]=r.bind(null,e.options[n]||function(){})}var v=new WeakMap;function s(n){t&&t(!0,!0),r=n,t=n&&n._()}function l(n){var r=u.signal(void 0);return r._c=!0,r._u=n,r}function b(n){var r=v.get(n);if(r)r.__.length=0;else{var t=[];(r=l(function(){for(var r=n.__e,e=0;e<t.length;e++){var i=t[e],u=i.t,o=i.i._v;if(!r)return;u in r?r[u]=o:o?r.setAttribute(u,o):r.removeAttribute(u)}})).__=t,v.set(n,r)}return r}function p(n,r,t){"object"!=typeof n||null==n||(Array.isArray(n)?n.forEach(p):n instanceof u.Signal&&(t[r]=e.createElement(_,{data:n})))}function _(n){var t=this,e=n.data,o=h(e);o.value=e;var f=i.useMemo(function(){for(var n=t.__v;n=n.__;)if(n.__c){c.add(n.__c);break}return r._u=function(){t.base.data=f._v},u.computed(function(){var n=o.value.value;return 0===n?0:!0===n?"":n||""})},[]);return f.value}function h(n){return i.useMemo(function(){return u.signal(n)},[])}_.displayName="_st",a("__b",function(n,r){if("string"==typeof r.type){var t,e=r.props;for(var i in e){var o=e[i];"children"===i?p(o,"children",e):o instanceof u.Signal&&function(){t||(t=b(r)),t.__.push({t:i,i:o});var n=t._u;if(o._u){var u=o._u;o._u=function(){n(),u()}}else o._u=n;e[i]=o.peek()}()}s(t)}n(r)}),a("__r",function(r,t){var e,i=t.__c;i&&(o.delete(i),void 0===(e=v.get(i))&&(e=l(function(){o.add(i),i.setState({})}),v.set(i,e))),n=i,s(e),r(t)}),a("__e",function(r,t,e,i){s(),n=void 0,r(t,e,i)}),a("diffed",function(r,t){s(),n=void 0,r(t)}),a("unmount",function(n,r){var t=r.__c||r,e=v.get(t);if(e){v.delete(t);var i=e._d;i&&(i.forEach(function(n){return n._s.delete(e)}),i.clear())}n(r)}),a("__h",function(n,r,t,e){e<3&&f.add(r),n(r,t,e)}),e.Component.prototype.shouldComponentUpdate=function(n,r){var t,e=v.get(this);if(!(e&&0!==(null==(t=e._d)?void 0:t.size)||c.has(this)))return!0;if(o.has(this))return!0;if(f.has(this))return!0;for(var i in r)return!0;for(var u in n)if("__source"!==u&&n[u]!==this.props[u])return!0;for(var a in this.props)if(!(a in n))return!0;return!1},Object.defineProperty(exports,"Signal",{enumerable:!0,get:function(){return u.Signal}}),Object.defineProperty(exports,"batch",{enumerable:!0,get:function(){return u.batch}}),Object.defineProperty(exports,"computed",{enumerable:!0,get:function(){return u.computed}}),Object.defineProperty(exports,"effect",{enumerable:!0,get:function(){return u.effect}}),Object.defineProperty(exports,"signal",{enumerable:!0,get:function(){return u.signal}}),exports.useComputed=function(r){var t=i.useRef(r);return t.current=r,c.add(n),i.useMemo(function(){return u.computed(function(){return t.current()})},[])},exports.useSignal=h;
|
|
2
|
+
//# sourceMappingURL=signals.js.map
|
package/dist/signals.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signals.js","sources":["../src/index.ts"],"sourcesContent":["import { options, Component, createElement } from \"preact\";\nimport { useRef, useMemo } from \"preact/hooks\";\nimport {\n\tsignal,\n\tcomputed,\n\tbatch,\n\teffect,\n\tSignal,\n\ttype ReadonlySignal,\n} from \"@preact/signals-core\";\nimport {\n\tVNode,\n\tComponentType,\n\tOptionsTypes,\n\tHookFn,\n\tUpdater,\n\tElementUpdater,\n} from \"./internal\";\n\nexport { signal, computed, batch, effect, Signal, type ReadonlySignal };\n\n// Components that have a pending Signal update: (used to bypass default sCU:false)\nconst hasPendingUpdate = new WeakSet<Component>();\n\n// Components that have useState()/useReducer() hooks:\nconst hasHookState = new WeakSet<Component>();\n\n// Components that have useComputed():\nconst hasComputeds = new WeakSet<Component>();\n\n// Install a Preact options hook\nfunction hook<T extends OptionsTypes>(hookName: T, hookFn: HookFn<T>) {\n\t// @ts-ignore-next-line private options hooks usage\n\toptions[hookName] = hookFn.bind(null, options[hookName] || (() => {}));\n}\n\nlet currentComponent: Component | undefined;\nlet currentUpdater: Updater | undefined;\nlet finishUpdate: ReturnType<Updater[\"_setCurrent\"]> | undefined;\nconst updaterForComponent = new WeakMap<Component | VNode, Updater>();\n\nfunction setCurrentUpdater(updater?: Updater) {\n\t// end tracking for the current update:\n\tif (finishUpdate) finishUpdate(true, true);\n\t// start tracking the new update:\n\tcurrentUpdater = updater;\n\tfinishUpdate = updater && updater._setCurrent();\n}\n\nfunction createUpdater(updater: () => void) {\n\tconst s = signal(undefined) as Updater;\n\ts._canActivate = true;\n\ts._updater = updater;\n\treturn s;\n}\n\n// Get a (cached) Signal property updater for an element VNode\nfunction getElementUpdater(vnode: VNode) {\n\tlet updater = updaterForComponent.get(vnode) as ElementUpdater;\n\tif (!updater) {\n\t\tlet signalProps: Array<{ _key: string, _signal: Signal }> = [];\n\t\tupdater = createUpdater(() => {\n\t\t\tlet dom = vnode.__e as Element;\n\n\t\t\tfor (let i = 0; i < signalProps.length; i++) {\n\t\t\t\tlet { _key: prop, _signal: signal } = signalProps[i];\n\t\t\t\tlet value = signal._value;\n\t\t\t\tif (!dom) return;\n\t\t\t\tif (prop in dom) {\n\t\t\t\t\t// @ts-ignore-next-line silly\n\t\t\t\t\tdom[prop] = value;\n\t\t\t\t} else if (value) {\n\t\t\t\t\tdom.setAttribute(prop, value);\n\t\t\t\t} else {\n\t\t\t\t\tdom.removeAttribute(prop);\n\t\t\t\t}\n\t\t\t}\n\t\t}) as ElementUpdater;\n\t\tupdater._props = signalProps;\n\t\tupdaterForComponent.set(vnode, updater);\n\t} else {\n\t\tupdater._props.length = 0;\n\t}\n\treturn updater;\n}\n\n/** @todo This may be needed for complex prop value detection. */\n// function isSignalValue(value: any): value is Signal {\n// \tif (typeof value !== \"object\" || value == null) return false;\n// \tif (value instanceof Signal) return true;\n// \t// @TODO: uncomment this when we land Reactive (ideally behind a brand check)\n// \t// for (let i in value) if (value[i] instanceof Signal) return true;\n// \treturn false;\n// }\n\n/** Convert Signals within (nested) props.children into Text components */\nfunction childToSignal<T>(child: any, i: keyof T, arr: T) {\n\tif (typeof child !== \"object\" || child == null) {\n\t\t// can't be a signal\n\t} else if (Array.isArray(child)) {\n\t\tchild.forEach(childToSignal);\n\t} else if (child instanceof Signal) {\n\t\t// @ts-ignore-next-line yes, arr can accept VNodes:\n\t\tarr[i] = createElement(Text, { data: child });\n\t}\n}\n\n/**\n * A wrapper component that renders a Signal directly as a Text node.\n * @todo: in Preact 11, just decorate Signal with `type:null`\n */\nfunction Text(this: ComponentType, { data }: { data: Signal }) {\n\t// hasComputeds.add(this);\n\n\tconst s = useMemo(() => {\n\t\t// mark the parent component as having computeds so it gets optimized\n\t\tlet v = this.__v;\n\t\twhile ((v = v.__!)) {\n\t\t\tif (v.__c) {\n\t\t\t\thasComputeds.add(v.__c);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Replace this component's vdom updater with a direct text one:\n\t\tcurrentUpdater!._updater = () => {\n\t\t\t(this.base as Text).data = s._value;\n\t\t};\n\n\t\treturn computed(() => {\n\t\t\tlet s = data.value;\n\t\t\treturn s === 0 ? 0 : s === true ? \"\" : s || \"\";\n\t\t});\n\t}, []);\n\n\treturn s.value;\n}\nText.displayName = \"_st\";\n\n/** Inject low-level property/attribute bindings for Signals into Preact's diff */\nhook(OptionsTypes.DIFF, (old, vnode) => {\n\tif (typeof vnode.type === \"string\") {\n\t\t// let orig = vnode.__o || vnode;\n\t\tlet props = vnode.props;\n\t\tlet updater;\n\n\t\tfor (let i in props) {\n\t\t\tlet value = props[i];\n\t\t\tif (i === \"children\") {\n\t\t\t\tchildToSignal(value, \"children\", props);\n\t\t\t} else if (value instanceof Signal) {\n\t\t\t\t// first Signal prop triggers creation/cleanup of the updater:\n\t\t\t\tif (!updater) updater = getElementUpdater(vnode);\n\t\t\t\t// track which props are Signals for precise updates:\n\t\t\t\tupdater._props.push({ _key: i, _signal: value });\n\t\t\t\tlet newUpdater = updater._updater\n\t\t\t\tif (value._updater) {\n\t\t\t\t\tlet oldUpdater = value._updater\n\t\t\t\t\tvalue._updater = () => {\n\t\t\t\t\t\tnewUpdater();\n\t\t\t\t\t\toldUpdater();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tvalue._updater = newUpdater\n\t\t\t\t}\n\t\t\t\tprops[i] = value.peek()\n\t\t\t}\n\t\t}\n\n\t\tsetCurrentUpdater(updater);\n\t}\n\n\told(vnode);\n});\n\n/** Set up Updater before rendering a component */\nhook(OptionsTypes.RENDER, (old, vnode) => {\n\tlet updater;\n\n\tlet component = vnode.__c;\n\tif (component) {\n\t\thasPendingUpdate.delete(component);\n\n\t\tupdater = updaterForComponent.get(component);\n\t\tif (updater === undefined) {\n\t\t\tupdater = createUpdater(() => {\n\t\t\t\thasPendingUpdate.add(component);\n\t\t\t\tcomponent.setState({});\n\t\t\t});\n\t\t\tupdaterForComponent.set(component, updater);\n\t\t}\n\t}\n\n\tcurrentComponent = component;\n\tsetCurrentUpdater(updater);\n\told(vnode);\n});\n\n/** Finish current updater if a component errors */\nhook(OptionsTypes.CATCH_ERROR, (old, error, vnode, oldVNode) => {\n\tsetCurrentUpdater();\n\tcurrentComponent = undefined;\n\told(error, vnode, oldVNode);\n});\n\n/** Finish current updater after rendering any VNode */\nhook(OptionsTypes.DIFFED, (old, vnode) => {\n\tsetCurrentUpdater();\n\tcurrentComponent = undefined;\n\told(vnode);\n});\n\n/** Unsubscribe from Signals when unmounting components/vnodes */\nhook(OptionsTypes.UNMOUNT, (old, vnode: VNode) => {\n\tlet thing = vnode.__c || vnode;\n\tconst updater = updaterForComponent.get(thing);\n\tif (updater) {\n\t\tupdaterForComponent.delete(thing);\n\t\tconst signals = updater._deps;\n\t\tif (signals) {\n\t\t\tsignals.forEach(signal => signal._subs.delete(updater));\n\t\t\tsignals.clear();\n\t\t}\n\t}\n\told(vnode);\n});\n\n/** Mark components that use hook state so we can skip sCU optimization. */\nhook(OptionsTypes.HOOK, (old, component, index, type) => {\n\tif (type < 3) hasHookState.add(component);\n\told(component, index, type);\n});\n\n/**\n * Auto-memoize components that use Signals/Computeds.\n * Note: Does _not_ optimize components that use hook/class state.\n */\nComponent.prototype.shouldComponentUpdate = function (props, state) {\n\t// @todo: Once preactjs/preact#3671 lands, this could just use `currentUpdater`:\n\tconst updater = updaterForComponent.get(this);\n\n\tconst hasSignals = updater && updater._deps?.size !== 0;\n\n\t// let reason;\n\t// if (!hasSignals && !hasComputeds.has(this)) {\n\t// \treason = \"no signals or computeds\";\n\t// } else if (hasPendingUpdate.has(this)) {\n\t// \treason = \"has pending update\";\n\t// } else if (hasHookState.has(this)) {\n\t// \treason = \"has hook state\";\n\t// }\n\t// if (reason) {\n\t// \tif (!this) reason += \" (`this` bug)\";\n\t// \tconsole.log(\"not optimizing\", this?.constructor?.name, \": \", reason, {\n\t// \t\tdetails: {\n\t// \t\t\thasSignals,\n\t// \t\t\thasComputeds: hasComputeds.has(this),\n\t// \t\t\thasPendingUpdate: hasPendingUpdate.has(this),\n\t// \t\t\thasHookState: hasHookState.has(this),\n\t// \t\t\tdeps: Array.from(updater._deps),\n\t// \t\t\tupdater,\n\t// \t\t},\n\t// \t});\n\t// }\n\n\t// if this component used no signals or computeds, update:\n\tif (!hasSignals && !hasComputeds.has(this)) return true;\n\n\t// if there is a pending re-render triggered from Signals, update:\n\tif (hasPendingUpdate.has(this)) return true;\n\n\t// if there is hook or class state, update:\n\tif (hasHookState.has(this)) return true;\n\tfor (let i in state) return true;\n\n\t// if any non-Signal props changed, update:\n\tfor (let i in props) {\n\t\tif (i !== \"__source\" && props[i] !== this.props[i]) return true;\n\t}\n\tfor (let i in this.props) if (!(i in props)) return true;\n\n\t// this is a purely Signal-driven component, don't update:\n\treturn false;\n};\n\nexport function useSignal<T>(value: T) {\n\treturn useMemo(() => signal<T>(value), []);\n}\n\nexport function useComputed<T>(compute: () => T) {\n\tconst $compute = useRef(compute);\n\t$compute.current = compute;\n\thasComputeds.add(currentComponent!);\n\treturn useMemo(() => computed<T>(() => $compute.current()), []);\n}\n\n/**\n * @todo Determine which Reactive implementation we'll be using.\n * @internal\n */\n// export function useReactive<T extends object>(value: T): Reactive<T> {\n// \treturn useMemo(() => reactive<T>(value), []);\n// }\n\n/**\n * @internal\n * Update a Reactive's using the properties of an object or other Reactive.\n * Also works for Signals.\n * @example\n * // Update a Reactive with Object.assign()-like syntax:\n * const r = reactive({ name: \"Alice\" });\n * update(r, { name: \"Bob\" });\n * update(r, { age: 42 }); // property 'age' does not exist in type '{ name?: string }'\n * update(r, 2); // '2' has no properties in common with '{ name?: string }'\n * console.log(r.name.value); // \"Bob\"\n *\n * @example\n * // Update a Reactive with the properties of another Reactive:\n * const A = reactive({ name: \"Alice\" });\n * const B = reactive({ name: \"Bob\", age: 42 });\n * update(A, B);\n * console.log(`${A.name} is ${A.age}`); // \"Bob is 42\"\n *\n * @example\n * // Update a signal with assign()-like syntax:\n * const s = signal(42);\n * update(s, \"hi\"); // Argument type 'string' not assignable to type 'number'\n * update(s, {}); // Argument type '{}' not assignable to type 'number'\n * update(s, 43);\n * console.log(s.value); // 43\n *\n * @param obj The Reactive or Signal to be updated\n * @param update The value, Signal, object or Reactive to update `obj` to match\n * @param overwrite If `true`, any properties `obj` missing from `update` are set to `undefined`\n */\n/*\nexport function update<T extends SignalOrReactive>(\n\tobj: T,\n\tupdate: Partial<Unwrap<T>>,\n\toverwrite = false\n) {\n\tif (obj instanceof Signal) {\n\t\tobj.value = peekValue(update);\n\t} else {\n\t\tfor (let i in update) {\n\t\t\tif (i in obj) {\n\t\t\t\tobj[i].value = peekValue(update[i]);\n\t\t\t} else {\n\t\t\t\tlet sig = signal(peekValue(update[i]));\n\t\t\t\tsig[KEY] = i;\n\t\t\t\tobj[i] = sig;\n\t\t\t}\n\t\t}\n\t\tif (overwrite) {\n\t\t\tfor (let i in obj) {\n\t\t\t\tif (!(i in update)) {\n\t\t\t\t\tobj[i].value = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n*/\n"],"names":["currentComponent","currentUpdater","finishUpdate","preact","require","hooks","signalsCore","hasPendingUpdate","WeakSet","hasComputeds","hook","hookName","hookFn","options","bind","updaterForComponent","WeakMap","setCurrentUpdater","updater","_setCurrent","s","signal","undefined","_canActivate","_updater","getElementUpdater","vnode","get","_props","length","signalProps","createUpdater","dom","__e","i","prop","_key","value","_signal","_value","setAttribute","removeAttribute","set","childToSignal","child","arr","Array","isArray","forEach","Signal","createElement","Text","data","_ref","_this","this","useMemo","__v","v","__","__c","add","base","computed","displayName","old","type","props","push","oldUpdater","newUpdater","peek","component","setState","error","oldVNode","thing","signals","_deps","_subs","clear","index","hasHookState","Component","prototype","shouldComponentUpdate","state","_updater$_deps","size","has","_i","_i2","exports","batch","effect","useComputed","compute","$compute","useRef","current","useSignal"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"signals.js","sources":["../src/index.ts"],"sourcesContent":["import { options, Component, createElement } from \"preact\";\nimport { useRef, useMemo } from \"preact/hooks\";\nimport {\n\tsignal,\n\tcomputed,\n\tbatch,\n\teffect,\n\tSignal,\n\ttype ReadonlySignal,\n} from \"@preact/signals-core\";\nimport {\n\tVNode,\n\tComponentType,\n\tOptionsTypes,\n\tHookFn,\n\tUpdater,\n\tElementUpdater,\n} from \"./internal\";\n\nexport { signal, computed, batch, effect, Signal, type ReadonlySignal };\n\n// Components that have a pending Signal update: (used to bypass default sCU:false)\nconst hasPendingUpdate = new WeakSet<Component>();\n\n// Components that have useState()/useReducer() hooks:\nconst hasHookState = new WeakSet<Component>();\n\n// Components that have useComputed():\nconst hasComputeds = new WeakSet<Component>();\n\n// Install a Preact options hook\nfunction hook<T extends OptionsTypes>(hookName: T, hookFn: HookFn<T>) {\n\t// @ts-ignore-next-line private options hooks usage\n\toptions[hookName] = hookFn.bind(null, options[hookName] || (() => {}));\n}\n\nlet currentComponent: Component | undefined;\nlet currentUpdater: Updater | undefined;\nlet finishUpdate: ReturnType<Updater[\"_setCurrent\"]> | undefined;\nconst updaterForComponent = new WeakMap<Component | VNode, Updater>();\n\nfunction setCurrentUpdater(updater?: Updater) {\n\t// end tracking for the current update:\n\tif (finishUpdate) finishUpdate(true, true);\n\t// start tracking the new update:\n\tcurrentUpdater = updater;\n\tfinishUpdate = updater && updater._setCurrent();\n}\n\nfunction createUpdater(updater: () => void) {\n\tconst s = signal(undefined) as Updater;\n\ts._canActivate = true;\n\ts._updater = updater;\n\treturn s;\n}\n\n// Get a (cached) Signal property updater for an element VNode\nfunction getElementUpdater(vnode: VNode) {\n\tlet updater = updaterForComponent.get(vnode) as ElementUpdater;\n\tif (!updater) {\n\t\tlet signalProps: Array<{ _key: string; _signal: Signal }> = [];\n\t\tupdater = createUpdater(() => {\n\t\t\tlet dom = vnode.__e as Element;\n\n\t\t\tfor (let i = 0; i < signalProps.length; i++) {\n\t\t\t\tlet { _key: prop, _signal: signal } = signalProps[i];\n\t\t\t\tlet value = signal._value;\n\t\t\t\tif (!dom) return;\n\t\t\t\tif (prop in dom) {\n\t\t\t\t\t// @ts-ignore-next-line silly\n\t\t\t\t\tdom[prop] = value;\n\t\t\t\t} else if (value) {\n\t\t\t\t\tdom.setAttribute(prop, value);\n\t\t\t\t} else {\n\t\t\t\t\tdom.removeAttribute(prop);\n\t\t\t\t}\n\t\t\t}\n\t\t}) as ElementUpdater;\n\t\tupdater._props = signalProps;\n\t\tupdaterForComponent.set(vnode, updater);\n\t} else {\n\t\tupdater._props.length = 0;\n\t}\n\treturn updater;\n}\n\n/** @todo This may be needed for complex prop value detection. */\n// function isSignalValue(value: any): value is Signal {\n// \tif (typeof value !== \"object\" || value == null) return false;\n// \tif (value instanceof Signal) return true;\n// \t// @TODO: uncomment this when we land Reactive (ideally behind a brand check)\n// \t// for (let i in value) if (value[i] instanceof Signal) return true;\n// \treturn false;\n// }\n\n/** Convert Signals within (nested) props.children into Text components */\nfunction childToSignal<T>(child: any, i: keyof T, arr: T) {\n\tif (typeof child !== \"object\" || child == null) {\n\t\t// can't be a signal\n\t} else if (Array.isArray(child)) {\n\t\tchild.forEach(childToSignal);\n\t} else if (child instanceof Signal) {\n\t\t// @ts-ignore-next-line yes, arr can accept VNodes:\n\t\tarr[i] = createElement(Text, { data: child });\n\t}\n}\n\n/**\n * A wrapper component that renders a Signal directly as a Text node.\n * @todo: in Preact 11, just decorate Signal with `type:null`\n */\nfunction Text(this: ComponentType, { data }: { data: Signal }) {\n\t// hasComputeds.add(this);\n\n\t// Store the props.data signal in another signal so that\n\t// passing a new signal reference re-runs the text computed:\n\tconst currentSignal = useSignal(data);\n\tcurrentSignal.value = data;\n\n\tconst s = useMemo(() => {\n\t\t// mark the parent component as having computeds so it gets optimized\n\t\tlet v = this.__v;\n\t\twhile ((v = v.__!)) {\n\t\t\tif (v.__c) {\n\t\t\t\thasComputeds.add(v.__c);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Replace this component's vdom updater with a direct text one:\n\t\tcurrentUpdater!._updater = () => {\n\t\t\t(this.base as Text).data = s._value;\n\t\t};\n\n\t\treturn computed(() => {\n\t\t\tlet data = currentSignal.value;\n\t\t\tlet s = data.value;\n\t\t\treturn s === 0 ? 0 : s === true ? \"\" : s || \"\";\n\t\t});\n\t}, []);\n\n\treturn s.value;\n}\nText.displayName = \"_st\";\n\n/** Inject low-level property/attribute bindings for Signals into Preact's diff */\nhook(OptionsTypes.DIFF, (old, vnode) => {\n\tif (typeof vnode.type === \"string\") {\n\t\t// let orig = vnode.__o || vnode;\n\t\tlet props = vnode.props;\n\t\tlet updater;\n\n\t\tfor (let i in props) {\n\t\t\tlet value = props[i];\n\t\t\tif (i === \"children\") {\n\t\t\t\tchildToSignal(value, \"children\", props);\n\t\t\t} else if (value instanceof Signal) {\n\t\t\t\t// first Signal prop triggers creation/cleanup of the updater:\n\t\t\t\tif (!updater) updater = getElementUpdater(vnode);\n\t\t\t\t// track which props are Signals for precise updates:\n\t\t\t\tupdater._props.push({ _key: i, _signal: value });\n\t\t\t\tlet newUpdater = updater._updater;\n\t\t\t\tif (value._updater) {\n\t\t\t\t\tlet oldUpdater = value._updater;\n\t\t\t\t\tvalue._updater = () => {\n\t\t\t\t\t\tnewUpdater();\n\t\t\t\t\t\toldUpdater();\n\t\t\t\t\t};\n\t\t\t\t} else {\n\t\t\t\t\tvalue._updater = newUpdater;\n\t\t\t\t}\n\t\t\t\tprops[i] = value.peek();\n\t\t\t}\n\t\t}\n\n\t\tsetCurrentUpdater(updater);\n\t}\n\n\told(vnode);\n});\n\n/** Set up Updater before rendering a component */\nhook(OptionsTypes.RENDER, (old, vnode) => {\n\tlet updater;\n\n\tlet component = vnode.__c;\n\tif (component) {\n\t\thasPendingUpdate.delete(component);\n\n\t\tupdater = updaterForComponent.get(component);\n\t\tif (updater === undefined) {\n\t\t\tupdater = createUpdater(() => {\n\t\t\t\thasPendingUpdate.add(component);\n\t\t\t\tcomponent.setState({});\n\t\t\t});\n\t\t\tupdaterForComponent.set(component, updater);\n\t\t}\n\t}\n\n\tcurrentComponent = component;\n\tsetCurrentUpdater(updater);\n\told(vnode);\n});\n\n/** Finish current updater if a component errors */\nhook(OptionsTypes.CATCH_ERROR, (old, error, vnode, oldVNode) => {\n\tsetCurrentUpdater();\n\tcurrentComponent = undefined;\n\told(error, vnode, oldVNode);\n});\n\n/** Finish current updater after rendering any VNode */\nhook(OptionsTypes.DIFFED, (old, vnode) => {\n\tsetCurrentUpdater();\n\tcurrentComponent = undefined;\n\told(vnode);\n});\n\n/** Unsubscribe from Signals when unmounting components/vnodes */\nhook(OptionsTypes.UNMOUNT, (old, vnode: VNode) => {\n\tlet thing = vnode.__c || vnode;\n\tconst updater = updaterForComponent.get(thing);\n\tif (updater) {\n\t\tupdaterForComponent.delete(thing);\n\t\tconst signals = updater._deps;\n\t\tif (signals) {\n\t\t\tsignals.forEach(signal => signal._subs.delete(updater));\n\t\t\tsignals.clear();\n\t\t}\n\t}\n\told(vnode);\n});\n\n/** Mark components that use hook state so we can skip sCU optimization. */\nhook(OptionsTypes.HOOK, (old, component, index, type) => {\n\tif (type < 3) hasHookState.add(component);\n\told(component, index, type);\n});\n\n/**\n * Auto-memoize components that use Signals/Computeds.\n * Note: Does _not_ optimize components that use hook/class state.\n */\nComponent.prototype.shouldComponentUpdate = function (props, state) {\n\t// @todo: Once preactjs/preact#3671 lands, this could just use `currentUpdater`:\n\tconst updater = updaterForComponent.get(this);\n\n\tconst hasSignals = updater && updater._deps?.size !== 0;\n\n\t// let reason;\n\t// if (!hasSignals && !hasComputeds.has(this)) {\n\t// \treason = \"no signals or computeds\";\n\t// } else if (hasPendingUpdate.has(this)) {\n\t// \treason = \"has pending update\";\n\t// } else if (hasHookState.has(this)) {\n\t// \treason = \"has hook state\";\n\t// }\n\t// if (reason) {\n\t// \tif (!this) reason += \" (`this` bug)\";\n\t// \tconsole.log(\"not optimizing\", this?.constructor?.name, \": \", reason, {\n\t// \t\tdetails: {\n\t// \t\t\thasSignals,\n\t// \t\t\thasComputeds: hasComputeds.has(this),\n\t// \t\t\thasPendingUpdate: hasPendingUpdate.has(this),\n\t// \t\t\thasHookState: hasHookState.has(this),\n\t// \t\t\tdeps: Array.from(updater._deps),\n\t// \t\t\tupdater,\n\t// \t\t},\n\t// \t});\n\t// }\n\n\t// if this component used no signals or computeds, update:\n\tif (!hasSignals && !hasComputeds.has(this)) return true;\n\n\t// if there is a pending re-render triggered from Signals, update:\n\tif (hasPendingUpdate.has(this)) return true;\n\n\t// if there is hook or class state, update:\n\tif (hasHookState.has(this)) return true;\n\tfor (let i in state) return true;\n\n\t// if any non-Signal props changed, update:\n\tfor (let i in props) {\n\t\tif (i !== \"__source\" && props[i] !== this.props[i]) return true;\n\t}\n\tfor (let i in this.props) if (!(i in props)) return true;\n\n\t// this is a purely Signal-driven component, don't update:\n\treturn false;\n};\n\nexport function useSignal<T>(value: T) {\n\treturn useMemo(() => signal<T>(value), []);\n}\n\nexport function useComputed<T>(compute: () => T) {\n\tconst $compute = useRef(compute);\n\t$compute.current = compute;\n\thasComputeds.add(currentComponent!);\n\treturn useMemo(() => computed<T>(() => $compute.current()), []);\n}\n\n/**\n * @todo Determine which Reactive implementation we'll be using.\n * @internal\n */\n// export function useReactive<T extends object>(value: T): Reactive<T> {\n// \treturn useMemo(() => reactive<T>(value), []);\n// }\n\n/**\n * @internal\n * Update a Reactive's using the properties of an object or other Reactive.\n * Also works for Signals.\n * @example\n * // Update a Reactive with Object.assign()-like syntax:\n * const r = reactive({ name: \"Alice\" });\n * update(r, { name: \"Bob\" });\n * update(r, { age: 42 }); // property 'age' does not exist in type '{ name?: string }'\n * update(r, 2); // '2' has no properties in common with '{ name?: string }'\n * console.log(r.name.value); // \"Bob\"\n *\n * @example\n * // Update a Reactive with the properties of another Reactive:\n * const A = reactive({ name: \"Alice\" });\n * const B = reactive({ name: \"Bob\", age: 42 });\n * update(A, B);\n * console.log(`${A.name} is ${A.age}`); // \"Bob is 42\"\n *\n * @example\n * // Update a signal with assign()-like syntax:\n * const s = signal(42);\n * update(s, \"hi\"); // Argument type 'string' not assignable to type 'number'\n * update(s, {}); // Argument type '{}' not assignable to type 'number'\n * update(s, 43);\n * console.log(s.value); // 43\n *\n * @param obj The Reactive or Signal to be updated\n * @param update The value, Signal, object or Reactive to update `obj` to match\n * @param overwrite If `true`, any properties `obj` missing from `update` are set to `undefined`\n */\n/*\nexport function update<T extends SignalOrReactive>(\n\tobj: T,\n\tupdate: Partial<Unwrap<T>>,\n\toverwrite = false\n) {\n\tif (obj instanceof Signal) {\n\t\tobj.value = peekValue(update);\n\t} else {\n\t\tfor (let i in update) {\n\t\t\tif (i in obj) {\n\t\t\t\tobj[i].value = peekValue(update[i]);\n\t\t\t} else {\n\t\t\t\tlet sig = signal(peekValue(update[i]));\n\t\t\t\tsig[KEY] = i;\n\t\t\t\tobj[i] = sig;\n\t\t\t}\n\t\t}\n\t\tif (overwrite) {\n\t\t\tfor (let i in obj) {\n\t\t\t\tif (!(i in update)) {\n\t\t\t\t\tobj[i].value = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n*/\n"],"names":["currentComponent","finishUpdate","hasPendingUpdate","WeakSet","hasComputeds","hook","hookName","hookFn","options","bind","updaterForComponent","setCurrentUpdater","updater","currentUpdater","_setCurrent","createUpdater","s","signal","undefined","_canActivate","_updater","getElementUpdater","vnode","get","_props","length","signalProps","dom","__e","i","prop","_key","_signal","_value","value","setAttribute","removeAttribute","set","childToSignal","child","arr","Array","isArray","forEach","Signal","createElement","Text","data","_ref","_this","this","currentSignal","useSignal","useMemo","v","__v","__","__c","add","base","computed","displayName","old","type","props","push","newUpdater","oldUpdater","peek","component","setState","error","oldVNode","thing","signals","_deps","_subs","clear","index","hasHookState","Component","prototype","shouldComponentUpdate","state","_updater$_deps","size","has","_i","_i2","useComputed","compute","$compute","useRef","current"],"mappings":"IAoCIA,IAEJC,kFAhBsBC,EAAG,IAAIC,UAGR,IAArBA,QAGMC,EAAe,YAGrB,SAAAC,EAAsCC,EAAaC,GAElDC,EAAAA,QAAQF,GAAYC,EAAOE,KAAK,KAAMD,EAAAA,QAAQF,IAAc,WAAO,EACnE,CAKD,IAAMI,EAAsB,YAE5B,SAAAC,EAA2BC,GAEtBX,GAAcA,GAAa,GAAM,GAErCY,EAAiBD,EACjBX,EAAeW,GAAWA,EAAQE,GAClC,CAED,SAASC,EAAcH,GACtB,IAAOI,EAAGC,EAAMA,YAACC,GAGjB,OAFAF,EAAEG,IAAe,EACjBH,EAAEI,GAAWR,EACNI,CACP,CAGD,SAAAK,EAA2BC,GAC1B,IAAWV,EAAGF,EAAoBa,IAAID,GACtC,GAAKV,EAsBJA,EAAQY,GAAOC,OAAS,MAtBX,CACb,IAAeC,EAA6C,IAC5Dd,EAAUG,EAAc,WAGvB,IAFA,IAAOY,EAAGL,EAAMM,IAEPC,EAAI,EAAGA,EAAIH,EAAYD,OAAQI,IAAK,CAC5C,IAAsCH,EAAAA,EAAYG,GAAtCC,EAANC,EAAAA,IAAYC,EAAAA,EACCC,GACnB,IAAKN,EAAK,OACNG,KAAJH,EAECA,EAAIG,GAAQI,EACFA,EACVP,EAAIQ,aAAaL,EAAMI,GAEvBP,EAAIS,gBAAgBN,EAErB,CACD,IACON,GAASE,EACjBhB,EAAoB2B,IAAIf,EAAOV,EAC/B,CAGD,OAAOA,CACP,CAYD,SAAS0B,EAAiBC,EAAYV,EAAYW,GAC5B,iBAAjBD,GAAsC,MAATA,IAEtBE,MAAMC,QAAQH,GACxBA,EAAMI,QAAQL,GACJC,aAAJK,EAAAA,SAENJ,EAAIX,GAAKgB,EAAAA,cAAcC,EAAM,CAAEC,KAAMR,KAEtC,CAMD,SAASO,EAAoDE,GAAA,IAAAC,EAAAC,KAAAH,EAAAC,EAAxBD,KAKjBI,EAAGC,EAAUL,GAChCI,EAAcjB,MAAQa,EAEtB,MAAUM,EAAOA,QAAC,WAGjB,IADA,IAAKC,EAAGL,EAAKM,IACLD,EAAIA,EAAEE,IACb,GAAIF,EAAEG,IAAK,CACVrD,EAAasD,IAAIJ,EAAEG,KACnB,KACA,CAQF,OAJA5C,EAAgBO,GAAW,WACzB6B,EAAKU,KAAcZ,KAAO/B,EAAEiB,EAC7B,EAEM2B,EAAAA,SAAS,WACf,IACI5C,EADOmC,EAAcjB,MACZA,MACb,OAAa,IAANlB,EAAU,GAAU,IAANA,EAAa,GAAKA,GAAK,EAC5C,EACD,EAAE,IAEH,OAAQA,EAACkB,KACT,CAqJekB,SAAAA,EAAalB,GAC5B,OAAOmB,EAAOA,QAAC,WAAMpC,OAAAA,EAAAA,OAAUiB,EAAhB,EAAwB,GACvC,CAtJDY,EAAKe,YAAc,MAGnBxD,QAAwB,SAACyD,EAAKxC,GAC7B,GAA0B,iBAAVA,EAACyC,KAAmB,CAEnC,IACAnD,EADIoD,EAAQ1C,EAAM0C,MAGlB,IAAK,SAASA,EAAO,CACpB,IAAI9B,EAAQ8B,EAAMnC,GACR,aAANA,EACHS,EAAcJ,EAAO,WAAY8B,GACvB9B,aAAiBU,EAAAA,QAAQ,WAE9BhC,IAASA,EAAUS,EAAkBC,IAE1CV,EAAQY,GAAOyC,KAAK,CAAElC,EAAMF,EAAGG,EAASE,IACxC,IAAcgC,EAAGtD,EAAQQ,GACzB,GAAIc,EAAMd,GAAU,CACnB,IAAc+C,EAAGjC,EAAMd,GACvBc,EAAMd,GAAW,WAChB8C,IACAC,GACA,CACD,MACAjC,EAAMd,GAAW8C,EAElBF,EAAMnC,GAAKK,EAAMkC,MACjB,CAhBmC,EAiBpC,CAEDzD,EAAkBC,EAClB,CAEDkD,EAAIxC,EACJ,GAGDjB,QAA0B,SAACyD,EAAKxC,GAC/B,IAAAV,EAEayD,EAAG/C,EAAMmC,IAClBY,IACHnE,EAAA,OAAwBmE,QAGRnD,KADhBN,EAAUF,EAAoBa,IAAI8C,MAEjCzD,EAAUG,EAAc,WACvBb,EAAiBwD,IAAIW,GACrBA,EAAUC,SAAS,CAAA,EACnB,GACD5D,EAAoB2B,IAAIgC,EAAWzD,KAIrCZ,EAAmBqE,EACnB1D,EAAkBC,GAClBkD,EAAIxC,EACJ,GAGDjB,EAAI,MAA2B,SAACyD,EAAKS,EAAOjD,EAAOkD,GAClD7D,IACAX,OAAmBkB,EACnB4C,EAAIS,EAAOjD,EAAOkD,EAClB,GAGDnE,WAA0B,SAACyD,EAAKxC,GAC/BX,IACAX,OAAmBkB,EACnB4C,EAAIxC,EACJ,GAGDjB,YAA2B,SAACyD,EAAKxC,GAChC,IAAImD,EAAQnD,EAAMmC,KAAOnC,EACnBV,EAAUF,EAAoBa,IAAIkD,GACxC,GAAI7D,EAAS,CACZF,EAAA,OAA2B+D,GAC3B,IAAMC,EAAU9D,EAAQ+D,GACpBD,IACHA,EAAQ/B,QAAQ,SAAA1B,GAAUA,OAAAA,EAAO2D,UAAahE,EAAxB,GACtB8D,EAAQG,QAET,CACDf,EAAIxC,EACJ,GAGDjB,EAAI,MAAoB,SAACyD,EAAKO,EAAWS,EAAOf,GAC3CA,EAAO,GAAGgB,EAAarB,IAAIW,GAC/BP,EAAIO,EAAWS,EAAOf,EACtB,GAMDiB,YAAUC,UAAUC,sBAAwB,SAAUlB,EAAOmB,GAAK,IAAAC,EAE3DxE,EAAUF,EAAoBa,IAAI2B,MA2BxC,KAzBmBtC,GAAmC,KAATyE,OAAfzE,EAAAA,EAAQ+D,SAAOU,EAAAA,EAAAA,OAyBzBjF,EAAakF,IAAIpC,OAAO,OAAA,EAG5C,GAAIhD,EAAiBoF,IAAIpC,MAAO,OAAA,EAGhC,GAAI6B,EAAaO,IAAIpC,MAAO,OAAA,EAC5B,IAAK,IAALrB,OAAqB,OAArB,EAGA,IAAK,IAAL0D,OACC,GAAU,aAAN1D,GAAoBmC,EAAMnC,KAAOqB,KAAKc,MAAMnC,GAAI,OACpD,EACD,IAAK,IAAL2D,UAAmBxB,MAAO,KAAMnC,KAAFmC,GAAe,OAAO,EAGpD,OACA,CAAA,gdAMeyB,SAAeC,GAC9B,IAAcC,EAAGC,EAAAA,OAAOF,GAGxB,OAFAC,EAASE,QAAUH,EACnBtF,EAAasD,IAAI1D,GACVqD,UAAQ,WAAA,OAAcO,EAAAA,SAAI,kBAAc+B,EAACE,SAAf,EAAlB,EAA6C,GAC5D"}
|
package/dist/signals.min.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
!function(n,
|
|
1
|
+
!function(n,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("preact"),require("preact/hooks"),require("@preact/signals-core")):"function"==typeof define&&define.amd?define(["exports","preact","preact/hooks","@preact/signals-core"],e):e((n||self).preactSignals={},n.preact,n.hooks,n.signalsCore)}(this,function(n,e,r,t){var i,u,o,f=new WeakSet,c=new WeakSet,a=new WeakSet;function s(n,r){e.options[n]=r.bind(null,e.options[n]||function(){})}var v=new WeakMap;function l(n){o&&o(!0,!0),u=n,o=n&&n._()}function d(n){var e=t.signal(void 0);return e._c=!0,e._u=n,e}function b(n){var e=v.get(n);if(e)e.__.length=0;else{var r=[];(e=d(function(){for(var e=n.__e,t=0;t<r.length;t++){var i=r[t],u=i.t,o=i.i._v;if(!e)return;u in e?e[u]=o:o?e.setAttribute(u,o):e.removeAttribute(u)}})).__=r,v.set(n,e)}return e}function p(n,r,i){"object"!=typeof n||null==n||(Array.isArray(n)?n.forEach(p):n instanceof t.Signal&&(i[r]=e.createElement(h,{data:n})))}function h(n){var e=this,i=n.data,o=g(i);o.value=i;var f=r.useMemo(function(){for(var n=e.__v;n=n.__;)if(n.__c){a.add(n.__c);break}return u._u=function(){e.base.data=f._v},t.computed(function(){var n=o.value.value;return 0===n?0:!0===n?"":n||""})},[]);return f.value}function g(n){return r.useMemo(function(){return t.signal(n)},[])}h.displayName="_st",s("__b",function(n,e){if("string"==typeof e.type){var r,i=e.props;for(var u in i){var o=i[u];"children"===u?p(o,"children",i):o instanceof t.Signal&&function(){r||(r=b(e)),r.__.push({t:u,i:o});var n=r._u;if(o._u){var t=o._u;o._u=function(){n(),t()}}else o._u=n;i[u]=o.peek()}()}l(r)}n(e)}),s("__r",function(n,e){var r,t=e.__c;t&&(f.delete(t),void 0===(r=v.get(t))&&(r=d(function(){f.add(t),t.setState({})}),v.set(t,r))),i=t,l(r),n(e)}),s("__e",function(n,e,r,t){l(),i=void 0,n(e,r,t)}),s("diffed",function(n,e){l(),i=void 0,n(e)}),s("unmount",function(n,e){var r=e.__c||e,t=v.get(r);if(t){v.delete(r);var i=t._d;i&&(i.forEach(function(n){return n._s.delete(t)}),i.clear())}n(e)}),s("__h",function(n,e,r,t){t<3&&c.add(e),n(e,r,t)}),e.Component.prototype.shouldComponentUpdate=function(n,e){var r,t=v.get(this);if(!(t&&0!==(null==(r=t._d)?void 0:r.size)||a.has(this)))return!0;if(f.has(this))return!0;if(c.has(this))return!0;for(var i in e)return!0;for(var u in n)if("__source"!==u&&n[u]!==this.props[u])return!0;for(var o in this.props)if(!(o in n))return!0;return!1},Object.defineProperty(n,"Signal",{enumerable:!0,get:function(){return t.Signal}}),Object.defineProperty(n,"batch",{enumerable:!0,get:function(){return t.batch}}),Object.defineProperty(n,"computed",{enumerable:!0,get:function(){return t.computed}}),Object.defineProperty(n,"effect",{enumerable:!0,get:function(){return t.effect}}),Object.defineProperty(n,"signal",{enumerable:!0,get:function(){return t.signal}}),n.useComputed=function(n){var e=r.useRef(n);return e.current=n,a.add(i),r.useMemo(function(){return t.computed(function(){return e.current()})},[])},n.useSignal=g});
|
|
2
|
+
//# sourceMappingURL=signals.min.js.map
|
package/dist/signals.min.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signals.min.js","sources":["../src/index.ts"],"sourcesContent":["import { options, Component, createElement } from \"preact\";\nimport { useRef, useMemo } from \"preact/hooks\";\nimport {\n\tsignal,\n\tcomputed,\n\tbatch,\n\teffect,\n\tSignal,\n\ttype ReadonlySignal,\n} from \"@preact/signals-core\";\nimport {\n\tVNode,\n\tComponentType,\n\tOptionsTypes,\n\tHookFn,\n\tUpdater,\n\tElementUpdater,\n} from \"./internal\";\n\nexport { signal, computed, batch, effect, Signal, type ReadonlySignal };\n\n// Components that have a pending Signal update: (used to bypass default sCU:false)\nconst hasPendingUpdate = new WeakSet<Component>();\n\n// Components that have useState()/useReducer() hooks:\nconst hasHookState = new WeakSet<Component>();\n\n// Components that have useComputed():\nconst hasComputeds = new WeakSet<Component>();\n\n// Install a Preact options hook\nfunction hook<T extends OptionsTypes>(hookName: T, hookFn: HookFn<T>) {\n\t// @ts-ignore-next-line private options hooks usage\n\toptions[hookName] = hookFn.bind(null, options[hookName] || (() => {}));\n}\n\nlet currentComponent: Component | undefined;\nlet currentUpdater: Updater | undefined;\nlet finishUpdate: ReturnType<Updater[\"_setCurrent\"]> | undefined;\nconst updaterForComponent = new WeakMap<Component | VNode, Updater>();\n\nfunction setCurrentUpdater(updater?: Updater) {\n\t// end tracking for the current update:\n\tif (finishUpdate) finishUpdate(true, true);\n\t// start tracking the new update:\n\tcurrentUpdater = updater;\n\tfinishUpdate = updater && updater._setCurrent();\n}\n\nfunction createUpdater(updater: () => void) {\n\tconst s = signal(undefined) as Updater;\n\ts._canActivate = true;\n\ts._updater = updater;\n\treturn s;\n}\n\n// Get a (cached) Signal property updater for an element VNode\nfunction getElementUpdater(vnode: VNode) {\n\tlet updater = updaterForComponent.get(vnode) as ElementUpdater;\n\tif (!updater) {\n\t\tlet signalProps: Array<{ _key: string, _signal: Signal }> = [];\n\t\tupdater = createUpdater(() => {\n\t\t\tlet dom = vnode.__e as Element;\n\n\t\t\tfor (let i = 0; i < signalProps.length; i++) {\n\t\t\t\tlet { _key: prop, _signal: signal } = signalProps[i];\n\t\t\t\tlet value = signal._value;\n\t\t\t\tif (!dom) return;\n\t\t\t\tif (prop in dom) {\n\t\t\t\t\t// @ts-ignore-next-line silly\n\t\t\t\t\tdom[prop] = value;\n\t\t\t\t} else if (value) {\n\t\t\t\t\tdom.setAttribute(prop, value);\n\t\t\t\t} else {\n\t\t\t\t\tdom.removeAttribute(prop);\n\t\t\t\t}\n\t\t\t}\n\t\t}) as ElementUpdater;\n\t\tupdater._props = signalProps;\n\t\tupdaterForComponent.set(vnode, updater);\n\t} else {\n\t\tupdater._props.length = 0;\n\t}\n\treturn updater;\n}\n\n/** @todo This may be needed for complex prop value detection. */\n// function isSignalValue(value: any): value is Signal {\n// \tif (typeof value !== \"object\" || value == null) return false;\n// \tif (value instanceof Signal) return true;\n// \t// @TODO: uncomment this when we land Reactive (ideally behind a brand check)\n// \t// for (let i in value) if (value[i] instanceof Signal) return true;\n// \treturn false;\n// }\n\n/** Convert Signals within (nested) props.children into Text components */\nfunction childToSignal<T>(child: any, i: keyof T, arr: T) {\n\tif (typeof child !== \"object\" || child == null) {\n\t\t// can't be a signal\n\t} else if (Array.isArray(child)) {\n\t\tchild.forEach(childToSignal);\n\t} else if (child instanceof Signal) {\n\t\t// @ts-ignore-next-line yes, arr can accept VNodes:\n\t\tarr[i] = createElement(Text, { data: child });\n\t}\n}\n\n/**\n * A wrapper component that renders a Signal directly as a Text node.\n * @todo: in Preact 11, just decorate Signal with `type:null`\n */\nfunction Text(this: ComponentType, { data }: { data: Signal }) {\n\t// hasComputeds.add(this);\n\n\tconst s = useMemo(() => {\n\t\t// mark the parent component as having computeds so it gets optimized\n\t\tlet v = this.__v;\n\t\twhile ((v = v.__!)) {\n\t\t\tif (v.__c) {\n\t\t\t\thasComputeds.add(v.__c);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Replace this component's vdom updater with a direct text one:\n\t\tcurrentUpdater!._updater = () => {\n\t\t\t(this.base as Text).data = s._value;\n\t\t};\n\n\t\treturn computed(() => {\n\t\t\tlet s = data.value;\n\t\t\treturn s === 0 ? 0 : s === true ? \"\" : s || \"\";\n\t\t});\n\t}, []);\n\n\treturn s.value;\n}\nText.displayName = \"_st\";\n\n/** Inject low-level property/attribute bindings for Signals into Preact's diff */\nhook(OptionsTypes.DIFF, (old, vnode) => {\n\tif (typeof vnode.type === \"string\") {\n\t\t// let orig = vnode.__o || vnode;\n\t\tlet props = vnode.props;\n\t\tlet updater;\n\n\t\tfor (let i in props) {\n\t\t\tlet value = props[i];\n\t\t\tif (i === \"children\") {\n\t\t\t\tchildToSignal(value, \"children\", props);\n\t\t\t} else if (value instanceof Signal) {\n\t\t\t\t// first Signal prop triggers creation/cleanup of the updater:\n\t\t\t\tif (!updater) updater = getElementUpdater(vnode);\n\t\t\t\t// track which props are Signals for precise updates:\n\t\t\t\tupdater._props.push({ _key: i, _signal: value });\n\t\t\t\tlet newUpdater = updater._updater\n\t\t\t\tif (value._updater) {\n\t\t\t\t\tlet oldUpdater = value._updater\n\t\t\t\t\tvalue._updater = () => {\n\t\t\t\t\t\tnewUpdater();\n\t\t\t\t\t\toldUpdater();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tvalue._updater = newUpdater\n\t\t\t\t}\n\t\t\t\tprops[i] = value.peek()\n\t\t\t}\n\t\t}\n\n\t\tsetCurrentUpdater(updater);\n\t}\n\n\told(vnode);\n});\n\n/** Set up Updater before rendering a component */\nhook(OptionsTypes.RENDER, (old, vnode) => {\n\tlet updater;\n\n\tlet component = vnode.__c;\n\tif (component) {\n\t\thasPendingUpdate.delete(component);\n\n\t\tupdater = updaterForComponent.get(component);\n\t\tif (updater === undefined) {\n\t\t\tupdater = createUpdater(() => {\n\t\t\t\thasPendingUpdate.add(component);\n\t\t\t\tcomponent.setState({});\n\t\t\t});\n\t\t\tupdaterForComponent.set(component, updater);\n\t\t}\n\t}\n\n\tcurrentComponent = component;\n\tsetCurrentUpdater(updater);\n\told(vnode);\n});\n\n/** Finish current updater if a component errors */\nhook(OptionsTypes.CATCH_ERROR, (old, error, vnode, oldVNode) => {\n\tsetCurrentUpdater();\n\tcurrentComponent = undefined;\n\told(error, vnode, oldVNode);\n});\n\n/** Finish current updater after rendering any VNode */\nhook(OptionsTypes.DIFFED, (old, vnode) => {\n\tsetCurrentUpdater();\n\tcurrentComponent = undefined;\n\told(vnode);\n});\n\n/** Unsubscribe from Signals when unmounting components/vnodes */\nhook(OptionsTypes.UNMOUNT, (old, vnode: VNode) => {\n\tlet thing = vnode.__c || vnode;\n\tconst updater = updaterForComponent.get(thing);\n\tif (updater) {\n\t\tupdaterForComponent.delete(thing);\n\t\tconst signals = updater._deps;\n\t\tif (signals) {\n\t\t\tsignals.forEach(signal => signal._subs.delete(updater));\n\t\t\tsignals.clear();\n\t\t}\n\t}\n\told(vnode);\n});\n\n/** Mark components that use hook state so we can skip sCU optimization. */\nhook(OptionsTypes.HOOK, (old, component, index, type) => {\n\tif (type < 3) hasHookState.add(component);\n\told(component, index, type);\n});\n\n/**\n * Auto-memoize components that use Signals/Computeds.\n * Note: Does _not_ optimize components that use hook/class state.\n */\nComponent.prototype.shouldComponentUpdate = function (props, state) {\n\t// @todo: Once preactjs/preact#3671 lands, this could just use `currentUpdater`:\n\tconst updater = updaterForComponent.get(this);\n\n\tconst hasSignals = updater && updater._deps?.size !== 0;\n\n\t// let reason;\n\t// if (!hasSignals && !hasComputeds.has(this)) {\n\t// \treason = \"no signals or computeds\";\n\t// } else if (hasPendingUpdate.has(this)) {\n\t// \treason = \"has pending update\";\n\t// } else if (hasHookState.has(this)) {\n\t// \treason = \"has hook state\";\n\t// }\n\t// if (reason) {\n\t// \tif (!this) reason += \" (`this` bug)\";\n\t// \tconsole.log(\"not optimizing\", this?.constructor?.name, \": \", reason, {\n\t// \t\tdetails: {\n\t// \t\t\thasSignals,\n\t// \t\t\thasComputeds: hasComputeds.has(this),\n\t// \t\t\thasPendingUpdate: hasPendingUpdate.has(this),\n\t// \t\t\thasHookState: hasHookState.has(this),\n\t// \t\t\tdeps: Array.from(updater._deps),\n\t// \t\t\tupdater,\n\t// \t\t},\n\t// \t});\n\t// }\n\n\t// if this component used no signals or computeds, update:\n\tif (!hasSignals && !hasComputeds.has(this)) return true;\n\n\t// if there is a pending re-render triggered from Signals, update:\n\tif (hasPendingUpdate.has(this)) return true;\n\n\t// if there is hook or class state, update:\n\tif (hasHookState.has(this)) return true;\n\tfor (let i in state) return true;\n\n\t// if any non-Signal props changed, update:\n\tfor (let i in props) {\n\t\tif (i !== \"__source\" && props[i] !== this.props[i]) return true;\n\t}\n\tfor (let i in this.props) if (!(i in props)) return true;\n\n\t// this is a purely Signal-driven component, don't update:\n\treturn false;\n};\n\nexport function useSignal<T>(value: T) {\n\treturn useMemo(() => signal<T>(value), []);\n}\n\nexport function useComputed<T>(compute: () => T) {\n\tconst $compute = useRef(compute);\n\t$compute.current = compute;\n\thasComputeds.add(currentComponent!);\n\treturn useMemo(() => computed<T>(() => $compute.current()), []);\n}\n\n/**\n * @todo Determine which Reactive implementation we'll be using.\n * @internal\n */\n// export function useReactive<T extends object>(value: T): Reactive<T> {\n// \treturn useMemo(() => reactive<T>(value), []);\n// }\n\n/**\n * @internal\n * Update a Reactive's using the properties of an object or other Reactive.\n * Also works for Signals.\n * @example\n * // Update a Reactive with Object.assign()-like syntax:\n * const r = reactive({ name: \"Alice\" });\n * update(r, { name: \"Bob\" });\n * update(r, { age: 42 }); // property 'age' does not exist in type '{ name?: string }'\n * update(r, 2); // '2' has no properties in common with '{ name?: string }'\n * console.log(r.name.value); // \"Bob\"\n *\n * @example\n * // Update a Reactive with the properties of another Reactive:\n * const A = reactive({ name: \"Alice\" });\n * const B = reactive({ name: \"Bob\", age: 42 });\n * update(A, B);\n * console.log(`${A.name} is ${A.age}`); // \"Bob is 42\"\n *\n * @example\n * // Update a signal with assign()-like syntax:\n * const s = signal(42);\n * update(s, \"hi\"); // Argument type 'string' not assignable to type 'number'\n * update(s, {}); // Argument type '{}' not assignable to type 'number'\n * update(s, 43);\n * console.log(s.value); // 43\n *\n * @param obj The Reactive or Signal to be updated\n * @param update The value, Signal, object or Reactive to update `obj` to match\n * @param overwrite If `true`, any properties `obj` missing from `update` are set to `undefined`\n */\n/*\nexport function update<T extends SignalOrReactive>(\n\tobj: T,\n\tupdate: Partial<Unwrap<T>>,\n\toverwrite = false\n) {\n\tif (obj instanceof Signal) {\n\t\tobj.value = peekValue(update);\n\t} else {\n\t\tfor (let i in update) {\n\t\t\tif (i in obj) {\n\t\t\t\tobj[i].value = peekValue(update[i]);\n\t\t\t} else {\n\t\t\t\tlet sig = signal(peekValue(update[i]));\n\t\t\t\tsig[KEY] = i;\n\t\t\t\tobj[i] = sig;\n\t\t\t}\n\t\t}\n\t\tif (overwrite) {\n\t\t\tfor (let i in obj) {\n\t\t\t\tif (!(i in update)) {\n\t\t\t\t\tobj[i].value = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n*/\n"],"names":["g","f","exports","module","require","define","amd","globalThis","self","preactSignals","preact","hooks","signalsCore","this","currentComponent","currentUpdater","finishUpdate","hasPendingUpdate","WeakSet","hasComputeds","hook","hookName","hookFn","options","bind","updaterForComponent","WeakMap","setCurrentUpdater","updater","_setCurrent","s","signal","undefined","_canActivate","_updater","getElementUpdater","vnode","get","_props","length","signalProps","createUpdater","dom","__e","i","prop","_key","value","_signal","_value","setAttribute","removeAttribute","set","childToSignal","child","arr","Array","isArray","forEach","Signal","createElement","Text","data","_ref","_this","useMemo","__v","v","__","__c","add","base","computed","displayName","old","type","props","push","oldUpdater","newUpdater","peek","component","setState","error","oldVNode","thing","signals","_deps","_subs","clear","index","hasHookState","Component","prototype","shouldComponentUpdate","state","_updater$_deps","size","has","_i","_i2","batch","effect","useComputed","compute","$compute","useRef","current","useSignal"],"mappings":"CAsBA,SAAAA,EAAAC,GAAA,iBAAAC,SAAA,oBAAAC,OAAAF,EAAAC,QAAAE,QAAA,UAAAA,QAAA,gBAAAA,QAAA,yBAAA,mBAAAC,QAAAA,OAAAC,IAAAD,OAAA,CAAA,UAAA,SAAA,eAAA,wBAAAJ,GAAAA,GAAAD,EAAA,oBAAAO,WAAAA,WAAAP,GAAAQ,MAAAC,cAAA,CAAA,EAAAT,EAAAU,OAAAV,EAAAW,MAAAX,EAAAY,YAAA,CAAA,CAAAC,KAAA,SAAAX,EAAAQ,EAAAC,EAAAC,GAAA,IAcAE,EACIC,EACJC,EAhBsBC,EAAG,IAAIC,UAGR,IAArBA,QAGMC,EAAe,IAArBD,QAGA,SAASE,EAA6BC,EAAaC,GAElDC,EAAOA,QAACF,GAAYC,EAAOE,KAAK,KAAMD,EAAOA,QAACF,IAAc,WAAxC,EACpB,CAKD,IAAMI,EAAsB,IAAIC,QAEhC,SAAAC,EAA2BC,GAEtBZ,GAAcA,GAAa,GAAM,GAErCD,EAAiBa,EACjBZ,EAAeY,GAAWA,EAAQC,GAClC,CAED,WAAuBD,GACtB,IAAME,EAAIC,cAAOC,GAGjB,OAFAF,EAAEG,IAAe,EACjBH,EAAEI,GAAWN,EAEbE,CAAA,CAGD,SAASK,EAAkBC,GAC1B,IAAWR,EAAGH,EAAoBY,IAAID,GACtC,GAAKR,EAsBJA,EAAQU,GAAOC,OAAS,MAtBX,CACb,IAAIC,EAAwD,IAC5DZ,EAAUa,EAAc,WAGvB,IAFA,IAAIC,EAAMN,EAAMO,IAENC,EAAG,EAAGA,EAAIJ,EAAYD,OAAQK,IAAK,CAC5C,IAAsCJ,EAAAA,EAAYI,GAAtCC,EAANC,EAAAA,EACGC,EADSC,EAAAA,EACCC,GACnB,IAAKP,EAAK,OACNG,KAAQH,EAEXA,EAAIG,GAAQE,EACFA,EACVL,EAAIQ,aAAaL,EAAME,GAEvBL,EAAIS,gBAAgBN,EAErB,CACD,IACOP,GAASE,EACjBf,EAAoB2B,IAAIhB,EAAOR,EAC/B,CAGD,OAAOA,CACP,CAYD,SAAAyB,EAA0BC,EAAYV,EAAYW,GAC5B,iBAAjBD,GAAsC,MAATA,IAEtBE,MAAMC,QAAQH,GACxBA,EAAMI,QAAQL,GACJC,aAAJK,EAAAA,SAENJ,EAAIX,GAAKgB,gBAAcC,EAAM,CAAEC,KAAMR,KAEtC,CAMD,SAASO,EAAoDE,GAAA,IAAAC,EAAAnD,KAAAiD,EAAAC,EAAxBD,KAG9BhC,EAAImC,EAAOA,QAAC,WAGjB,IADA,MAAQD,EAAKE,IACLC,EAAIA,EAAEC,IACb,GAAID,EAAEE,IAAK,CACVlD,EAAamD,IAAIH,EAAEE,KACnB,KACA,CAQF,OAJAtD,EAAgBmB,GAAW,WACzB8B,EAAKO,KAAcT,KAAOhC,EAAEmB,EAC7B,EAEcuB,WAAC,WACf,IAAK1C,EAAGgC,EAAKf,MACb,OAAa,IAANjB,EAAU,GAAU,IAANA,EAAa,GAAKA,GAAK,EAC5C,EACD,EAAE,IAEH,OAAOA,EAAEiB,KACT,CACDc,EAAKY,YAAc,MAGnBrD,QAAwB,SAACsD,EAAKtC,GAC7B,GAA0B,iBAAfA,EAAMuC,KAAmB,CAEnC,IACI/C,EADAgD,EAAQxC,EAAMwC,MAGlB,IAAK,IAAIhC,KAAKgC,EAAO,CACpB,IAAI7B,EAAQ6B,EAAMhC,GACR,aAANA,EACHS,EAAcN,EAAO,WAAY6B,GACvB7B,aAAJY,EAAAA,QAEN,WAAK/B,IAASA,EAAUO,EAAkBC,IAE1CR,EAAQU,GAAOuC,KAAK,CAAE/B,EAAMF,EAAGI,EAASD,IACxC,MAAiBnB,EAAQM,GACzB,GAAIa,EAAMb,GAAU,CACnB,IAAc4C,EAAG/B,EAAMb,GACvBa,EAAMb,GAAW,WAChB6C,IACAD,GACA,CACD,MACA/B,EAAMb,GAAW6C,EAElBH,EAAMhC,GAAKG,EAAMiC,MAfkB,CAEnC,EAeD,CAEDrD,EAAkBC,EAClB,CAED8C,EAAItC,EACJ,GAGDhB,QAA0B,SAACsD,EAAKtC,GAC/B,IAAIR,EAESqD,EAAG7C,EAAMiC,IAClBY,IACHhE,EAAA,OAAwBgE,QAGRjD,KADhBJ,EAAUH,EAAoBY,IAAI4C,MAEjCrD,EAAUa,EAAc,WACvBxB,EAAiBqD,IAAIW,GACrBA,EAAUC,SAAS,CAAnB,EACA,GACDzD,EAAoB2B,IAAI6B,EAAWrD,KAIrCd,EAAmBmE,EACnBtD,EAAkBC,GAClB8C,EAAItC,EACJ,GAGDhB,EAAI,MAA2B,SAACsD,EAAKS,EAAO/C,EAAOgD,GAClDzD,IACAb,OAAmBkB,EACnB0C,EAAIS,EAAO/C,EAAOgD,EAClB,GAGDhE,WAA0B,SAACsD,EAAKtC,GAC/BT,IACAb,OAAmBkB,EACnB0C,EAAItC,EACJ,GAGDhB,YAA2B,SAACsD,EAAKtC,GAChC,IAASiD,EAAGjD,EAAMiC,KAAOjC,EACZR,EAAGH,EAAoBY,IAAIgD,GACxC,GAAIzD,EAAS,CACZH,EAAA,OAA2B4D,GAC3B,IAAMC,EAAU1D,EAAQ2D,GACpBD,IACHA,EAAQ5B,QAAQ,SAAA3B,GAAUA,OAAAA,EAAOyD,GAAa5D,OAAAA,EAAxB,GACtB0D,EAAQG,QAET,CACDf,EAAItC,EACJ,GAGDhB,EAAI,MAAoB,SAACsD,EAAKO,EAAWS,EAAOf,GAC3CA,EAAO,GAAGgB,EAAarB,IAAIW,GAC/BP,EAAIO,EAAWS,EAAOf,EACtB,GAMDiB,EAASA,UAACC,UAAUC,sBAAwB,SAAUlB,EAAOmB,GAAK,IAAAC,EAE3DpE,EAAUH,EAAoBY,IAAIxB,MA2BxC,KAzBmBe,GAAmC,KAATqE,OAAfrE,EAAAA,EAAQ2D,SAAOU,EAAAA,EAAAA,OAyBzB9E,EAAa+E,IAAIrF,OAAO,OAAO,EAGnD,GAAII,EAAiBiF,IAAIrF,MAAO,OAAO,EAGvC,GAAI8E,EAAaO,IAAIrF,MAAO,SAC5B,IAAK,IAAI+B,KAAKmD,EAAO,OAArB,EAGA,IAAK,IAALI,KAAAvB,EACC,GAAU,aAANhC,GAAoBgC,EAAMhC,KAAO/B,KAAK+D,MAAMhC,GAAI,OACpD,EACD,IAAK,IAALwD,KAAmBxB,KAAAA,MAAO,KAAMhC,KAAKgC,GAAQ,OAA7C,EAGA,OAAO,CACP,EAWA1E,EAAAyD,OAAA/C,EAAA+C,OAAAzD,EAAAmG,MAAAzF,EAAAyF,MAAAnG,EAAAsE,SAAA5D,EAAA4D,SAAAtE,EAAAoG,OAAA1F,EAAA0F,OAAApG,EAAA6B,OAAAnB,EAAAmB,OAAA7B,EAAAqG,YALeA,SAAeC,GAC9B,IAAcC,EAAGC,EAAMA,OAACF,GAGxB,OAFAC,EAASE,QAAUH,EACnBrF,EAAamD,IAAIxD,GACVmD,UAAQ,WAAA,OAAcO,EAAAA,SAAI,kBAAciC,EAACE,SAAf,EAAlB,EAA6C,GAC5D,EAAAzG,EAAA0G,UATK,SAAuB7D,GAC5B,OAAckB,EAAAA,QAAC,kBAAYlC,EAAAA,OAAIgB,EAAhB,EAAwB,GACvC,CAOA"}
|
|
1
|
+
{"version":3,"file":"signals.min.js","sources":["../src/index.ts"],"sourcesContent":["import { options, Component, createElement } from \"preact\";\nimport { useRef, useMemo } from \"preact/hooks\";\nimport {\n\tsignal,\n\tcomputed,\n\tbatch,\n\teffect,\n\tSignal,\n\ttype ReadonlySignal,\n} from \"@preact/signals-core\";\nimport {\n\tVNode,\n\tComponentType,\n\tOptionsTypes,\n\tHookFn,\n\tUpdater,\n\tElementUpdater,\n} from \"./internal\";\n\nexport { signal, computed, batch, effect, Signal, type ReadonlySignal };\n\n// Components that have a pending Signal update: (used to bypass default sCU:false)\nconst hasPendingUpdate = new WeakSet<Component>();\n\n// Components that have useState()/useReducer() hooks:\nconst hasHookState = new WeakSet<Component>();\n\n// Components that have useComputed():\nconst hasComputeds = new WeakSet<Component>();\n\n// Install a Preact options hook\nfunction hook<T extends OptionsTypes>(hookName: T, hookFn: HookFn<T>) {\n\t// @ts-ignore-next-line private options hooks usage\n\toptions[hookName] = hookFn.bind(null, options[hookName] || (() => {}));\n}\n\nlet currentComponent: Component | undefined;\nlet currentUpdater: Updater | undefined;\nlet finishUpdate: ReturnType<Updater[\"_setCurrent\"]> | undefined;\nconst updaterForComponent = new WeakMap<Component | VNode, Updater>();\n\nfunction setCurrentUpdater(updater?: Updater) {\n\t// end tracking for the current update:\n\tif (finishUpdate) finishUpdate(true, true);\n\t// start tracking the new update:\n\tcurrentUpdater = updater;\n\tfinishUpdate = updater && updater._setCurrent();\n}\n\nfunction createUpdater(updater: () => void) {\n\tconst s = signal(undefined) as Updater;\n\ts._canActivate = true;\n\ts._updater = updater;\n\treturn s;\n}\n\n// Get a (cached) Signal property updater for an element VNode\nfunction getElementUpdater(vnode: VNode) {\n\tlet updater = updaterForComponent.get(vnode) as ElementUpdater;\n\tif (!updater) {\n\t\tlet signalProps: Array<{ _key: string; _signal: Signal }> = [];\n\t\tupdater = createUpdater(() => {\n\t\t\tlet dom = vnode.__e as Element;\n\n\t\t\tfor (let i = 0; i < signalProps.length; i++) {\n\t\t\t\tlet { _key: prop, _signal: signal } = signalProps[i];\n\t\t\t\tlet value = signal._value;\n\t\t\t\tif (!dom) return;\n\t\t\t\tif (prop in dom) {\n\t\t\t\t\t// @ts-ignore-next-line silly\n\t\t\t\t\tdom[prop] = value;\n\t\t\t\t} else if (value) {\n\t\t\t\t\tdom.setAttribute(prop, value);\n\t\t\t\t} else {\n\t\t\t\t\tdom.removeAttribute(prop);\n\t\t\t\t}\n\t\t\t}\n\t\t}) as ElementUpdater;\n\t\tupdater._props = signalProps;\n\t\tupdaterForComponent.set(vnode, updater);\n\t} else {\n\t\tupdater._props.length = 0;\n\t}\n\treturn updater;\n}\n\n/** @todo This may be needed for complex prop value detection. */\n// function isSignalValue(value: any): value is Signal {\n// \tif (typeof value !== \"object\" || value == null) return false;\n// \tif (value instanceof Signal) return true;\n// \t// @TODO: uncomment this when we land Reactive (ideally behind a brand check)\n// \t// for (let i in value) if (value[i] instanceof Signal) return true;\n// \treturn false;\n// }\n\n/** Convert Signals within (nested) props.children into Text components */\nfunction childToSignal<T>(child: any, i: keyof T, arr: T) {\n\tif (typeof child !== \"object\" || child == null) {\n\t\t// can't be a signal\n\t} else if (Array.isArray(child)) {\n\t\tchild.forEach(childToSignal);\n\t} else if (child instanceof Signal) {\n\t\t// @ts-ignore-next-line yes, arr can accept VNodes:\n\t\tarr[i] = createElement(Text, { data: child });\n\t}\n}\n\n/**\n * A wrapper component that renders a Signal directly as a Text node.\n * @todo: in Preact 11, just decorate Signal with `type:null`\n */\nfunction Text(this: ComponentType, { data }: { data: Signal }) {\n\t// hasComputeds.add(this);\n\n\t// Store the props.data signal in another signal so that\n\t// passing a new signal reference re-runs the text computed:\n\tconst currentSignal = useSignal(data);\n\tcurrentSignal.value = data;\n\n\tconst s = useMemo(() => {\n\t\t// mark the parent component as having computeds so it gets optimized\n\t\tlet v = this.__v;\n\t\twhile ((v = v.__!)) {\n\t\t\tif (v.__c) {\n\t\t\t\thasComputeds.add(v.__c);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Replace this component's vdom updater with a direct text one:\n\t\tcurrentUpdater!._updater = () => {\n\t\t\t(this.base as Text).data = s._value;\n\t\t};\n\n\t\treturn computed(() => {\n\t\t\tlet data = currentSignal.value;\n\t\t\tlet s = data.value;\n\t\t\treturn s === 0 ? 0 : s === true ? \"\" : s || \"\";\n\t\t});\n\t}, []);\n\n\treturn s.value;\n}\nText.displayName = \"_st\";\n\n/** Inject low-level property/attribute bindings for Signals into Preact's diff */\nhook(OptionsTypes.DIFF, (old, vnode) => {\n\tif (typeof vnode.type === \"string\") {\n\t\t// let orig = vnode.__o || vnode;\n\t\tlet props = vnode.props;\n\t\tlet updater;\n\n\t\tfor (let i in props) {\n\t\t\tlet value = props[i];\n\t\t\tif (i === \"children\") {\n\t\t\t\tchildToSignal(value, \"children\", props);\n\t\t\t} else if (value instanceof Signal) {\n\t\t\t\t// first Signal prop triggers creation/cleanup of the updater:\n\t\t\t\tif (!updater) updater = getElementUpdater(vnode);\n\t\t\t\t// track which props are Signals for precise updates:\n\t\t\t\tupdater._props.push({ _key: i, _signal: value });\n\t\t\t\tlet newUpdater = updater._updater;\n\t\t\t\tif (value._updater) {\n\t\t\t\t\tlet oldUpdater = value._updater;\n\t\t\t\t\tvalue._updater = () => {\n\t\t\t\t\t\tnewUpdater();\n\t\t\t\t\t\toldUpdater();\n\t\t\t\t\t};\n\t\t\t\t} else {\n\t\t\t\t\tvalue._updater = newUpdater;\n\t\t\t\t}\n\t\t\t\tprops[i] = value.peek();\n\t\t\t}\n\t\t}\n\n\t\tsetCurrentUpdater(updater);\n\t}\n\n\told(vnode);\n});\n\n/** Set up Updater before rendering a component */\nhook(OptionsTypes.RENDER, (old, vnode) => {\n\tlet updater;\n\n\tlet component = vnode.__c;\n\tif (component) {\n\t\thasPendingUpdate.delete(component);\n\n\t\tupdater = updaterForComponent.get(component);\n\t\tif (updater === undefined) {\n\t\t\tupdater = createUpdater(() => {\n\t\t\t\thasPendingUpdate.add(component);\n\t\t\t\tcomponent.setState({});\n\t\t\t});\n\t\t\tupdaterForComponent.set(component, updater);\n\t\t}\n\t}\n\n\tcurrentComponent = component;\n\tsetCurrentUpdater(updater);\n\told(vnode);\n});\n\n/** Finish current updater if a component errors */\nhook(OptionsTypes.CATCH_ERROR, (old, error, vnode, oldVNode) => {\n\tsetCurrentUpdater();\n\tcurrentComponent = undefined;\n\told(error, vnode, oldVNode);\n});\n\n/** Finish current updater after rendering any VNode */\nhook(OptionsTypes.DIFFED, (old, vnode) => {\n\tsetCurrentUpdater();\n\tcurrentComponent = undefined;\n\told(vnode);\n});\n\n/** Unsubscribe from Signals when unmounting components/vnodes */\nhook(OptionsTypes.UNMOUNT, (old, vnode: VNode) => {\n\tlet thing = vnode.__c || vnode;\n\tconst updater = updaterForComponent.get(thing);\n\tif (updater) {\n\t\tupdaterForComponent.delete(thing);\n\t\tconst signals = updater._deps;\n\t\tif (signals) {\n\t\t\tsignals.forEach(signal => signal._subs.delete(updater));\n\t\t\tsignals.clear();\n\t\t}\n\t}\n\told(vnode);\n});\n\n/** Mark components that use hook state so we can skip sCU optimization. */\nhook(OptionsTypes.HOOK, (old, component, index, type) => {\n\tif (type < 3) hasHookState.add(component);\n\told(component, index, type);\n});\n\n/**\n * Auto-memoize components that use Signals/Computeds.\n * Note: Does _not_ optimize components that use hook/class state.\n */\nComponent.prototype.shouldComponentUpdate = function (props, state) {\n\t// @todo: Once preactjs/preact#3671 lands, this could just use `currentUpdater`:\n\tconst updater = updaterForComponent.get(this);\n\n\tconst hasSignals = updater && updater._deps?.size !== 0;\n\n\t// let reason;\n\t// if (!hasSignals && !hasComputeds.has(this)) {\n\t// \treason = \"no signals or computeds\";\n\t// } else if (hasPendingUpdate.has(this)) {\n\t// \treason = \"has pending update\";\n\t// } else if (hasHookState.has(this)) {\n\t// \treason = \"has hook state\";\n\t// }\n\t// if (reason) {\n\t// \tif (!this) reason += \" (`this` bug)\";\n\t// \tconsole.log(\"not optimizing\", this?.constructor?.name, \": \", reason, {\n\t// \t\tdetails: {\n\t// \t\t\thasSignals,\n\t// \t\t\thasComputeds: hasComputeds.has(this),\n\t// \t\t\thasPendingUpdate: hasPendingUpdate.has(this),\n\t// \t\t\thasHookState: hasHookState.has(this),\n\t// \t\t\tdeps: Array.from(updater._deps),\n\t// \t\t\tupdater,\n\t// \t\t},\n\t// \t});\n\t// }\n\n\t// if this component used no signals or computeds, update:\n\tif (!hasSignals && !hasComputeds.has(this)) return true;\n\n\t// if there is a pending re-render triggered from Signals, update:\n\tif (hasPendingUpdate.has(this)) return true;\n\n\t// if there is hook or class state, update:\n\tif (hasHookState.has(this)) return true;\n\tfor (let i in state) return true;\n\n\t// if any non-Signal props changed, update:\n\tfor (let i in props) {\n\t\tif (i !== \"__source\" && props[i] !== this.props[i]) return true;\n\t}\n\tfor (let i in this.props) if (!(i in props)) return true;\n\n\t// this is a purely Signal-driven component, don't update:\n\treturn false;\n};\n\nexport function useSignal<T>(value: T) {\n\treturn useMemo(() => signal<T>(value), []);\n}\n\nexport function useComputed<T>(compute: () => T) {\n\tconst $compute = useRef(compute);\n\t$compute.current = compute;\n\thasComputeds.add(currentComponent!);\n\treturn useMemo(() => computed<T>(() => $compute.current()), []);\n}\n\n/**\n * @todo Determine which Reactive implementation we'll be using.\n * @internal\n */\n// export function useReactive<T extends object>(value: T): Reactive<T> {\n// \treturn useMemo(() => reactive<T>(value), []);\n// }\n\n/**\n * @internal\n * Update a Reactive's using the properties of an object or other Reactive.\n * Also works for Signals.\n * @example\n * // Update a Reactive with Object.assign()-like syntax:\n * const r = reactive({ name: \"Alice\" });\n * update(r, { name: \"Bob\" });\n * update(r, { age: 42 }); // property 'age' does not exist in type '{ name?: string }'\n * update(r, 2); // '2' has no properties in common with '{ name?: string }'\n * console.log(r.name.value); // \"Bob\"\n *\n * @example\n * // Update a Reactive with the properties of another Reactive:\n * const A = reactive({ name: \"Alice\" });\n * const B = reactive({ name: \"Bob\", age: 42 });\n * update(A, B);\n * console.log(`${A.name} is ${A.age}`); // \"Bob is 42\"\n *\n * @example\n * // Update a signal with assign()-like syntax:\n * const s = signal(42);\n * update(s, \"hi\"); // Argument type 'string' not assignable to type 'number'\n * update(s, {}); // Argument type '{}' not assignable to type 'number'\n * update(s, 43);\n * console.log(s.value); // 43\n *\n * @param obj The Reactive or Signal to be updated\n * @param update The value, Signal, object or Reactive to update `obj` to match\n * @param overwrite If `true`, any properties `obj` missing from `update` are set to `undefined`\n */\n/*\nexport function update<T extends SignalOrReactive>(\n\tobj: T,\n\tupdate: Partial<Unwrap<T>>,\n\toverwrite = false\n) {\n\tif (obj instanceof Signal) {\n\t\tobj.value = peekValue(update);\n\t} else {\n\t\tfor (let i in update) {\n\t\t\tif (i in obj) {\n\t\t\t\tobj[i].value = peekValue(update[i]);\n\t\t\t} else {\n\t\t\t\tlet sig = signal(peekValue(update[i]));\n\t\t\t\tsig[KEY] = i;\n\t\t\t\tobj[i] = sig;\n\t\t\t}\n\t\t}\n\t\tif (overwrite) {\n\t\t\tfor (let i in obj) {\n\t\t\t\tif (!(i in update)) {\n\t\t\t\t\tobj[i].value = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n*/\n"],"names":["currentComponent","finishUpdate","hasPendingUpdate","WeakSet","hasComputeds","hook","hookName","hookFn","options","bind","updaterForComponent","setCurrentUpdater","updater","currentUpdater","_setCurrent","createUpdater","s","signal","undefined","_canActivate","_updater","getElementUpdater","vnode","get","_props","length","signalProps","dom","__e","i","prop","_key","_signal","_value","value","setAttribute","removeAttribute","set","childToSignal","child","arr","Array","isArray","forEach","Signal","createElement","Text","data","_ref","_this","this","currentSignal","useSignal","useMemo","v","__v","__","__c","add","base","computed","displayName","old","type","props","push","newUpdater","oldUpdater","peek","component","setState","error","oldVNode","thing","signals","_deps","_subs","clear","index","hasHookState","Component","prototype","shouldComponentUpdate","state","_updater$_deps","size","has","_i","_i2","useComputed","compute","$compute","useRef","current"],"mappings":"qYAsBA,IAcIA,IAEJC,EAhBsBC,EAAG,IAAIC,UAGR,IAArBA,QAGMC,EAAe,YAGrB,SAAAC,EAAsCC,EAAaC,GAElDC,EAAAA,QAAQF,GAAYC,EAAOE,KAAK,KAAMD,EAAAA,QAAQF,IAAc,WAAO,EACnE,CAKD,IAAMI,EAAsB,YAE5B,SAAAC,EAA2BC,GAEtBX,GAAcA,GAAa,GAAM,GAErCY,EAAiBD,EACjBX,EAAeW,GAAWA,EAAQE,GAClC,CAED,SAASC,EAAcH,GACtB,IAAOI,EAAGC,EAAMA,YAACC,GAGjB,OAFAF,EAAEG,IAAe,EACjBH,EAAEI,GAAWR,EACNI,CACP,CAGD,SAAAK,EAA2BC,GAC1B,IAAWV,EAAGF,EAAoBa,IAAID,GACtC,GAAKV,EAsBJA,EAAQY,GAAOC,OAAS,MAtBX,CACb,IAAeC,EAA6C,IAC5Dd,EAAUG,EAAc,WAGvB,IAFA,IAAOY,EAAGL,EAAMM,IAEPC,EAAI,EAAGA,EAAIH,EAAYD,OAAQI,IAAK,CAC5C,IAAsCH,EAAAA,EAAYG,GAAtCC,EAANC,EAAAA,IAAYC,EAAAA,EACCC,GACnB,IAAKN,EAAK,OACNG,KAAJH,EAECA,EAAIG,GAAQI,EACFA,EACVP,EAAIQ,aAAaL,EAAMI,GAEvBP,EAAIS,gBAAgBN,EAErB,CACD,IACON,GAASE,EACjBhB,EAAoB2B,IAAIf,EAAOV,EAC/B,CAGD,OAAOA,CACP,CAYD,SAAS0B,EAAiBC,EAAYV,EAAYW,GAC5B,iBAAjBD,GAAsC,MAATA,IAEtBE,MAAMC,QAAQH,GACxBA,EAAMI,QAAQL,GACJC,aAAJK,EAAAA,SAENJ,EAAIX,GAAKgB,EAAAA,cAAcC,EAAM,CAAEC,KAAMR,KAEtC,CAMD,SAASO,EAAoDE,GAAA,IAAAC,EAAAC,KAAAH,EAAAC,EAAxBD,KAKjBI,EAAGC,EAAUL,GAChCI,EAAcjB,MAAQa,EAEtB,MAAUM,EAAOA,QAAC,WAGjB,IADA,IAAKC,EAAGL,EAAKM,IACLD,EAAIA,EAAEE,IACb,GAAIF,EAAEG,IAAK,CACVrD,EAAasD,IAAIJ,EAAEG,KACnB,KACA,CAQF,OAJA5C,EAAgBO,GAAW,WACzB6B,EAAKU,KAAcZ,KAAO/B,EAAEiB,EAC7B,EAEM2B,EAAAA,SAAS,WACf,IACI5C,EADOmC,EAAcjB,MACZA,MACb,OAAa,IAANlB,EAAU,GAAU,IAANA,EAAa,GAAKA,GAAK,EAC5C,EACD,EAAE,IAEH,OAAQA,EAACkB,KACT,CAqJekB,SAAAA,EAAalB,GAC5B,OAAOmB,EAAOA,QAAC,WAAMpC,OAAAA,EAAAA,OAAUiB,EAAhB,EAAwB,GACvC,CAtJDY,EAAKe,YAAc,MAGnBxD,QAAwB,SAACyD,EAAKxC,GAC7B,GAA0B,iBAAVA,EAACyC,KAAmB,CAEnC,IACAnD,EADIoD,EAAQ1C,EAAM0C,MAGlB,IAAK,SAASA,EAAO,CACpB,IAAI9B,EAAQ8B,EAAMnC,GACR,aAANA,EACHS,EAAcJ,EAAO,WAAY8B,GACvB9B,aAAiBU,EAAAA,QAAQ,WAE9BhC,IAASA,EAAUS,EAAkBC,IAE1CV,EAAQY,GAAOyC,KAAK,CAAElC,EAAMF,EAAGG,EAASE,IACxC,IAAcgC,EAAGtD,EAAQQ,GACzB,GAAIc,EAAMd,GAAU,CACnB,IAAc+C,EAAGjC,EAAMd,GACvBc,EAAMd,GAAW,WAChB8C,IACAC,GACA,CACD,MACAjC,EAAMd,GAAW8C,EAElBF,EAAMnC,GAAKK,EAAMkC,MACjB,CAhBmC,EAiBpC,CAEDzD,EAAkBC,EAClB,CAEDkD,EAAIxC,EACJ,GAGDjB,QAA0B,SAACyD,EAAKxC,GAC/B,IAAAV,EAEayD,EAAG/C,EAAMmC,IAClBY,IACHnE,EAAA,OAAwBmE,QAGRnD,KADhBN,EAAUF,EAAoBa,IAAI8C,MAEjCzD,EAAUG,EAAc,WACvBb,EAAiBwD,IAAIW,GACrBA,EAAUC,SAAS,CAAA,EACnB,GACD5D,EAAoB2B,IAAIgC,EAAWzD,KAIrCZ,EAAmBqE,EACnB1D,EAAkBC,GAClBkD,EAAIxC,EACJ,GAGDjB,EAAI,MAA2B,SAACyD,EAAKS,EAAOjD,EAAOkD,GAClD7D,IACAX,OAAmBkB,EACnB4C,EAAIS,EAAOjD,EAAOkD,EAClB,GAGDnE,WAA0B,SAACyD,EAAKxC,GAC/BX,IACAX,OAAmBkB,EACnB4C,EAAIxC,EACJ,GAGDjB,YAA2B,SAACyD,EAAKxC,GAChC,IAAImD,EAAQnD,EAAMmC,KAAOnC,EACnBV,EAAUF,EAAoBa,IAAIkD,GACxC,GAAI7D,EAAS,CACZF,EAAA,OAA2B+D,GAC3B,IAAMC,EAAU9D,EAAQ+D,GACpBD,IACHA,EAAQ/B,QAAQ,SAAA1B,GAAUA,OAAAA,EAAO2D,UAAahE,EAAxB,GACtB8D,EAAQG,QAET,CACDf,EAAIxC,EACJ,GAGDjB,EAAI,MAAoB,SAACyD,EAAKO,EAAWS,EAAOf,GAC3CA,EAAO,GAAGgB,EAAarB,IAAIW,GAC/BP,EAAIO,EAAWS,EAAOf,EACtB,GAMDiB,YAAUC,UAAUC,sBAAwB,SAAUlB,EAAOmB,GAAK,IAAAC,EAE3DxE,EAAUF,EAAoBa,IAAI2B,MA2BxC,KAzBmBtC,GAAmC,KAATyE,OAAfzE,EAAAA,EAAQ+D,SAAOU,EAAAA,EAAAA,OAyBzBjF,EAAakF,IAAIpC,OAAO,OAAA,EAG5C,GAAIhD,EAAiBoF,IAAIpC,MAAO,OAAA,EAGhC,GAAI6B,EAAaO,IAAIpC,MAAO,OAAA,EAC5B,IAAK,IAALrB,OAAqB,OAArB,EAGA,IAAK,IAAL0D,OACC,GAAU,aAAN1D,GAAoBmC,EAAMnC,KAAOqB,KAAKc,MAAMnC,GAAI,OACpD,EACD,IAAK,IAAL2D,UAAmBxB,MAAO,KAAMnC,KAAFmC,GAAe,OAAO,EAGpD,OACA,CAAA,4aAMeyB,SAAeC,GAC9B,IAAcC,EAAGC,EAAAA,OAAOF,GAGxB,OAFAC,EAASE,QAAUH,EACnBtF,EAAasD,IAAI1D,GACVqD,UAAQ,WAAA,OAAcO,EAAAA,SAAI,kBAAc+B,EAACE,SAAf,EAAlB,EAA6C,GAC5D"}
|
package/dist/signals.mjs
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
import{Component as t,options as e,createElement as n}from"preact";import{useMemo as r,useRef as i}from"preact/hooks";import{Signal as o,signal as f,computed as l}from"@preact/signals-core";export{Signal,batch,computed,effect,signal}from"@preact/signals-core";const c=new WeakSet,s=new WeakSet,u=new WeakSet;function a(t,n){e[t]=n.bind(null,e[t]||(()=>{}))}let _,h,p;const d=new WeakMap;function m(t){p&&p(!0,!0),h=t,p=t&&t._()}function k(t){const e=f(void 0);return e._c=!0,e._u=t,e}function g(t){let e=d.get(t);if(e)e.__.length=0;else{let n=[];e=k(()=>{let e=t.__e;for(let t=0;t<n.length;t++){let{t:r,i}=n[t],o=i._v;if(!e)return;r in e?e[r]=o:o?e.setAttribute(r,o):e.removeAttribute(r)}}),e.__=n,d.set(t,e)}return e}function v(t,e,r){"object"!=typeof t||null==t||(Array.isArray(t)?t.forEach(v):t instanceof o&&(r[e]=n(y,{data:t})))}function y({data:t}){const e=r(()=>{let
|
|
1
|
+
import{Component as t,options as e,createElement as n}from"preact";import{useMemo as r,useRef as i}from"preact/hooks";import{Signal as o,signal as f,computed as l}from"@preact/signals-core";export{Signal,batch,computed,effect,signal}from"@preact/signals-core";const c=new WeakSet,s=new WeakSet,u=new WeakSet;function a(t,n){e[t]=n.bind(null,e[t]||(()=>{}))}let _,h,p;const d=new WeakMap;function m(t){p&&p(!0,!0),h=t,p=t&&t._()}function k(t){const e=f(void 0);return e._c=!0,e._u=t,e}function g(t){let e=d.get(t);if(e)e.__.length=0;else{let n=[];e=k(()=>{let e=t.__e;for(let t=0;t<n.length;t++){let{t:r,i}=n[t],o=i._v;if(!e)return;r in e?e[r]=o:o?e.setAttribute(r,o):e.removeAttribute(r)}}),e.__=n,d.set(t,e)}return e}function v(t,e,r){"object"!=typeof t||null==t||(Array.isArray(t)?t.forEach(v):t instanceof o&&(r[e]=n(y,{data:t})))}function y({data:t}){const e=b(t);e.value=t;const n=r(()=>{let t=this.__v;for(;t=t.__;)if(t.__c){u.add(t.__c);break}return h._u=()=>{this.base.data=n._v},l(()=>{let t=e.value.value;return 0===t?0:!0===t?"":t||""})},[]);return n.value}function b(t){return r(()=>f(t),[])}function w(t){const e=i(t);return e.current=t,u.add(_),r(()=>l(()=>e.current()),[])}y.displayName="_st",a("__b",(t,e)=>{if("string"==typeof e.type){let t,n=e.props;for(let r in n){let i=n[r];if("children"===r)v(i,"children",n);else if(i instanceof o){t||(t=g(e)),t.__.push({t:r,i});let o=t._u;if(i._u){let t=i._u;i._u=()=>{o(),t()}}else i._u=o;n[r]=i.peek()}}m(t)}t(e)}),a("__r",(t,e)=>{let n,r=e.__c;r&&(c.delete(r),n=d.get(r),void 0===n&&(n=k(()=>{c.add(r),r.setState({})}),d.set(r,n))),_=r,m(n),t(e)}),a("__e",(t,e,n,r)=>{m(),_=void 0,t(e,n,r)}),a("diffed",(t,e)=>{m(),_=void 0,t(e)}),a("unmount",(t,e)=>{let n=e.__c||e;const r=d.get(n);if(r){d.delete(n);const t=r._d;t&&(t.forEach(t=>t._s.delete(r)),t.clear())}t(e)}),a("__h",(t,e,n,r)=>{r<3&&s.add(e),t(e,n,r)}),t.prototype.shouldComponentUpdate=function(t,e){var n;const r=d.get(this);if(!(r&&0!==(null==(n=r._d)?void 0:n.size)||u.has(this)))return!0;if(c.has(this))return!0;if(s.has(this))return!0;for(let t in e)return!0;for(let e in t)if("__source"!==e&&t[e]!==this.props[e])return!0;for(let e in this.props)if(!(e in t))return!0;return!1};export{w as useComputed,b as useSignal};
|
|
2
|
+
//# sourceMappingURL=signals.mjs.map
|
package/dist/signals.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signals.mjs","sources":["../src/index.ts"],"sourcesContent":["import { options, Component, createElement } from \"preact\";\nimport { useRef, useMemo } from \"preact/hooks\";\nimport {\n\tsignal,\n\tcomputed,\n\tbatch,\n\teffect,\n\tSignal,\n\ttype ReadonlySignal,\n} from \"@preact/signals-core\";\nimport {\n\tVNode,\n\tComponentType,\n\tOptionsTypes,\n\tHookFn,\n\tUpdater,\n\tElementUpdater,\n} from \"./internal\";\n\nexport { signal, computed, batch, effect, Signal, type ReadonlySignal };\n\n// Components that have a pending Signal update: (used to bypass default sCU:false)\nconst hasPendingUpdate = new WeakSet<Component>();\n\n// Components that have useState()/useReducer() hooks:\nconst hasHookState = new WeakSet<Component>();\n\n// Components that have useComputed():\nconst hasComputeds = new WeakSet<Component>();\n\n// Install a Preact options hook\nfunction hook<T extends OptionsTypes>(hookName: T, hookFn: HookFn<T>) {\n\t// @ts-ignore-next-line private options hooks usage\n\toptions[hookName] = hookFn.bind(null, options[hookName] || (() => {}));\n}\n\nlet currentComponent: Component | undefined;\nlet currentUpdater: Updater | undefined;\nlet finishUpdate: ReturnType<Updater[\"_setCurrent\"]> | undefined;\nconst updaterForComponent = new WeakMap<Component | VNode, Updater>();\n\nfunction setCurrentUpdater(updater?: Updater) {\n\t// end tracking for the current update:\n\tif (finishUpdate) finishUpdate(true, true);\n\t// start tracking the new update:\n\tcurrentUpdater = updater;\n\tfinishUpdate = updater && updater._setCurrent();\n}\n\nfunction createUpdater(updater: () => void) {\n\tconst s = signal(undefined) as Updater;\n\ts._canActivate = true;\n\ts._updater = updater;\n\treturn s;\n}\n\n// Get a (cached) Signal property updater for an element VNode\nfunction getElementUpdater(vnode: VNode) {\n\tlet updater = updaterForComponent.get(vnode) as ElementUpdater;\n\tif (!updater) {\n\t\tlet signalProps: Array<{ _key: string, _signal: Signal }> = [];\n\t\tupdater = createUpdater(() => {\n\t\t\tlet dom = vnode.__e as Element;\n\n\t\t\tfor (let i = 0; i < signalProps.length; i++) {\n\t\t\t\tlet { _key: prop, _signal: signal } = signalProps[i];\n\t\t\t\tlet value = signal._value;\n\t\t\t\tif (!dom) return;\n\t\t\t\tif (prop in dom) {\n\t\t\t\t\t// @ts-ignore-next-line silly\n\t\t\t\t\tdom[prop] = value;\n\t\t\t\t} else if (value) {\n\t\t\t\t\tdom.setAttribute(prop, value);\n\t\t\t\t} else {\n\t\t\t\t\tdom.removeAttribute(prop);\n\t\t\t\t}\n\t\t\t}\n\t\t}) as ElementUpdater;\n\t\tupdater._props = signalProps;\n\t\tupdaterForComponent.set(vnode, updater);\n\t} else {\n\t\tupdater._props.length = 0;\n\t}\n\treturn updater;\n}\n\n/** @todo This may be needed for complex prop value detection. */\n// function isSignalValue(value: any): value is Signal {\n// \tif (typeof value !== \"object\" || value == null) return false;\n// \tif (value instanceof Signal) return true;\n// \t// @TODO: uncomment this when we land Reactive (ideally behind a brand check)\n// \t// for (let i in value) if (value[i] instanceof Signal) return true;\n// \treturn false;\n// }\n\n/** Convert Signals within (nested) props.children into Text components */\nfunction childToSignal<T>(child: any, i: keyof T, arr: T) {\n\tif (typeof child !== \"object\" || child == null) {\n\t\t// can't be a signal\n\t} else if (Array.isArray(child)) {\n\t\tchild.forEach(childToSignal);\n\t} else if (child instanceof Signal) {\n\t\t// @ts-ignore-next-line yes, arr can accept VNodes:\n\t\tarr[i] = createElement(Text, { data: child });\n\t}\n}\n\n/**\n * A wrapper component that renders a Signal directly as a Text node.\n * @todo: in Preact 11, just decorate Signal with `type:null`\n */\nfunction Text(this: ComponentType, { data }: { data: Signal }) {\n\t// hasComputeds.add(this);\n\n\tconst s = useMemo(() => {\n\t\t// mark the parent component as having computeds so it gets optimized\n\t\tlet v = this.__v;\n\t\twhile ((v = v.__!)) {\n\t\t\tif (v.__c) {\n\t\t\t\thasComputeds.add(v.__c);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Replace this component's vdom updater with a direct text one:\n\t\tcurrentUpdater!._updater = () => {\n\t\t\t(this.base as Text).data = s._value;\n\t\t};\n\n\t\treturn computed(() => {\n\t\t\tlet s = data.value;\n\t\t\treturn s === 0 ? 0 : s === true ? \"\" : s || \"\";\n\t\t});\n\t}, []);\n\n\treturn s.value;\n}\nText.displayName = \"_st\";\n\n/** Inject low-level property/attribute bindings for Signals into Preact's diff */\nhook(OptionsTypes.DIFF, (old, vnode) => {\n\tif (typeof vnode.type === \"string\") {\n\t\t// let orig = vnode.__o || vnode;\n\t\tlet props = vnode.props;\n\t\tlet updater;\n\n\t\tfor (let i in props) {\n\t\t\tlet value = props[i];\n\t\t\tif (i === \"children\") {\n\t\t\t\tchildToSignal(value, \"children\", props);\n\t\t\t} else if (value instanceof Signal) {\n\t\t\t\t// first Signal prop triggers creation/cleanup of the updater:\n\t\t\t\tif (!updater) updater = getElementUpdater(vnode);\n\t\t\t\t// track which props are Signals for precise updates:\n\t\t\t\tupdater._props.push({ _key: i, _signal: value });\n\t\t\t\tlet newUpdater = updater._updater\n\t\t\t\tif (value._updater) {\n\t\t\t\t\tlet oldUpdater = value._updater\n\t\t\t\t\tvalue._updater = () => {\n\t\t\t\t\t\tnewUpdater();\n\t\t\t\t\t\toldUpdater();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tvalue._updater = newUpdater\n\t\t\t\t}\n\t\t\t\tprops[i] = value.peek()\n\t\t\t}\n\t\t}\n\n\t\tsetCurrentUpdater(updater);\n\t}\n\n\told(vnode);\n});\n\n/** Set up Updater before rendering a component */\nhook(OptionsTypes.RENDER, (old, vnode) => {\n\tlet updater;\n\n\tlet component = vnode.__c;\n\tif (component) {\n\t\thasPendingUpdate.delete(component);\n\n\t\tupdater = updaterForComponent.get(component);\n\t\tif (updater === undefined) {\n\t\t\tupdater = createUpdater(() => {\n\t\t\t\thasPendingUpdate.add(component);\n\t\t\t\tcomponent.setState({});\n\t\t\t});\n\t\t\tupdaterForComponent.set(component, updater);\n\t\t}\n\t}\n\n\tcurrentComponent = component;\n\tsetCurrentUpdater(updater);\n\told(vnode);\n});\n\n/** Finish current updater if a component errors */\nhook(OptionsTypes.CATCH_ERROR, (old, error, vnode, oldVNode) => {\n\tsetCurrentUpdater();\n\tcurrentComponent = undefined;\n\told(error, vnode, oldVNode);\n});\n\n/** Finish current updater after rendering any VNode */\nhook(OptionsTypes.DIFFED, (old, vnode) => {\n\tsetCurrentUpdater();\n\tcurrentComponent = undefined;\n\told(vnode);\n});\n\n/** Unsubscribe from Signals when unmounting components/vnodes */\nhook(OptionsTypes.UNMOUNT, (old, vnode: VNode) => {\n\tlet thing = vnode.__c || vnode;\n\tconst updater = updaterForComponent.get(thing);\n\tif (updater) {\n\t\tupdaterForComponent.delete(thing);\n\t\tconst signals = updater._deps;\n\t\tif (signals) {\n\t\t\tsignals.forEach(signal => signal._subs.delete(updater));\n\t\t\tsignals.clear();\n\t\t}\n\t}\n\told(vnode);\n});\n\n/** Mark components that use hook state so we can skip sCU optimization. */\nhook(OptionsTypes.HOOK, (old, component, index, type) => {\n\tif (type < 3) hasHookState.add(component);\n\told(component, index, type);\n});\n\n/**\n * Auto-memoize components that use Signals/Computeds.\n * Note: Does _not_ optimize components that use hook/class state.\n */\nComponent.prototype.shouldComponentUpdate = function (props, state) {\n\t// @todo: Once preactjs/preact#3671 lands, this could just use `currentUpdater`:\n\tconst updater = updaterForComponent.get(this);\n\n\tconst hasSignals = updater && updater._deps?.size !== 0;\n\n\t// let reason;\n\t// if (!hasSignals && !hasComputeds.has(this)) {\n\t// \treason = \"no signals or computeds\";\n\t// } else if (hasPendingUpdate.has(this)) {\n\t// \treason = \"has pending update\";\n\t// } else if (hasHookState.has(this)) {\n\t// \treason = \"has hook state\";\n\t// }\n\t// if (reason) {\n\t// \tif (!this) reason += \" (`this` bug)\";\n\t// \tconsole.log(\"not optimizing\", this?.constructor?.name, \": \", reason, {\n\t// \t\tdetails: {\n\t// \t\t\thasSignals,\n\t// \t\t\thasComputeds: hasComputeds.has(this),\n\t// \t\t\thasPendingUpdate: hasPendingUpdate.has(this),\n\t// \t\t\thasHookState: hasHookState.has(this),\n\t// \t\t\tdeps: Array.from(updater._deps),\n\t// \t\t\tupdater,\n\t// \t\t},\n\t// \t});\n\t// }\n\n\t// if this component used no signals or computeds, update:\n\tif (!hasSignals && !hasComputeds.has(this)) return true;\n\n\t// if there is a pending re-render triggered from Signals, update:\n\tif (hasPendingUpdate.has(this)) return true;\n\n\t// if there is hook or class state, update:\n\tif (hasHookState.has(this)) return true;\n\tfor (let i in state) return true;\n\n\t// if any non-Signal props changed, update:\n\tfor (let i in props) {\n\t\tif (i !== \"__source\" && props[i] !== this.props[i]) return true;\n\t}\n\tfor (let i in this.props) if (!(i in props)) return true;\n\n\t// this is a purely Signal-driven component, don't update:\n\treturn false;\n};\n\nexport function useSignal<T>(value: T) {\n\treturn useMemo(() => signal<T>(value), []);\n}\n\nexport function useComputed<T>(compute: () => T) {\n\tconst $compute = useRef(compute);\n\t$compute.current = compute;\n\thasComputeds.add(currentComponent!);\n\treturn useMemo(() => computed<T>(() => $compute.current()), []);\n}\n\n/**\n * @todo Determine which Reactive implementation we'll be using.\n * @internal\n */\n// export function useReactive<T extends object>(value: T): Reactive<T> {\n// \treturn useMemo(() => reactive<T>(value), []);\n// }\n\n/**\n * @internal\n * Update a Reactive's using the properties of an object or other Reactive.\n * Also works for Signals.\n * @example\n * // Update a Reactive with Object.assign()-like syntax:\n * const r = reactive({ name: \"Alice\" });\n * update(r, { name: \"Bob\" });\n * update(r, { age: 42 }); // property 'age' does not exist in type '{ name?: string }'\n * update(r, 2); // '2' has no properties in common with '{ name?: string }'\n * console.log(r.name.value); // \"Bob\"\n *\n * @example\n * // Update a Reactive with the properties of another Reactive:\n * const A = reactive({ name: \"Alice\" });\n * const B = reactive({ name: \"Bob\", age: 42 });\n * update(A, B);\n * console.log(`${A.name} is ${A.age}`); // \"Bob is 42\"\n *\n * @example\n * // Update a signal with assign()-like syntax:\n * const s = signal(42);\n * update(s, \"hi\"); // Argument type 'string' not assignable to type 'number'\n * update(s, {}); // Argument type '{}' not assignable to type 'number'\n * update(s, 43);\n * console.log(s.value); // 43\n *\n * @param obj The Reactive or Signal to be updated\n * @param update The value, Signal, object or Reactive to update `obj` to match\n * @param overwrite If `true`, any properties `obj` missing from `update` are set to `undefined`\n */\n/*\nexport function update<T extends SignalOrReactive>(\n\tobj: T,\n\tupdate: Partial<Unwrap<T>>,\n\toverwrite = false\n) {\n\tif (obj instanceof Signal) {\n\t\tobj.value = peekValue(update);\n\t} else {\n\t\tfor (let i in update) {\n\t\t\tif (i in obj) {\n\t\t\t\tobj[i].value = peekValue(update[i]);\n\t\t\t} else {\n\t\t\t\tlet sig = signal(peekValue(update[i]));\n\t\t\t\tsig[KEY] = i;\n\t\t\t\tobj[i] = sig;\n\t\t\t}\n\t\t}\n\t\tif (overwrite) {\n\t\t\tfor (let i in obj) {\n\t\t\t\tif (!(i in update)) {\n\t\t\t\t\tobj[i].value = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n*/\n"],"names":["Component","options","createElement","useMemo","useRef","Signal","signal","computed","batch","effect","hasPendingUpdate","WeakSet","hasComputeds","hook","hookName","hookFn","bind","currentComponent","currentUpdater","finishUpdate","WeakMap","setCurrentUpdater","updater","_setCurrent","createUpdater","s","undefined","_canActivate","_updater","getElementUpdater","vnode","updaterForComponent","get","_props","length","dom","__e","i","signalProps","_key","prop","_signal","value","_value","setAttribute","removeAttribute","set","childToSignal","child","arr","Array","isArray","forEach","Text","data","v","this","__v","__","__c","add","base","useSignal","useComputed","compute","$compute","current","displayName","old","type","props","push","newUpdater","oldUpdater","peek","component","delete","setState","error","oldVNode","thing","signals","_deps","_subs","clear","index","hasHookState","prototype","shouldComponentUpdate","state","_updater$_deps","size","has"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"signals.mjs","sources":["../src/index.ts"],"sourcesContent":["import { options, Component, createElement } from \"preact\";\nimport { useRef, useMemo } from \"preact/hooks\";\nimport {\n\tsignal,\n\tcomputed,\n\tbatch,\n\teffect,\n\tSignal,\n\ttype ReadonlySignal,\n} from \"@preact/signals-core\";\nimport {\n\tVNode,\n\tComponentType,\n\tOptionsTypes,\n\tHookFn,\n\tUpdater,\n\tElementUpdater,\n} from \"./internal\";\n\nexport { signal, computed, batch, effect, Signal, type ReadonlySignal };\n\n// Components that have a pending Signal update: (used to bypass default sCU:false)\nconst hasPendingUpdate = new WeakSet<Component>();\n\n// Components that have useState()/useReducer() hooks:\nconst hasHookState = new WeakSet<Component>();\n\n// Components that have useComputed():\nconst hasComputeds = new WeakSet<Component>();\n\n// Install a Preact options hook\nfunction hook<T extends OptionsTypes>(hookName: T, hookFn: HookFn<T>) {\n\t// @ts-ignore-next-line private options hooks usage\n\toptions[hookName] = hookFn.bind(null, options[hookName] || (() => {}));\n}\n\nlet currentComponent: Component | undefined;\nlet currentUpdater: Updater | undefined;\nlet finishUpdate: ReturnType<Updater[\"_setCurrent\"]> | undefined;\nconst updaterForComponent = new WeakMap<Component | VNode, Updater>();\n\nfunction setCurrentUpdater(updater?: Updater) {\n\t// end tracking for the current update:\n\tif (finishUpdate) finishUpdate(true, true);\n\t// start tracking the new update:\n\tcurrentUpdater = updater;\n\tfinishUpdate = updater && updater._setCurrent();\n}\n\nfunction createUpdater(updater: () => void) {\n\tconst s = signal(undefined) as Updater;\n\ts._canActivate = true;\n\ts._updater = updater;\n\treturn s;\n}\n\n// Get a (cached) Signal property updater for an element VNode\nfunction getElementUpdater(vnode: VNode) {\n\tlet updater = updaterForComponent.get(vnode) as ElementUpdater;\n\tif (!updater) {\n\t\tlet signalProps: Array<{ _key: string; _signal: Signal }> = [];\n\t\tupdater = createUpdater(() => {\n\t\t\tlet dom = vnode.__e as Element;\n\n\t\t\tfor (let i = 0; i < signalProps.length; i++) {\n\t\t\t\tlet { _key: prop, _signal: signal } = signalProps[i];\n\t\t\t\tlet value = signal._value;\n\t\t\t\tif (!dom) return;\n\t\t\t\tif (prop in dom) {\n\t\t\t\t\t// @ts-ignore-next-line silly\n\t\t\t\t\tdom[prop] = value;\n\t\t\t\t} else if (value) {\n\t\t\t\t\tdom.setAttribute(prop, value);\n\t\t\t\t} else {\n\t\t\t\t\tdom.removeAttribute(prop);\n\t\t\t\t}\n\t\t\t}\n\t\t}) as ElementUpdater;\n\t\tupdater._props = signalProps;\n\t\tupdaterForComponent.set(vnode, updater);\n\t} else {\n\t\tupdater._props.length = 0;\n\t}\n\treturn updater;\n}\n\n/** @todo This may be needed for complex prop value detection. */\n// function isSignalValue(value: any): value is Signal {\n// \tif (typeof value !== \"object\" || value == null) return false;\n// \tif (value instanceof Signal) return true;\n// \t// @TODO: uncomment this when we land Reactive (ideally behind a brand check)\n// \t// for (let i in value) if (value[i] instanceof Signal) return true;\n// \treturn false;\n// }\n\n/** Convert Signals within (nested) props.children into Text components */\nfunction childToSignal<T>(child: any, i: keyof T, arr: T) {\n\tif (typeof child !== \"object\" || child == null) {\n\t\t// can't be a signal\n\t} else if (Array.isArray(child)) {\n\t\tchild.forEach(childToSignal);\n\t} else if (child instanceof Signal) {\n\t\t// @ts-ignore-next-line yes, arr can accept VNodes:\n\t\tarr[i] = createElement(Text, { data: child });\n\t}\n}\n\n/**\n * A wrapper component that renders a Signal directly as a Text node.\n * @todo: in Preact 11, just decorate Signal with `type:null`\n */\nfunction Text(this: ComponentType, { data }: { data: Signal }) {\n\t// hasComputeds.add(this);\n\n\t// Store the props.data signal in another signal so that\n\t// passing a new signal reference re-runs the text computed:\n\tconst currentSignal = useSignal(data);\n\tcurrentSignal.value = data;\n\n\tconst s = useMemo(() => {\n\t\t// mark the parent component as having computeds so it gets optimized\n\t\tlet v = this.__v;\n\t\twhile ((v = v.__!)) {\n\t\t\tif (v.__c) {\n\t\t\t\thasComputeds.add(v.__c);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Replace this component's vdom updater with a direct text one:\n\t\tcurrentUpdater!._updater = () => {\n\t\t\t(this.base as Text).data = s._value;\n\t\t};\n\n\t\treturn computed(() => {\n\t\t\tlet data = currentSignal.value;\n\t\t\tlet s = data.value;\n\t\t\treturn s === 0 ? 0 : s === true ? \"\" : s || \"\";\n\t\t});\n\t}, []);\n\n\treturn s.value;\n}\nText.displayName = \"_st\";\n\n/** Inject low-level property/attribute bindings for Signals into Preact's diff */\nhook(OptionsTypes.DIFF, (old, vnode) => {\n\tif (typeof vnode.type === \"string\") {\n\t\t// let orig = vnode.__o || vnode;\n\t\tlet props = vnode.props;\n\t\tlet updater;\n\n\t\tfor (let i in props) {\n\t\t\tlet value = props[i];\n\t\t\tif (i === \"children\") {\n\t\t\t\tchildToSignal(value, \"children\", props);\n\t\t\t} else if (value instanceof Signal) {\n\t\t\t\t// first Signal prop triggers creation/cleanup of the updater:\n\t\t\t\tif (!updater) updater = getElementUpdater(vnode);\n\t\t\t\t// track which props are Signals for precise updates:\n\t\t\t\tupdater._props.push({ _key: i, _signal: value });\n\t\t\t\tlet newUpdater = updater._updater;\n\t\t\t\tif (value._updater) {\n\t\t\t\t\tlet oldUpdater = value._updater;\n\t\t\t\t\tvalue._updater = () => {\n\t\t\t\t\t\tnewUpdater();\n\t\t\t\t\t\toldUpdater();\n\t\t\t\t\t};\n\t\t\t\t} else {\n\t\t\t\t\tvalue._updater = newUpdater;\n\t\t\t\t}\n\t\t\t\tprops[i] = value.peek();\n\t\t\t}\n\t\t}\n\n\t\tsetCurrentUpdater(updater);\n\t}\n\n\told(vnode);\n});\n\n/** Set up Updater before rendering a component */\nhook(OptionsTypes.RENDER, (old, vnode) => {\n\tlet updater;\n\n\tlet component = vnode.__c;\n\tif (component) {\n\t\thasPendingUpdate.delete(component);\n\n\t\tupdater = updaterForComponent.get(component);\n\t\tif (updater === undefined) {\n\t\t\tupdater = createUpdater(() => {\n\t\t\t\thasPendingUpdate.add(component);\n\t\t\t\tcomponent.setState({});\n\t\t\t});\n\t\t\tupdaterForComponent.set(component, updater);\n\t\t}\n\t}\n\n\tcurrentComponent = component;\n\tsetCurrentUpdater(updater);\n\told(vnode);\n});\n\n/** Finish current updater if a component errors */\nhook(OptionsTypes.CATCH_ERROR, (old, error, vnode, oldVNode) => {\n\tsetCurrentUpdater();\n\tcurrentComponent = undefined;\n\told(error, vnode, oldVNode);\n});\n\n/** Finish current updater after rendering any VNode */\nhook(OptionsTypes.DIFFED, (old, vnode) => {\n\tsetCurrentUpdater();\n\tcurrentComponent = undefined;\n\told(vnode);\n});\n\n/** Unsubscribe from Signals when unmounting components/vnodes */\nhook(OptionsTypes.UNMOUNT, (old, vnode: VNode) => {\n\tlet thing = vnode.__c || vnode;\n\tconst updater = updaterForComponent.get(thing);\n\tif (updater) {\n\t\tupdaterForComponent.delete(thing);\n\t\tconst signals = updater._deps;\n\t\tif (signals) {\n\t\t\tsignals.forEach(signal => signal._subs.delete(updater));\n\t\t\tsignals.clear();\n\t\t}\n\t}\n\told(vnode);\n});\n\n/** Mark components that use hook state so we can skip sCU optimization. */\nhook(OptionsTypes.HOOK, (old, component, index, type) => {\n\tif (type < 3) hasHookState.add(component);\n\told(component, index, type);\n});\n\n/**\n * Auto-memoize components that use Signals/Computeds.\n * Note: Does _not_ optimize components that use hook/class state.\n */\nComponent.prototype.shouldComponentUpdate = function (props, state) {\n\t// @todo: Once preactjs/preact#3671 lands, this could just use `currentUpdater`:\n\tconst updater = updaterForComponent.get(this);\n\n\tconst hasSignals = updater && updater._deps?.size !== 0;\n\n\t// let reason;\n\t// if (!hasSignals && !hasComputeds.has(this)) {\n\t// \treason = \"no signals or computeds\";\n\t// } else if (hasPendingUpdate.has(this)) {\n\t// \treason = \"has pending update\";\n\t// } else if (hasHookState.has(this)) {\n\t// \treason = \"has hook state\";\n\t// }\n\t// if (reason) {\n\t// \tif (!this) reason += \" (`this` bug)\";\n\t// \tconsole.log(\"not optimizing\", this?.constructor?.name, \": \", reason, {\n\t// \t\tdetails: {\n\t// \t\t\thasSignals,\n\t// \t\t\thasComputeds: hasComputeds.has(this),\n\t// \t\t\thasPendingUpdate: hasPendingUpdate.has(this),\n\t// \t\t\thasHookState: hasHookState.has(this),\n\t// \t\t\tdeps: Array.from(updater._deps),\n\t// \t\t\tupdater,\n\t// \t\t},\n\t// \t});\n\t// }\n\n\t// if this component used no signals or computeds, update:\n\tif (!hasSignals && !hasComputeds.has(this)) return true;\n\n\t// if there is a pending re-render triggered from Signals, update:\n\tif (hasPendingUpdate.has(this)) return true;\n\n\t// if there is hook or class state, update:\n\tif (hasHookState.has(this)) return true;\n\tfor (let i in state) return true;\n\n\t// if any non-Signal props changed, update:\n\tfor (let i in props) {\n\t\tif (i !== \"__source\" && props[i] !== this.props[i]) return true;\n\t}\n\tfor (let i in this.props) if (!(i in props)) return true;\n\n\t// this is a purely Signal-driven component, don't update:\n\treturn false;\n};\n\nexport function useSignal<T>(value: T) {\n\treturn useMemo(() => signal<T>(value), []);\n}\n\nexport function useComputed<T>(compute: () => T) {\n\tconst $compute = useRef(compute);\n\t$compute.current = compute;\n\thasComputeds.add(currentComponent!);\n\treturn useMemo(() => computed<T>(() => $compute.current()), []);\n}\n\n/**\n * @todo Determine which Reactive implementation we'll be using.\n * @internal\n */\n// export function useReactive<T extends object>(value: T): Reactive<T> {\n// \treturn useMemo(() => reactive<T>(value), []);\n// }\n\n/**\n * @internal\n * Update a Reactive's using the properties of an object or other Reactive.\n * Also works for Signals.\n * @example\n * // Update a Reactive with Object.assign()-like syntax:\n * const r = reactive({ name: \"Alice\" });\n * update(r, { name: \"Bob\" });\n * update(r, { age: 42 }); // property 'age' does not exist in type '{ name?: string }'\n * update(r, 2); // '2' has no properties in common with '{ name?: string }'\n * console.log(r.name.value); // \"Bob\"\n *\n * @example\n * // Update a Reactive with the properties of another Reactive:\n * const A = reactive({ name: \"Alice\" });\n * const B = reactive({ name: \"Bob\", age: 42 });\n * update(A, B);\n * console.log(`${A.name} is ${A.age}`); // \"Bob is 42\"\n *\n * @example\n * // Update a signal with assign()-like syntax:\n * const s = signal(42);\n * update(s, \"hi\"); // Argument type 'string' not assignable to type 'number'\n * update(s, {}); // Argument type '{}' not assignable to type 'number'\n * update(s, 43);\n * console.log(s.value); // 43\n *\n * @param obj The Reactive or Signal to be updated\n * @param update The value, Signal, object or Reactive to update `obj` to match\n * @param overwrite If `true`, any properties `obj` missing from `update` are set to `undefined`\n */\n/*\nexport function update<T extends SignalOrReactive>(\n\tobj: T,\n\tupdate: Partial<Unwrap<T>>,\n\toverwrite = false\n) {\n\tif (obj instanceof Signal) {\n\t\tobj.value = peekValue(update);\n\t} else {\n\t\tfor (let i in update) {\n\t\t\tif (i in obj) {\n\t\t\t\tobj[i].value = peekValue(update[i]);\n\t\t\t} else {\n\t\t\t\tlet sig = signal(peekValue(update[i]));\n\t\t\t\tsig[KEY] = i;\n\t\t\t\tobj[i] = sig;\n\t\t\t}\n\t\t}\n\t\tif (overwrite) {\n\t\t\tfor (let i in obj) {\n\t\t\t\tif (!(i in update)) {\n\t\t\t\t\tobj[i].value = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n*/\n"],"names":["hasPendingUpdate","WeakSet","hasComputeds","hook","hookName","hookFn","options","bind","currentComponent","currentUpdater","finishUpdate","updaterForComponent","WeakMap","setCurrentUpdater","updater","_setCurrent","createUpdater","s","signal","undefined","_canActivate","_updater","getElementUpdater","vnode","get","_props","length","signalProps","__e","i","_key","prop","_signal","value","_value","dom","setAttribute","removeAttribute","set","childToSignal","child","arr","Array","isArray","forEach","Signal","createElement","Text","data","currentSignal","useSignal","useMemo","v","this","__v","__","__c","add","base","computed","useComputed","compute","$compute","useRef","current","displayName","old","type","props","push","newUpdater","oldUpdater","peek","component","delete","setState","error","oldVNode","thing","signals","_deps","_subs","clear","index","hasHookState","Component","prototype","shouldComponentUpdate","state","_updater$_deps","size","has"],"mappings":"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"}
|
package/dist/signals.module.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
import{Component as n,options as r,createElement as t}from"preact";import{useMemo as i,useRef as o}from"preact/hooks";import{Signal as e,signal as f,computed as u}from"@preact/signals-core";export{Signal,batch,computed,effect,signal}from"@preact/signals-core";var a,c,v,s=new WeakSet,l=new WeakSet,p=new WeakSet;function _(n,t){r[n]=t.bind(null,r[n]||function(){})}var h=new WeakMap;function d(n){v&&v(!0,!0),c=n,v=n&&n._()}function m(n){var r=f(void 0);return r._c=!0,r._u=n,r}function k(n){var r=h.get(n);if(r)r.__.length=0;else{var t=[];(r=m(function(){for(var r=n.__e,i=0;i<t.length;i++){var o=t[i],e=o.t,f=o.i._v;if(!r)return;e in r?r[e]=f:f?r.setAttribute(e,f):r.removeAttribute(e)}})).__=t,h.set(n,r)}return r}function g(n,r,i){"object"!=typeof n||null==n||(Array.isArray(n)?n.forEach(g):n instanceof e&&(i[r]=t(b,{data:n})))}function b(n){var r=this,t=n.data,o=i(function(){for(var n=r.__v;n=n.__;)if(n.__c){p.add(n.__c);break}return c._u=function(){r.base.data=
|
|
1
|
+
import{Component as n,options as r,createElement as t}from"preact";import{useMemo as i,useRef as o}from"preact/hooks";import{Signal as e,signal as f,computed as u}from"@preact/signals-core";export{Signal,batch,computed,effect,signal}from"@preact/signals-core";var a,c,v,s=new WeakSet,l=new WeakSet,p=new WeakSet;function _(n,t){r[n]=t.bind(null,r[n]||function(){})}var h=new WeakMap;function d(n){v&&v(!0,!0),c=n,v=n&&n._()}function m(n){var r=f(void 0);return r._c=!0,r._u=n,r}function k(n){var r=h.get(n);if(r)r.__.length=0;else{var t=[];(r=m(function(){for(var r=n.__e,i=0;i<t.length;i++){var o=t[i],e=o.t,f=o.i._v;if(!r)return;e in r?r[e]=f:f?r.setAttribute(e,f):r.removeAttribute(e)}})).__=t,h.set(n,r)}return r}function g(n,r,i){"object"!=typeof n||null==n||(Array.isArray(n)?n.forEach(g):n instanceof e&&(i[r]=t(b,{data:n})))}function b(n){var r=this,t=n.data,o=w(t);o.value=t;var e=i(function(){for(var n=r.__v;n=n.__;)if(n.__c){p.add(n.__c);break}return c._u=function(){r.base.data=e._v},u(function(){var n=o.value.value;return 0===n?0:!0===n?"":n||""})},[]);return e.value}function w(n){return i(function(){return f(n)},[])}function y(n){var r=o(n);return r.current=n,p.add(a),i(function(){return u(function(){return r.current()})},[])}b.displayName="_st",_("__b",function(n,r){if("string"==typeof r.type){var t,i=r.props;for(var o in i){var f=i[o];"children"===o?g(f,"children",i):f instanceof e&&function(){t||(t=k(r)),t.__.push({t:o,i:f});var n=t._u;if(f._u){var e=f._u;f._u=function(){n(),e()}}else f._u=n;i[o]=f.peek()}()}d(t)}n(r)}),_("__r",function(n,r){var t,i=r.__c;i&&(s.delete(i),void 0===(t=h.get(i))&&(t=m(function(){s.add(i),i.setState({})}),h.set(i,t))),a=i,d(t),n(r)}),_("__e",function(n,r,t,i){d(),a=void 0,n(r,t,i)}),_("diffed",function(n,r){d(),a=void 0,n(r)}),_("unmount",function(n,r){var t=r.__c||r,i=h.get(t);if(i){h.delete(t);var o=i._d;o&&(o.forEach(function(n){return n._s.delete(i)}),o.clear())}n(r)}),_("__h",function(n,r,t,i){i<3&&l.add(r),n(r,t,i)}),n.prototype.shouldComponentUpdate=function(n,r){var t,i=h.get(this);if(!(i&&0!==(null==(t=i._d)?void 0:t.size)||p.has(this)))return!0;if(s.has(this))return!0;if(l.has(this))return!0;for(var o in r)return!0;for(var e in n)if("__source"!==e&&n[e]!==this.props[e])return!0;for(var f in this.props)if(!(f in n))return!0;return!1};export{y as useComputed,w as useSignal};
|
|
2
|
+
//# sourceMappingURL=signals.module.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signals.module.js","sources":["../src/index.ts"],"sourcesContent":["import { options, Component, createElement } from \"preact\";\nimport { useRef, useMemo } from \"preact/hooks\";\nimport {\n\tsignal,\n\tcomputed,\n\tbatch,\n\teffect,\n\tSignal,\n\ttype ReadonlySignal,\n} from \"@preact/signals-core\";\nimport {\n\tVNode,\n\tComponentType,\n\tOptionsTypes,\n\tHookFn,\n\tUpdater,\n\tElementUpdater,\n} from \"./internal\";\n\nexport { signal, computed, batch, effect, Signal, type ReadonlySignal };\n\n// Components that have a pending Signal update: (used to bypass default sCU:false)\nconst hasPendingUpdate = new WeakSet<Component>();\n\n// Components that have useState()/useReducer() hooks:\nconst hasHookState = new WeakSet<Component>();\n\n// Components that have useComputed():\nconst hasComputeds = new WeakSet<Component>();\n\n// Install a Preact options hook\nfunction hook<T extends OptionsTypes>(hookName: T, hookFn: HookFn<T>) {\n\t// @ts-ignore-next-line private options hooks usage\n\toptions[hookName] = hookFn.bind(null, options[hookName] || (() => {}));\n}\n\nlet currentComponent: Component | undefined;\nlet currentUpdater: Updater | undefined;\nlet finishUpdate: ReturnType<Updater[\"_setCurrent\"]> | undefined;\nconst updaterForComponent = new WeakMap<Component | VNode, Updater>();\n\nfunction setCurrentUpdater(updater?: Updater) {\n\t// end tracking for the current update:\n\tif (finishUpdate) finishUpdate(true, true);\n\t// start tracking the new update:\n\tcurrentUpdater = updater;\n\tfinishUpdate = updater && updater._setCurrent();\n}\n\nfunction createUpdater(updater: () => void) {\n\tconst s = signal(undefined) as Updater;\n\ts._canActivate = true;\n\ts._updater = updater;\n\treturn s;\n}\n\n// Get a (cached) Signal property updater for an element VNode\nfunction getElementUpdater(vnode: VNode) {\n\tlet updater = updaterForComponent.get(vnode) as ElementUpdater;\n\tif (!updater) {\n\t\tlet signalProps: Array<{ _key: string, _signal: Signal }> = [];\n\t\tupdater = createUpdater(() => {\n\t\t\tlet dom = vnode.__e as Element;\n\n\t\t\tfor (let i = 0; i < signalProps.length; i++) {\n\t\t\t\tlet { _key: prop, _signal: signal } = signalProps[i];\n\t\t\t\tlet value = signal._value;\n\t\t\t\tif (!dom) return;\n\t\t\t\tif (prop in dom) {\n\t\t\t\t\t// @ts-ignore-next-line silly\n\t\t\t\t\tdom[prop] = value;\n\t\t\t\t} else if (value) {\n\t\t\t\t\tdom.setAttribute(prop, value);\n\t\t\t\t} else {\n\t\t\t\t\tdom.removeAttribute(prop);\n\t\t\t\t}\n\t\t\t}\n\t\t}) as ElementUpdater;\n\t\tupdater._props = signalProps;\n\t\tupdaterForComponent.set(vnode, updater);\n\t} else {\n\t\tupdater._props.length = 0;\n\t}\n\treturn updater;\n}\n\n/** @todo This may be needed for complex prop value detection. */\n// function isSignalValue(value: any): value is Signal {\n// \tif (typeof value !== \"object\" || value == null) return false;\n// \tif (value instanceof Signal) return true;\n// \t// @TODO: uncomment this when we land Reactive (ideally behind a brand check)\n// \t// for (let i in value) if (value[i] instanceof Signal) return true;\n// \treturn false;\n// }\n\n/** Convert Signals within (nested) props.children into Text components */\nfunction childToSignal<T>(child: any, i: keyof T, arr: T) {\n\tif (typeof child !== \"object\" || child == null) {\n\t\t// can't be a signal\n\t} else if (Array.isArray(child)) {\n\t\tchild.forEach(childToSignal);\n\t} else if (child instanceof Signal) {\n\t\t// @ts-ignore-next-line yes, arr can accept VNodes:\n\t\tarr[i] = createElement(Text, { data: child });\n\t}\n}\n\n/**\n * A wrapper component that renders a Signal directly as a Text node.\n * @todo: in Preact 11, just decorate Signal with `type:null`\n */\nfunction Text(this: ComponentType, { data }: { data: Signal }) {\n\t// hasComputeds.add(this);\n\n\tconst s = useMemo(() => {\n\t\t// mark the parent component as having computeds so it gets optimized\n\t\tlet v = this.__v;\n\t\twhile ((v = v.__!)) {\n\t\t\tif (v.__c) {\n\t\t\t\thasComputeds.add(v.__c);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Replace this component's vdom updater with a direct text one:\n\t\tcurrentUpdater!._updater = () => {\n\t\t\t(this.base as Text).data = s._value;\n\t\t};\n\n\t\treturn computed(() => {\n\t\t\tlet s = data.value;\n\t\t\treturn s === 0 ? 0 : s === true ? \"\" : s || \"\";\n\t\t});\n\t}, []);\n\n\treturn s.value;\n}\nText.displayName = \"_st\";\n\n/** Inject low-level property/attribute bindings for Signals into Preact's diff */\nhook(OptionsTypes.DIFF, (old, vnode) => {\n\tif (typeof vnode.type === \"string\") {\n\t\t// let orig = vnode.__o || vnode;\n\t\tlet props = vnode.props;\n\t\tlet updater;\n\n\t\tfor (let i in props) {\n\t\t\tlet value = props[i];\n\t\t\tif (i === \"children\") {\n\t\t\t\tchildToSignal(value, \"children\", props);\n\t\t\t} else if (value instanceof Signal) {\n\t\t\t\t// first Signal prop triggers creation/cleanup of the updater:\n\t\t\t\tif (!updater) updater = getElementUpdater(vnode);\n\t\t\t\t// track which props are Signals for precise updates:\n\t\t\t\tupdater._props.push({ _key: i, _signal: value });\n\t\t\t\tlet newUpdater = updater._updater\n\t\t\t\tif (value._updater) {\n\t\t\t\t\tlet oldUpdater = value._updater\n\t\t\t\t\tvalue._updater = () => {\n\t\t\t\t\t\tnewUpdater();\n\t\t\t\t\t\toldUpdater();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tvalue._updater = newUpdater\n\t\t\t\t}\n\t\t\t\tprops[i] = value.peek()\n\t\t\t}\n\t\t}\n\n\t\tsetCurrentUpdater(updater);\n\t}\n\n\told(vnode);\n});\n\n/** Set up Updater before rendering a component */\nhook(OptionsTypes.RENDER, (old, vnode) => {\n\tlet updater;\n\n\tlet component = vnode.__c;\n\tif (component) {\n\t\thasPendingUpdate.delete(component);\n\n\t\tupdater = updaterForComponent.get(component);\n\t\tif (updater === undefined) {\n\t\t\tupdater = createUpdater(() => {\n\t\t\t\thasPendingUpdate.add(component);\n\t\t\t\tcomponent.setState({});\n\t\t\t});\n\t\t\tupdaterForComponent.set(component, updater);\n\t\t}\n\t}\n\n\tcurrentComponent = component;\n\tsetCurrentUpdater(updater);\n\told(vnode);\n});\n\n/** Finish current updater if a component errors */\nhook(OptionsTypes.CATCH_ERROR, (old, error, vnode, oldVNode) => {\n\tsetCurrentUpdater();\n\tcurrentComponent = undefined;\n\told(error, vnode, oldVNode);\n});\n\n/** Finish current updater after rendering any VNode */\nhook(OptionsTypes.DIFFED, (old, vnode) => {\n\tsetCurrentUpdater();\n\tcurrentComponent = undefined;\n\told(vnode);\n});\n\n/** Unsubscribe from Signals when unmounting components/vnodes */\nhook(OptionsTypes.UNMOUNT, (old, vnode: VNode) => {\n\tlet thing = vnode.__c || vnode;\n\tconst updater = updaterForComponent.get(thing);\n\tif (updater) {\n\t\tupdaterForComponent.delete(thing);\n\t\tconst signals = updater._deps;\n\t\tif (signals) {\n\t\t\tsignals.forEach(signal => signal._subs.delete(updater));\n\t\t\tsignals.clear();\n\t\t}\n\t}\n\told(vnode);\n});\n\n/** Mark components that use hook state so we can skip sCU optimization. */\nhook(OptionsTypes.HOOK, (old, component, index, type) => {\n\tif (type < 3) hasHookState.add(component);\n\told(component, index, type);\n});\n\n/**\n * Auto-memoize components that use Signals/Computeds.\n * Note: Does _not_ optimize components that use hook/class state.\n */\nComponent.prototype.shouldComponentUpdate = function (props, state) {\n\t// @todo: Once preactjs/preact#3671 lands, this could just use `currentUpdater`:\n\tconst updater = updaterForComponent.get(this);\n\n\tconst hasSignals = updater && updater._deps?.size !== 0;\n\n\t// let reason;\n\t// if (!hasSignals && !hasComputeds.has(this)) {\n\t// \treason = \"no signals or computeds\";\n\t// } else if (hasPendingUpdate.has(this)) {\n\t// \treason = \"has pending update\";\n\t// } else if (hasHookState.has(this)) {\n\t// \treason = \"has hook state\";\n\t// }\n\t// if (reason) {\n\t// \tif (!this) reason += \" (`this` bug)\";\n\t// \tconsole.log(\"not optimizing\", this?.constructor?.name, \": \", reason, {\n\t// \t\tdetails: {\n\t// \t\t\thasSignals,\n\t// \t\t\thasComputeds: hasComputeds.has(this),\n\t// \t\t\thasPendingUpdate: hasPendingUpdate.has(this),\n\t// \t\t\thasHookState: hasHookState.has(this),\n\t// \t\t\tdeps: Array.from(updater._deps),\n\t// \t\t\tupdater,\n\t// \t\t},\n\t// \t});\n\t// }\n\n\t// if this component used no signals or computeds, update:\n\tif (!hasSignals && !hasComputeds.has(this)) return true;\n\n\t// if there is a pending re-render triggered from Signals, update:\n\tif (hasPendingUpdate.has(this)) return true;\n\n\t// if there is hook or class state, update:\n\tif (hasHookState.has(this)) return true;\n\tfor (let i in state) return true;\n\n\t// if any non-Signal props changed, update:\n\tfor (let i in props) {\n\t\tif (i !== \"__source\" && props[i] !== this.props[i]) return true;\n\t}\n\tfor (let i in this.props) if (!(i in props)) return true;\n\n\t// this is a purely Signal-driven component, don't update:\n\treturn false;\n};\n\nexport function useSignal<T>(value: T) {\n\treturn useMemo(() => signal<T>(value), []);\n}\n\nexport function useComputed<T>(compute: () => T) {\n\tconst $compute = useRef(compute);\n\t$compute.current = compute;\n\thasComputeds.add(currentComponent!);\n\treturn useMemo(() => computed<T>(() => $compute.current()), []);\n}\n\n/**\n * @todo Determine which Reactive implementation we'll be using.\n * @internal\n */\n// export function useReactive<T extends object>(value: T): Reactive<T> {\n// \treturn useMemo(() => reactive<T>(value), []);\n// }\n\n/**\n * @internal\n * Update a Reactive's using the properties of an object or other Reactive.\n * Also works for Signals.\n * @example\n * // Update a Reactive with Object.assign()-like syntax:\n * const r = reactive({ name: \"Alice\" });\n * update(r, { name: \"Bob\" });\n * update(r, { age: 42 }); // property 'age' does not exist in type '{ name?: string }'\n * update(r, 2); // '2' has no properties in common with '{ name?: string }'\n * console.log(r.name.value); // \"Bob\"\n *\n * @example\n * // Update a Reactive with the properties of another Reactive:\n * const A = reactive({ name: \"Alice\" });\n * const B = reactive({ name: \"Bob\", age: 42 });\n * update(A, B);\n * console.log(`${A.name} is ${A.age}`); // \"Bob is 42\"\n *\n * @example\n * // Update a signal with assign()-like syntax:\n * const s = signal(42);\n * update(s, \"hi\"); // Argument type 'string' not assignable to type 'number'\n * update(s, {}); // Argument type '{}' not assignable to type 'number'\n * update(s, 43);\n * console.log(s.value); // 43\n *\n * @param obj The Reactive or Signal to be updated\n * @param update The value, Signal, object or Reactive to update `obj` to match\n * @param overwrite If `true`, any properties `obj` missing from `update` are set to `undefined`\n */\n/*\nexport function update<T extends SignalOrReactive>(\n\tobj: T,\n\tupdate: Partial<Unwrap<T>>,\n\toverwrite = false\n) {\n\tif (obj instanceof Signal) {\n\t\tobj.value = peekValue(update);\n\t} else {\n\t\tfor (let i in update) {\n\t\t\tif (i in obj) {\n\t\t\t\tobj[i].value = peekValue(update[i]);\n\t\t\t} else {\n\t\t\t\tlet sig = signal(peekValue(update[i]));\n\t\t\t\tsig[KEY] = i;\n\t\t\t\tobj[i] = sig;\n\t\t\t}\n\t\t}\n\t\tif (overwrite) {\n\t\t\tfor (let i in obj) {\n\t\t\t\tif (!(i in update)) {\n\t\t\t\t\tobj[i].value = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n*/\n"],"names":["Component","options","createElement","useMemo","useRef","Signal","signal","computed","batch","effect","currentComponent","currentUpdater","finishUpdate","hasPendingUpdate","WeakSet","hasComputeds","hook","hookName","hookFn","bind","updaterForComponent","WeakMap","setCurrentUpdater","updater","_setCurrent","s","undefined","_canActivate","_updater","getElementUpdater","vnode","get","_props","length","signalProps","createUpdater","dom","__e","i","prop","_key","value","_signal","_value","setAttribute","removeAttribute","set","childToSignal","child","arr","Array","isArray","forEach","Text","data","_ref","_this","this","__v","v","__","__c","add","base","useSignal","useComputed","compute","$compute","current","displayName","old","type","props","push","oldUpdater","newUpdater","peek","component","setState","error","oldVNode","thing","signals","_deps","_subs","clear","index","hasHookState","prototype","shouldComponentUpdate","state","_updater$_deps","size","has","_i","_i2"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"signals.module.js","sources":["../src/index.ts"],"sourcesContent":["import { options, Component, createElement } from \"preact\";\nimport { useRef, useMemo } from \"preact/hooks\";\nimport {\n\tsignal,\n\tcomputed,\n\tbatch,\n\teffect,\n\tSignal,\n\ttype ReadonlySignal,\n} from \"@preact/signals-core\";\nimport {\n\tVNode,\n\tComponentType,\n\tOptionsTypes,\n\tHookFn,\n\tUpdater,\n\tElementUpdater,\n} from \"./internal\";\n\nexport { signal, computed, batch, effect, Signal, type ReadonlySignal };\n\n// Components that have a pending Signal update: (used to bypass default sCU:false)\nconst hasPendingUpdate = new WeakSet<Component>();\n\n// Components that have useState()/useReducer() hooks:\nconst hasHookState = new WeakSet<Component>();\n\n// Components that have useComputed():\nconst hasComputeds = new WeakSet<Component>();\n\n// Install a Preact options hook\nfunction hook<T extends OptionsTypes>(hookName: T, hookFn: HookFn<T>) {\n\t// @ts-ignore-next-line private options hooks usage\n\toptions[hookName] = hookFn.bind(null, options[hookName] || (() => {}));\n}\n\nlet currentComponent: Component | undefined;\nlet currentUpdater: Updater | undefined;\nlet finishUpdate: ReturnType<Updater[\"_setCurrent\"]> | undefined;\nconst updaterForComponent = new WeakMap<Component | VNode, Updater>();\n\nfunction setCurrentUpdater(updater?: Updater) {\n\t// end tracking for the current update:\n\tif (finishUpdate) finishUpdate(true, true);\n\t// start tracking the new update:\n\tcurrentUpdater = updater;\n\tfinishUpdate = updater && updater._setCurrent();\n}\n\nfunction createUpdater(updater: () => void) {\n\tconst s = signal(undefined) as Updater;\n\ts._canActivate = true;\n\ts._updater = updater;\n\treturn s;\n}\n\n// Get a (cached) Signal property updater for an element VNode\nfunction getElementUpdater(vnode: VNode) {\n\tlet updater = updaterForComponent.get(vnode) as ElementUpdater;\n\tif (!updater) {\n\t\tlet signalProps: Array<{ _key: string; _signal: Signal }> = [];\n\t\tupdater = createUpdater(() => {\n\t\t\tlet dom = vnode.__e as Element;\n\n\t\t\tfor (let i = 0; i < signalProps.length; i++) {\n\t\t\t\tlet { _key: prop, _signal: signal } = signalProps[i];\n\t\t\t\tlet value = signal._value;\n\t\t\t\tif (!dom) return;\n\t\t\t\tif (prop in dom) {\n\t\t\t\t\t// @ts-ignore-next-line silly\n\t\t\t\t\tdom[prop] = value;\n\t\t\t\t} else if (value) {\n\t\t\t\t\tdom.setAttribute(prop, value);\n\t\t\t\t} else {\n\t\t\t\t\tdom.removeAttribute(prop);\n\t\t\t\t}\n\t\t\t}\n\t\t}) as ElementUpdater;\n\t\tupdater._props = signalProps;\n\t\tupdaterForComponent.set(vnode, updater);\n\t} else {\n\t\tupdater._props.length = 0;\n\t}\n\treturn updater;\n}\n\n/** @todo This may be needed for complex prop value detection. */\n// function isSignalValue(value: any): value is Signal {\n// \tif (typeof value !== \"object\" || value == null) return false;\n// \tif (value instanceof Signal) return true;\n// \t// @TODO: uncomment this when we land Reactive (ideally behind a brand check)\n// \t// for (let i in value) if (value[i] instanceof Signal) return true;\n// \treturn false;\n// }\n\n/** Convert Signals within (nested) props.children into Text components */\nfunction childToSignal<T>(child: any, i: keyof T, arr: T) {\n\tif (typeof child !== \"object\" || child == null) {\n\t\t// can't be a signal\n\t} else if (Array.isArray(child)) {\n\t\tchild.forEach(childToSignal);\n\t} else if (child instanceof Signal) {\n\t\t// @ts-ignore-next-line yes, arr can accept VNodes:\n\t\tarr[i] = createElement(Text, { data: child });\n\t}\n}\n\n/**\n * A wrapper component that renders a Signal directly as a Text node.\n * @todo: in Preact 11, just decorate Signal with `type:null`\n */\nfunction Text(this: ComponentType, { data }: { data: Signal }) {\n\t// hasComputeds.add(this);\n\n\t// Store the props.data signal in another signal so that\n\t// passing a new signal reference re-runs the text computed:\n\tconst currentSignal = useSignal(data);\n\tcurrentSignal.value = data;\n\n\tconst s = useMemo(() => {\n\t\t// mark the parent component as having computeds so it gets optimized\n\t\tlet v = this.__v;\n\t\twhile ((v = v.__!)) {\n\t\t\tif (v.__c) {\n\t\t\t\thasComputeds.add(v.__c);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Replace this component's vdom updater with a direct text one:\n\t\tcurrentUpdater!._updater = () => {\n\t\t\t(this.base as Text).data = s._value;\n\t\t};\n\n\t\treturn computed(() => {\n\t\t\tlet data = currentSignal.value;\n\t\t\tlet s = data.value;\n\t\t\treturn s === 0 ? 0 : s === true ? \"\" : s || \"\";\n\t\t});\n\t}, []);\n\n\treturn s.value;\n}\nText.displayName = \"_st\";\n\n/** Inject low-level property/attribute bindings for Signals into Preact's diff */\nhook(OptionsTypes.DIFF, (old, vnode) => {\n\tif (typeof vnode.type === \"string\") {\n\t\t// let orig = vnode.__o || vnode;\n\t\tlet props = vnode.props;\n\t\tlet updater;\n\n\t\tfor (let i in props) {\n\t\t\tlet value = props[i];\n\t\t\tif (i === \"children\") {\n\t\t\t\tchildToSignal(value, \"children\", props);\n\t\t\t} else if (value instanceof Signal) {\n\t\t\t\t// first Signal prop triggers creation/cleanup of the updater:\n\t\t\t\tif (!updater) updater = getElementUpdater(vnode);\n\t\t\t\t// track which props are Signals for precise updates:\n\t\t\t\tupdater._props.push({ _key: i, _signal: value });\n\t\t\t\tlet newUpdater = updater._updater;\n\t\t\t\tif (value._updater) {\n\t\t\t\t\tlet oldUpdater = value._updater;\n\t\t\t\t\tvalue._updater = () => {\n\t\t\t\t\t\tnewUpdater();\n\t\t\t\t\t\toldUpdater();\n\t\t\t\t\t};\n\t\t\t\t} else {\n\t\t\t\t\tvalue._updater = newUpdater;\n\t\t\t\t}\n\t\t\t\tprops[i] = value.peek();\n\t\t\t}\n\t\t}\n\n\t\tsetCurrentUpdater(updater);\n\t}\n\n\told(vnode);\n});\n\n/** Set up Updater before rendering a component */\nhook(OptionsTypes.RENDER, (old, vnode) => {\n\tlet updater;\n\n\tlet component = vnode.__c;\n\tif (component) {\n\t\thasPendingUpdate.delete(component);\n\n\t\tupdater = updaterForComponent.get(component);\n\t\tif (updater === undefined) {\n\t\t\tupdater = createUpdater(() => {\n\t\t\t\thasPendingUpdate.add(component);\n\t\t\t\tcomponent.setState({});\n\t\t\t});\n\t\t\tupdaterForComponent.set(component, updater);\n\t\t}\n\t}\n\n\tcurrentComponent = component;\n\tsetCurrentUpdater(updater);\n\told(vnode);\n});\n\n/** Finish current updater if a component errors */\nhook(OptionsTypes.CATCH_ERROR, (old, error, vnode, oldVNode) => {\n\tsetCurrentUpdater();\n\tcurrentComponent = undefined;\n\told(error, vnode, oldVNode);\n});\n\n/** Finish current updater after rendering any VNode */\nhook(OptionsTypes.DIFFED, (old, vnode) => {\n\tsetCurrentUpdater();\n\tcurrentComponent = undefined;\n\told(vnode);\n});\n\n/** Unsubscribe from Signals when unmounting components/vnodes */\nhook(OptionsTypes.UNMOUNT, (old, vnode: VNode) => {\n\tlet thing = vnode.__c || vnode;\n\tconst updater = updaterForComponent.get(thing);\n\tif (updater) {\n\t\tupdaterForComponent.delete(thing);\n\t\tconst signals = updater._deps;\n\t\tif (signals) {\n\t\t\tsignals.forEach(signal => signal._subs.delete(updater));\n\t\t\tsignals.clear();\n\t\t}\n\t}\n\told(vnode);\n});\n\n/** Mark components that use hook state so we can skip sCU optimization. */\nhook(OptionsTypes.HOOK, (old, component, index, type) => {\n\tif (type < 3) hasHookState.add(component);\n\told(component, index, type);\n});\n\n/**\n * Auto-memoize components that use Signals/Computeds.\n * Note: Does _not_ optimize components that use hook/class state.\n */\nComponent.prototype.shouldComponentUpdate = function (props, state) {\n\t// @todo: Once preactjs/preact#3671 lands, this could just use `currentUpdater`:\n\tconst updater = updaterForComponent.get(this);\n\n\tconst hasSignals = updater && updater._deps?.size !== 0;\n\n\t// let reason;\n\t// if (!hasSignals && !hasComputeds.has(this)) {\n\t// \treason = \"no signals or computeds\";\n\t// } else if (hasPendingUpdate.has(this)) {\n\t// \treason = \"has pending update\";\n\t// } else if (hasHookState.has(this)) {\n\t// \treason = \"has hook state\";\n\t// }\n\t// if (reason) {\n\t// \tif (!this) reason += \" (`this` bug)\";\n\t// \tconsole.log(\"not optimizing\", this?.constructor?.name, \": \", reason, {\n\t// \t\tdetails: {\n\t// \t\t\thasSignals,\n\t// \t\t\thasComputeds: hasComputeds.has(this),\n\t// \t\t\thasPendingUpdate: hasPendingUpdate.has(this),\n\t// \t\t\thasHookState: hasHookState.has(this),\n\t// \t\t\tdeps: Array.from(updater._deps),\n\t// \t\t\tupdater,\n\t// \t\t},\n\t// \t});\n\t// }\n\n\t// if this component used no signals or computeds, update:\n\tif (!hasSignals && !hasComputeds.has(this)) return true;\n\n\t// if there is a pending re-render triggered from Signals, update:\n\tif (hasPendingUpdate.has(this)) return true;\n\n\t// if there is hook or class state, update:\n\tif (hasHookState.has(this)) return true;\n\tfor (let i in state) return true;\n\n\t// if any non-Signal props changed, update:\n\tfor (let i in props) {\n\t\tif (i !== \"__source\" && props[i] !== this.props[i]) return true;\n\t}\n\tfor (let i in this.props) if (!(i in props)) return true;\n\n\t// this is a purely Signal-driven component, don't update:\n\treturn false;\n};\n\nexport function useSignal<T>(value: T) {\n\treturn useMemo(() => signal<T>(value), []);\n}\n\nexport function useComputed<T>(compute: () => T) {\n\tconst $compute = useRef(compute);\n\t$compute.current = compute;\n\thasComputeds.add(currentComponent!);\n\treturn useMemo(() => computed<T>(() => $compute.current()), []);\n}\n\n/**\n * @todo Determine which Reactive implementation we'll be using.\n * @internal\n */\n// export function useReactive<T extends object>(value: T): Reactive<T> {\n// \treturn useMemo(() => reactive<T>(value), []);\n// }\n\n/**\n * @internal\n * Update a Reactive's using the properties of an object or other Reactive.\n * Also works for Signals.\n * @example\n * // Update a Reactive with Object.assign()-like syntax:\n * const r = reactive({ name: \"Alice\" });\n * update(r, { name: \"Bob\" });\n * update(r, { age: 42 }); // property 'age' does not exist in type '{ name?: string }'\n * update(r, 2); // '2' has no properties in common with '{ name?: string }'\n * console.log(r.name.value); // \"Bob\"\n *\n * @example\n * // Update a Reactive with the properties of another Reactive:\n * const A = reactive({ name: \"Alice\" });\n * const B = reactive({ name: \"Bob\", age: 42 });\n * update(A, B);\n * console.log(`${A.name} is ${A.age}`); // \"Bob is 42\"\n *\n * @example\n * // Update a signal with assign()-like syntax:\n * const s = signal(42);\n * update(s, \"hi\"); // Argument type 'string' not assignable to type 'number'\n * update(s, {}); // Argument type '{}' not assignable to type 'number'\n * update(s, 43);\n * console.log(s.value); // 43\n *\n * @param obj The Reactive or Signal to be updated\n * @param update The value, Signal, object or Reactive to update `obj` to match\n * @param overwrite If `true`, any properties `obj` missing from `update` are set to `undefined`\n */\n/*\nexport function update<T extends SignalOrReactive>(\n\tobj: T,\n\tupdate: Partial<Unwrap<T>>,\n\toverwrite = false\n) {\n\tif (obj instanceof Signal) {\n\t\tobj.value = peekValue(update);\n\t} else {\n\t\tfor (let i in update) {\n\t\t\tif (i in obj) {\n\t\t\t\tobj[i].value = peekValue(update[i]);\n\t\t\t} else {\n\t\t\t\tlet sig = signal(peekValue(update[i]));\n\t\t\t\tsig[KEY] = i;\n\t\t\t\tobj[i] = sig;\n\t\t\t}\n\t\t}\n\t\tif (overwrite) {\n\t\t\tfor (let i in obj) {\n\t\t\t\tif (!(i in update)) {\n\t\t\t\t\tobj[i].value = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n*/\n"],"names":["currentComponent","finishUpdate","hasPendingUpdate","WeakSet","hasComputeds","hook","hookName","hookFn","options","bind","updaterForComponent","setCurrentUpdater","updater","currentUpdater","_setCurrent","createUpdater","s","signal","undefined","_canActivate","_updater","getElementUpdater","vnode","get","_props","length","signalProps","dom","__e","i","prop","_key","_signal","_value","value","setAttribute","removeAttribute","set","childToSignal","child","arr","Array","isArray","forEach","Signal","createElement","Text","data","_ref","_this","this","currentSignal","useSignal","useMemo","v","__v","__","__c","add","base","computed","useComputed","compute","$compute","useRef","current","displayName","old","type","props","push","newUpdater","oldUpdater","peek","component","setState","error","oldVNode","thing","signals","_deps","_subs","clear","index","hasHookState","Component","prototype","shouldComponentUpdate","state","_updater$_deps","size","has","_i","_i2"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@preact/signals",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "",
|
|
6
6
|
"keywords": [],
|
|
7
7
|
"authors": [
|
|
8
8
|
"The Preact Authors (https://github.com/preactjs/signals/contributors)"
|
|
9
9
|
],
|
|
10
|
-
"repository":
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/preactjs/signals"
|
|
13
|
+
},
|
|
11
14
|
"bugs": "https://github.com/preactjs/signals/issues",
|
|
12
15
|
"homepage": "https://preactjs.com",
|
|
13
16
|
"funding": {
|
package/src/index.ts
CHANGED
|
@@ -58,7 +58,7 @@ function createUpdater(updater: () => void) {
|
|
|
58
58
|
function getElementUpdater(vnode: VNode) {
|
|
59
59
|
let updater = updaterForComponent.get(vnode) as ElementUpdater;
|
|
60
60
|
if (!updater) {
|
|
61
|
-
let signalProps: Array<{ _key: string
|
|
61
|
+
let signalProps: Array<{ _key: string; _signal: Signal }> = [];
|
|
62
62
|
updater = createUpdater(() => {
|
|
63
63
|
let dom = vnode.__e as Element;
|
|
64
64
|
|
|
@@ -112,6 +112,11 @@ function childToSignal<T>(child: any, i: keyof T, arr: T) {
|
|
|
112
112
|
function Text(this: ComponentType, { data }: { data: Signal }) {
|
|
113
113
|
// hasComputeds.add(this);
|
|
114
114
|
|
|
115
|
+
// Store the props.data signal in another signal so that
|
|
116
|
+
// passing a new signal reference re-runs the text computed:
|
|
117
|
+
const currentSignal = useSignal(data);
|
|
118
|
+
currentSignal.value = data;
|
|
119
|
+
|
|
115
120
|
const s = useMemo(() => {
|
|
116
121
|
// mark the parent component as having computeds so it gets optimized
|
|
117
122
|
let v = this.__v;
|
|
@@ -128,6 +133,7 @@ function Text(this: ComponentType, { data }: { data: Signal }) {
|
|
|
128
133
|
};
|
|
129
134
|
|
|
130
135
|
return computed(() => {
|
|
136
|
+
let data = currentSignal.value;
|
|
131
137
|
let s = data.value;
|
|
132
138
|
return s === 0 ? 0 : s === true ? "" : s || "";
|
|
133
139
|
});
|
|
@@ -153,17 +159,17 @@ hook(OptionsTypes.DIFF, (old, vnode) => {
|
|
|
153
159
|
if (!updater) updater = getElementUpdater(vnode);
|
|
154
160
|
// track which props are Signals for precise updates:
|
|
155
161
|
updater._props.push({ _key: i, _signal: value });
|
|
156
|
-
let newUpdater = updater._updater
|
|
162
|
+
let newUpdater = updater._updater;
|
|
157
163
|
if (value._updater) {
|
|
158
|
-
let oldUpdater = value._updater
|
|
164
|
+
let oldUpdater = value._updater;
|
|
159
165
|
value._updater = () => {
|
|
160
166
|
newUpdater();
|
|
161
167
|
oldUpdater();
|
|
162
|
-
}
|
|
168
|
+
};
|
|
163
169
|
} else {
|
|
164
|
-
value._updater = newUpdater
|
|
170
|
+
value._updater = newUpdater;
|
|
165
171
|
}
|
|
166
|
-
props[i] = value.peek()
|
|
172
|
+
props[i] = value.peek();
|
|
167
173
|
}
|
|
168
174
|
}
|
|
169
175
|
|
package/test/index.test.ts
CHANGED
|
@@ -50,12 +50,15 @@ describe("@preact/signals", () => {
|
|
|
50
50
|
expect(text).to.have.property("data", "changed");
|
|
51
51
|
});
|
|
52
52
|
|
|
53
|
-
it("should update Signal-based Text (in a parent component)", () => {
|
|
53
|
+
it("should update Signal-based Text (in a parent component)", async () => {
|
|
54
54
|
const sig = signal("test");
|
|
55
|
+
const spy = sinon.spy();
|
|
55
56
|
function App({ x }: { x: typeof sig }) {
|
|
57
|
+
spy();
|
|
56
58
|
return h("span", null, x);
|
|
57
59
|
}
|
|
58
60
|
render(h(App, { x: sig }), scratch);
|
|
61
|
+
spy.resetHistory();
|
|
59
62
|
|
|
60
63
|
const text = scratch.firstChild!.firstChild!;
|
|
61
64
|
expect(text).to.have.property("data", "test");
|
|
@@ -66,6 +69,51 @@ describe("@preact/signals", () => {
|
|
|
66
69
|
expect(scratch.firstChild!.firstChild!).to.equal(text);
|
|
67
70
|
// should update the text in-place
|
|
68
71
|
expect(text).to.have.property("data", "changed");
|
|
72
|
+
|
|
73
|
+
await sleep();
|
|
74
|
+
expect(spy).not.to.have.been.called;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("should support swapping Signals in Text positions", async () => {
|
|
78
|
+
const sig = signal("test");
|
|
79
|
+
const spy = sinon.spy();
|
|
80
|
+
function App({ x }: { x: typeof sig }) {
|
|
81
|
+
spy();
|
|
82
|
+
return h("span", null, x);
|
|
83
|
+
}
|
|
84
|
+
render(h(App, { x: sig }), scratch);
|
|
85
|
+
spy.resetHistory();
|
|
86
|
+
|
|
87
|
+
const text = scratch.firstChild!.firstChild!;
|
|
88
|
+
expect(text).to.have.property("data", "test");
|
|
89
|
+
|
|
90
|
+
const sig2 = signal("different");
|
|
91
|
+
render(h(App, { x: sig2 }), scratch);
|
|
92
|
+
expect(spy).to.have.been.called;
|
|
93
|
+
spy.resetHistory();
|
|
94
|
+
|
|
95
|
+
// should not remount/replace Text
|
|
96
|
+
expect(scratch.firstChild!.firstChild!).to.equal(text);
|
|
97
|
+
// should update the text in-place
|
|
98
|
+
expect(text).to.have.property("data", "different");
|
|
99
|
+
|
|
100
|
+
await sleep();
|
|
101
|
+
expect(spy).not.to.have.been.called;
|
|
102
|
+
|
|
103
|
+
sig.value = "changed old signal";
|
|
104
|
+
|
|
105
|
+
await sleep();
|
|
106
|
+
expect(spy).not.to.have.been.called;
|
|
107
|
+
// the text should _not_ have changed:
|
|
108
|
+
expect(text).to.have.property("data", "different");
|
|
109
|
+
|
|
110
|
+
sig2.value = "changed";
|
|
111
|
+
|
|
112
|
+
expect(scratch.firstChild!.firstChild!).to.equal(text);
|
|
113
|
+
expect(text).to.have.property("data", "changed");
|
|
114
|
+
|
|
115
|
+
await sleep();
|
|
116
|
+
expect(spy).not.to.have.been.called;
|
|
69
117
|
});
|
|
70
118
|
});
|
|
71
119
|
|
|
@@ -126,27 +174,6 @@ describe("@preact/signals", () => {
|
|
|
126
174
|
rerender();
|
|
127
175
|
expect(spy).to.be.calledOnce;
|
|
128
176
|
});
|
|
129
|
-
|
|
130
|
-
it("should update memo'ed component via signals", async () => {
|
|
131
|
-
const sig = signal("foo");
|
|
132
|
-
|
|
133
|
-
function Inner() {
|
|
134
|
-
const value = sig.value;
|
|
135
|
-
return h("p", null, value);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function App() {
|
|
139
|
-
sig.value;
|
|
140
|
-
return useMemo(() => h(Inner, { foo: 1 }), []);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
render(h(App, {}), scratch);
|
|
144
|
-
expect(scratch.textContent).to.equal("foo");
|
|
145
|
-
|
|
146
|
-
sig.value = "bar";
|
|
147
|
-
rerender();
|
|
148
|
-
expect(scratch.textContent).to.equal("bar");
|
|
149
|
-
});
|
|
150
177
|
});
|
|
151
178
|
|
|
152
179
|
describe("prop bindings", () => {
|