@preact/signals 1.0.3 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/dist/signals.d.ts +1 -0
- package/dist/signals.js +1 -2
- package/dist/signals.js.map +1 -1
- package/dist/signals.min.js +1 -2
- package/dist/signals.min.js.map +1 -1
- package/dist/signals.mjs +1 -2
- package/dist/signals.mjs.map +1 -1
- package/dist/signals.module.js +1 -2
- package/dist/signals.module.js.map +1 -1
- package/package.json +6 -5
- package/src/index.ts +164 -116
- package/src/internal.d.ts +25 -12
- package/test/{exports.test.ts → exports.test.tsx} +0 -0
- package/test/{index.test.ts → index.test.tsx} +104 -24
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# @preact/signals
|
|
2
2
|
|
|
3
|
+
## 1.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#91](https://github.com/preactjs/signals/pull/91) [`fb74bb9`](https://github.com/preactjs/signals/commit/fb74bb9ce4e44192e1ee7d3d041274cc985db767) Thanks [@JoviDeCroock](https://github.com/JoviDeCroock)! - add the `useSignalEffect` hook
|
|
8
|
+
|
|
9
|
+
* [#183](https://github.com/preactjs/signals/pull/183) [`79ff1e7`](https://github.com/preactjs/signals/commit/79ff1e794dde9952db2d6d43b22cebfb2accc770) Thanks [@jviide](https://github.com/jviide)! - Add ability to run custom cleanup logic when an effect is disposed.
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
effect(() => {
|
|
13
|
+
console.log("This runs whenever a dependency changes");
|
|
14
|
+
return () => {
|
|
15
|
+
console.log("This runs when the effect is disposed");
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- [#186](https://github.com/preactjs/signals/pull/186) [`7242bd6`](https://github.com/preactjs/signals/commit/7242bd68cc570c6159600f271ee95977d3970d0f) Thanks [@marvinhagemeister](https://github.com/marvinhagemeister)! - Fix unable to set SVG attribute via Signal
|
|
23
|
+
|
|
24
|
+
* [#161](https://github.com/preactjs/signals/pull/161) [`6ac6923`](https://github.com/preactjs/signals/commit/6ac6923e5294f8a31ee1a009550b9891c3996cb4) Thanks [@jviide](https://github.com/jviide)! - Remove all usages of `Set`, `Map` and other allocation heavy objects in signals-core. This substaintially increases performance across all measurements.
|
|
25
|
+
|
|
26
|
+
- [#171](https://github.com/preactjs/signals/pull/171) [`fcbb3f4`](https://github.com/preactjs/signals/commit/fcbb3f4b9077e201badec77b91f75c23623d1a9c) Thanks [@jviide](https://github.com/jviide)! - Reduce size of Preact adapter by replacing `WeakSet`s with bitmasks.
|
|
27
|
+
|
|
28
|
+
- Updated dependencies [[`b4611cc`](https://github.com/preactjs/signals/commit/b4611cc9dee0ae09f4b378ba293c3203edc32be4), [`9802da5`](https://github.com/preactjs/signals/commit/9802da5274bb45c3cc28dda961b9b2d18535729a), [`6ac6923`](https://github.com/preactjs/signals/commit/6ac6923e5294f8a31ee1a009550b9891c3996cb4), [`79ff1e7`](https://github.com/preactjs/signals/commit/79ff1e794dde9952db2d6d43b22cebfb2accc770), [`3e31aab`](https://github.com/preactjs/signals/commit/3e31aabb812ddb0f7451deba38267f8384eff9d1)]:
|
|
29
|
+
- @preact/signals-core@1.2.0
|
|
30
|
+
|
|
31
|
+
## 1.0.4
|
|
32
|
+
|
|
33
|
+
### Patch Changes
|
|
34
|
+
|
|
35
|
+
- [#147](https://github.com/preactjs/signals/pull/147) [`3556499`](https://github.com/preactjs/signals/commit/355649903b766630b62cdd0f90a35d3eafa99fa9) Thanks [@developit](https://github.com/developit)! - Improve performance when rendering Signals as Text in Preact.
|
|
36
|
+
|
|
37
|
+
* [#148](https://github.com/preactjs/signals/pull/148) [`b948745`](https://github.com/preactjs/signals/commit/b948745de7b5b60a20ce3bdc5ee72d47d47f38ec) Thanks [@marvinhagemeister](https://github.com/marvinhagemeister)! - Move `types` field in `package.json` to the top of the entry list to ensure that TypeScript always finds it.
|
|
38
|
+
|
|
39
|
+
- [#153](https://github.com/preactjs/signals/pull/153) [`0da9ce3`](https://github.com/preactjs/signals/commit/0da9ce3c6f57cef67c3e84f0d829421aee8defff) Thanks [@developit](https://github.com/developit)! - Optimize the performance of prop bindings in Preact
|
|
40
|
+
|
|
41
|
+
- Updated dependencies [[`f2ba3d6`](https://github.com/preactjs/signals/commit/f2ba3d657bf8169c6ba1d47c0827aa18cfe1c947), [`160ea77`](https://github.com/preactjs/signals/commit/160ea7791f3adb55c562f5990e0b4848d8491a38), [`4385ea8`](https://github.com/preactjs/signals/commit/4385ea8c8358a154d8b789685bb061658ce1153f), [`b948745`](https://github.com/preactjs/signals/commit/b948745de7b5b60a20ce3bdc5ee72d47d47f38ec), [`00a59c6`](https://github.com/preactjs/signals/commit/00a59c6475bd4542fb934474d82d1e242b2ac870)]:
|
|
42
|
+
- @preact/signals-core@1.1.1
|
|
43
|
+
|
|
3
44
|
## 1.0.3
|
|
4
45
|
|
|
5
46
|
### Patch Changes
|
package/dist/signals.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { signal, computed, batch, effect, Signal, type ReadonlySignal } from "@p
|
|
|
2
2
|
export { signal, computed, batch, effect, Signal, type ReadonlySignal };
|
|
3
3
|
export declare function useSignal<T>(value: T): Signal<T>;
|
|
4
4
|
export declare function useComputed<T>(compute: () => T): ReadonlySignal<T>;
|
|
5
|
+
export declare function useSignalEffect(cb: () => void | (() => void)): void;
|
|
5
6
|
/**
|
|
6
7
|
* @todo Determine which Reactive implementation we'll be using.
|
|
7
8
|
* @internal
|
package/dist/signals.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
var
|
|
2
|
-
//# sourceMappingURL=signals.js.map
|
|
1
|
+
var r,n,i=require("preact"),t=require("preact/hooks"),f=require("@preact/signals-core");function o(r,n){i.options[r]=n.bind(null,i.options[r]||function(){})}function e(r){if(n)n();n=r&&r.S()}function u(r){var n=this,i=r.data,o=c(i);o.value=i;var e=t.useMemo(function(){var r=n.__v;while(r=r.__)if(r.__c){r.__c.__$f|=4;break}n.__$u.c=function(){n.base.data=e.peek()};return f.computed(function(){var r=o.value.value;return 0===r?0:!0===r?"":r||""})},[]);return e.value}u.displayName="_st";Object.defineProperties(f.Signal.prototype,{constructor:{configurable:!0},type:{configurable:!0,value:u},props:{configurable:!0,get:function(){return{data:this}}},__b:{configurable:!0,value:1}});o("__b",function(r,n){if("string"==typeof n.type){var i,t=n.props;for(var o in t)if("children"!==o){var e=t[o];if(e instanceof f.Signal){if(!i)n.__np=i={};i[o]=e;t[o]=e.peek()}}}r(n)});o("__r",function(n,i){e();var t,o=i.__c;if(o){o.__$f&=-2;if(void 0===(t=o.__$u))o.__$u=t=function(r){var n;f.effect(function(){n=this});n.c=function(){o.__$f|=1;o.setState({})};return n}()}r=o;e(t);n(i)});o("__e",function(n,i,t,f){e();r=void 0;n(i,t,f)});o("diffed",function(n,i){e();r=void 0;var t;if("string"==typeof i.type&&(t=i.__e)){var f=i.__np,o=i.props;if(f){var u=t.U;if(u)for(var c in u){var v=u[c];if(void 0!==v&&!(c in f)){v.d();u[c]=void 0}}else t.U=u={};for(var s in f){var p=u[s],l=f[s];if(void 0===p){p=a(t,s,l,o);u[s]=p}else p.o(l,o)}}}n(i)});function a(r,n,i,t){var o=n in r&&void 0===r.ownerSVGElement,e=f.signal(i);return{o:function(r,n){e.value=r;t=n},d:f.effect(function(){var i=e.value.value;if(t[n]!==i){t[n]=i;if(o)r[n]=i;else if(i)r.setAttribute(n,i);else r.removeAttribute(n)}})}}o("unmount",function(r,n){var i=n.__c,t=i&&i.__$u;if(t)t.d();if("string"==typeof n.type){var f=n.__e,o=f.U;if(o){f.U=null;for(var e in o){var u=o[e];if(u)u.d()}}}r(n)});o("__h",function(r,n,i,t){if(t<3)n.__$f|=2;r(n,i,t)});i.Component.prototype.shouldComponentUpdate=function(r,n){var i=this.__$u;if(!(i&&void 0!==i.s||4&this.__$f))return!0;if(3&this.__$f)return!0;for(var t in n)return!0;for(var f in r)if("__source"!==f&&r[f]!==this.props[f])return!0;for(var o in this.props)if(!(o in r))return!0;return!1};function c(r){return t.useMemo(function(){return f.signal(r)},[])}exports.Signal=f.Signal;exports.batch=f.batch;exports.computed=f.computed;exports.effect=f.effect;exports.signal=f.signal;exports.useComputed=function(n){var i=t.useRef(n);i.current=n;r.__$f|=4;return t.useMemo(function(){return f.computed(function(){return i.current()})},[])};exports.useSignal=c;exports.useSignalEffect=function(r){var n=t.useRef(r);n.current=r;t.useEffect(function(){return f.effect(function(){n.current()})},[])};//# 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\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":"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"}
|
|
1
|
+
{"version":3,"file":"signals.js","sources":["../src/index.ts"],"sourcesContent":["import { options, Component } from \"preact\";\nimport { useRef, useMemo, useEffect } 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\tOptionsTypes,\n\tHookFn,\n\tEffect,\n\tPropertyUpdater,\n\tAugmentedComponent,\n\tAugmentedElement as Element,\n} from \"./internal\";\n\nexport { signal, computed, batch, effect, Signal, type ReadonlySignal };\n\nconst HAS_PENDING_UPDATE = 1 << 0;\nconst HAS_HOOK_STATE = 1 << 1;\nconst HAS_COMPUTEDS = 1 << 2;\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: AugmentedComponent | undefined;\nlet finishUpdate: (() => void) | undefined;\n\nfunction setCurrentUpdater(updater?: Effect) {\n\t// end tracking for the current update:\n\tif (finishUpdate) finishUpdate();\n\t// start tracking the new update:\n\tfinishUpdate = updater && updater._start();\n}\n\nfunction createUpdater(update: () => void) {\n\tlet updater!: Effect;\n\teffect(function (this: Effect) {\n\t\tupdater = this;\n\t});\n\tupdater._callback = update;\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/**\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: AugmentedComponent, { 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\tv.__c._updateFlags |= HAS_COMPUTEDS;\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\tthis._updater!._callback = () => {\n\t\t\t(this.base as Text).data = s.peek();\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\nObject.defineProperties(Signal.prototype, {\n\tconstructor: { configurable: true },\n\ttype: { configurable: true, value: Text },\n\tprops: {\n\t\tconfigurable: true,\n\t\tget() {\n\t\t\treturn { data: this };\n\t\t},\n\t},\n\t// Setting a VNode's _depth to 1 forces Preact to clone it before modifying:\n\t// https://github.com/preactjs/preact/blob/d7a433ee8463a7dc23a05111bb47de9ec729ad4d/src/diff/children.js#L77\n\t// @todo remove this for Preact 11\n\t__b: { configurable: true, value: 1 },\n});\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\tlet signalProps: Record<string, any> | undefined;\n\n\t\tlet props = vnode.props;\n\t\tfor (let i in props) {\n\t\t\tif (i === \"children\") continue;\n\n\t\t\tlet value = props[i];\n\t\t\tif (value instanceof Signal) {\n\t\t\t\tif (!signalProps) vnode.__np = signalProps = {};\n\t\t\t\tsignalProps[i] = value;\n\t\t\t\tprops[i] = value.peek();\n\t\t\t}\n\t\t}\n\t}\n\n\told(vnode);\n});\n\n/** Set up Updater before rendering a component */\nhook(OptionsTypes.RENDER, (old, vnode) => {\n\tsetCurrentUpdater();\n\n\tlet updater;\n\n\tlet component = vnode.__c;\n\tif (component) {\n\t\tcomponent._updateFlags &= ~HAS_PENDING_UPDATE;\n\n\t\tupdater = component._updater;\n\t\tif (updater === undefined) {\n\t\t\tcomponent._updater = updater = createUpdater(() => {\n\t\t\t\tcomponent._updateFlags |= HAS_PENDING_UPDATE;\n\t\t\t\tcomponent.setState({});\n\t\t\t});\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\n\tlet dom: Element;\n\n\t// vnode._dom is undefined during string rendering,\n\t// so we use this to skip prop subscriptions during SSR.\n\tif (typeof vnode.type === \"string\" && (dom = vnode.__e as Element)) {\n\t\tlet props = vnode.__np;\n\t\tlet renderedProps = vnode.props;\n\t\tif (props) {\n\t\t\tlet updaters = dom._updaters;\n\t\t\tif (updaters) {\n\t\t\t\tfor (let prop in updaters) {\n\t\t\t\t\tlet updater = updaters[prop];\n\t\t\t\t\tif (updater !== undefined && !(prop in props)) {\n\t\t\t\t\t\tupdater._dispose();\n\t\t\t\t\t\t// @todo we could just always invoke _dispose() here\n\t\t\t\t\t\tupdaters[prop] = undefined;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tupdaters = {};\n\t\t\t\tdom._updaters = updaters;\n\t\t\t}\n\t\t\tfor (let prop in props) {\n\t\t\t\tlet updater = updaters[prop];\n\t\t\t\tlet signal = props[prop];\n\t\t\t\tif (updater === undefined) {\n\t\t\t\t\tupdater = createPropUpdater(dom, prop, signal, renderedProps);\n\t\t\t\t\tupdaters[prop] = updater;\n\t\t\t\t} else {\n\t\t\t\t\tupdater._update(signal, renderedProps);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\told(vnode);\n});\n\nfunction createPropUpdater(\n\tdom: Element,\n\tprop: string,\n\tpropSignal: Signal,\n\tprops: Record<string, any>\n): PropertyUpdater {\n\tconst setAsProperty =\n\t\tprop in dom &&\n\t\t// SVG elements need to go through `setAttribute` because they\n\t\t// expect things like SVGAnimatedTransformList instead of strings.\n\t\t// @ts-ignore\n\t\tdom.ownerSVGElement === undefined;\n\n\tconst changeSignal = signal(propSignal);\n\treturn {\n\t\t_update: (newSignal: Signal, newProps: typeof props) => {\n\t\t\tchangeSignal.value = newSignal;\n\t\t\tprops = newProps;\n\t\t},\n\t\t_dispose: effect(() => {\n\t\t\tconst value = changeSignal.value.value;\n\t\t\t// If Preact just rendered this value, don't render it again:\n\t\t\tif (props[prop] === value) return;\n\t\t\tprops[prop] = value;\n\t\t\tif (setAsProperty) {\n\t\t\t\t// @ts-ignore-next-line silly\n\t\t\t\tdom[prop] = value;\n\t\t\t} else if (value) {\n\t\t\t\tdom.setAttribute(prop, value);\n\t\t\t} else {\n\t\t\t\tdom.removeAttribute(prop);\n\t\t\t}\n\t\t}),\n\t};\n}\n\n/** Unsubscribe from Signals when unmounting components/vnodes */\nhook(OptionsTypes.UNMOUNT, (old, vnode: VNode) => {\n\tlet component = vnode.__c;\n\tconst updater = component && component._updater;\n\tif (updater) {\n\t\tupdater._dispose();\n\t}\n\n\tif (typeof vnode.type === \"string\") {\n\t\tconst dom = vnode.__e as Element;\n\n\t\tconst updaters = dom._updaters;\n\t\tif (updaters) {\n\t\t\tdom._updaters = null;\n\t\t\tfor (let prop in updaters) {\n\t\t\t\tlet updater = updaters[prop];\n\t\t\t\tif (updater) updater._dispose();\n\t\t\t}\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)\n\t\t(component as AugmentedComponent)._updateFlags |= HAS_HOOK_STATE;\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 (\n\tthis: AugmentedComponent,\n\tprops,\n\tstate\n) {\n\t// @todo: Once preactjs/preact#3671 lands, this could just use `currentUpdater`:\n\tconst updater = this._updater;\n\tconst hasSignals = updater && updater._sources !== undefined;\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 && !(this._updateFlags & HAS_COMPUTEDS)) return true;\n\n\t// if there is a pending re-render triggered from Signals,\n\t// or if there is hook or class state, update:\n\tif (this._updateFlags & (HAS_PENDING_UPDATE | HAS_HOOK_STATE)) return true;\n\n\t// @ts-ignore\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\t(currentComponent as AugmentedComponent)._updateFlags |= HAS_COMPUTEDS;\n\treturn useMemo(() => computed<T>(() => $compute.current()), []);\n}\n\nexport function useSignalEffect(cb: () => void | (() => void)) {\n\tconst callback = useRef(cb);\n\tcallback.current = cb;\n\n\tuseEffect(() => {\n\t\treturn effect(() => {\n\t\t\tcallback.current();\n\t\t});\n\t}, []);\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","preact","require","hooks","signalsCore","hook","hookName","hookFn","options","bind","setCurrentUpdater","updater","_start","_ref","_this","this","data","currentSignal","useSignal","value","s","useMemo","v","__v","__","__c","_updateFlags","_updater","_callback","base","peek","computed","Text","displayName","Object","defineProperties","Signal","prototype","constructor","configurable","type","props","get","__b","old","vnode","signalProps","i","__np","component","undefined","update","effect","setState","createUpdater","error","oldVNode","dom","__e","updaters","_updaters","prop","_dispose","signal","createPropUpdater","renderedProps","_update","propSignal","ownerSVGElement","changeSignal","newSignal","newProps","setAsProperty","setAttribute","removeAttribute","index","Component","shouldComponentUpdate","state","_sources","exports","batch","useComputed","compute","$compute","useRef","current","useSignalEffect","cb","callback","useEffect"],"mappings":"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"}
|
package/dist/signals.min.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
!function(n,
|
|
2
|
-
//# sourceMappingURL=signals.min.js.map
|
|
1
|
+
!function(n,i){"object"==typeof exports&&"undefined"!=typeof module?i(exports,require("preact"),require("preact/hooks"),require("@preact/signals-core")):"function"==typeof define&&define.amd?define(["exports","preact","preact/hooks","@preact/signals-core"],i):i((n||self).preactSignals={},n.preact,n.hooks,n.signalsCore)}(this,function(n,i,r,t){var f,e;function o(n,r){i.options[n]=r.bind(null,i.options[n]||function(){})}function u(n){if(e)e();e=n&&n.S()}function c(n){var i=this,f=n.data,e=v(f);e.value=f;var o=r.useMemo(function(){var n=i.__v;while(n=n.__)if(n.__c){n.__c.__$f|=4;break}i.__$u.c=function(){i.base.data=o.peek()};return t.computed(function(){var n=e.value.value;return 0===n?0:!0===n?"":n||""})},[]);return o.value}c.displayName="_st";Object.defineProperties(t.Signal.prototype,{constructor:{configurable:!0},type:{configurable:!0,value:c},props:{configurable:!0,get:function(){return{data:this}}},__b:{configurable:!0,value:1}});o("__b",function(n,i){if("string"==typeof i.type){var r,f=i.props;for(var e in f)if("children"!==e){var o=f[e];if(o instanceof t.Signal){if(!r)i.__np=r={};r[e]=o;f[e]=o.peek()}}}n(i)});o("__r",function(n,i){u();var r,e=i.__c;if(e){e.__$f&=-2;if(void 0===(r=e.__$u))e.__$u=r=function(n){var i;t.effect(function(){i=this});i.c=function(){e.__$f|=1;e.setState({})};return i}()}f=e;u(r);n(i)});o("__e",function(n,i,r,t){u();f=void 0;n(i,r,t)});o("diffed",function(n,i){u();f=void 0;var r;if("string"==typeof i.type&&(r=i.__e)){var t=i.__np,e=i.props;if(t){var o=r.U;if(o)for(var c in o){var v=o[c];if(void 0!==v&&!(c in t)){v.d();o[c]=void 0}}else r.U=o={};for(var s in t){var l=o[s],d=t[s];if(void 0===l){l=a(r,s,d,e);o[s]=l}else l.o(d,e)}}}n(i)});function a(n,i,r,f){var e=i in n&&void 0===n.ownerSVGElement,o=t.signal(r);return{o:function(n,i){o.value=n;f=i},d:t.effect(function(){var r=o.value.value;if(f[i]!==r){f[i]=r;if(e)n[i]=r;else if(r)n.setAttribute(i,r);else n.removeAttribute(i)}})}}o("unmount",function(n,i){var r=i.__c,t=r&&r.__$u;if(t)t.d();if("string"==typeof i.type){var f=i.__e,e=f.U;if(e){f.U=null;for(var o in e){var u=e[o];if(u)u.d()}}}n(i)});o("__h",function(n,i,r,t){if(t<3)i.__$f|=2;n(i,r,t)});i.Component.prototype.shouldComponentUpdate=function(n,i){var r=this.__$u;if(!(r&&void 0!==r.s||4&this.__$f))return!0;if(3&this.__$f)return!0;for(var t in i)return!0;for(var f in n)if("__source"!==f&&n[f]!==this.props[f])return!0;for(var e in this.props)if(!(e in n))return!0;return!1};function v(n){return r.useMemo(function(){return t.signal(n)},[])}n.Signal=t.Signal;n.batch=t.batch;n.computed=t.computed;n.effect=t.effect;n.signal=t.signal;n.useComputed=function(n){var i=r.useRef(n);i.current=n;f.__$f|=4;return r.useMemo(function(){return t.computed(function(){return i.current()})},[])};n.useSignal=v;n.useSignalEffect=function(n){var i=r.useRef(n);i.current=n;r.useEffect(function(){return t.effect(function(){i.current()})},[])}});//# 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\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":"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"}
|
|
1
|
+
{"version":3,"file":"signals.min.js","sources":["../src/index.ts"],"sourcesContent":["import { options, Component } from \"preact\";\nimport { useRef, useMemo, useEffect } 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\tOptionsTypes,\n\tHookFn,\n\tEffect,\n\tPropertyUpdater,\n\tAugmentedComponent,\n\tAugmentedElement as Element,\n} from \"./internal\";\n\nexport { signal, computed, batch, effect, Signal, type ReadonlySignal };\n\nconst HAS_PENDING_UPDATE = 1 << 0;\nconst HAS_HOOK_STATE = 1 << 1;\nconst HAS_COMPUTEDS = 1 << 2;\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: AugmentedComponent | undefined;\nlet finishUpdate: (() => void) | undefined;\n\nfunction setCurrentUpdater(updater?: Effect) {\n\t// end tracking for the current update:\n\tif (finishUpdate) finishUpdate();\n\t// start tracking the new update:\n\tfinishUpdate = updater && updater._start();\n}\n\nfunction createUpdater(update: () => void) {\n\tlet updater!: Effect;\n\teffect(function (this: Effect) {\n\t\tupdater = this;\n\t});\n\tupdater._callback = update;\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/**\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: AugmentedComponent, { 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\tv.__c._updateFlags |= HAS_COMPUTEDS;\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\tthis._updater!._callback = () => {\n\t\t\t(this.base as Text).data = s.peek();\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\nObject.defineProperties(Signal.prototype, {\n\tconstructor: { configurable: true },\n\ttype: { configurable: true, value: Text },\n\tprops: {\n\t\tconfigurable: true,\n\t\tget() {\n\t\t\treturn { data: this };\n\t\t},\n\t},\n\t// Setting a VNode's _depth to 1 forces Preact to clone it before modifying:\n\t// https://github.com/preactjs/preact/blob/d7a433ee8463a7dc23a05111bb47de9ec729ad4d/src/diff/children.js#L77\n\t// @todo remove this for Preact 11\n\t__b: { configurable: true, value: 1 },\n});\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\tlet signalProps: Record<string, any> | undefined;\n\n\t\tlet props = vnode.props;\n\t\tfor (let i in props) {\n\t\t\tif (i === \"children\") continue;\n\n\t\t\tlet value = props[i];\n\t\t\tif (value instanceof Signal) {\n\t\t\t\tif (!signalProps) vnode.__np = signalProps = {};\n\t\t\t\tsignalProps[i] = value;\n\t\t\t\tprops[i] = value.peek();\n\t\t\t}\n\t\t}\n\t}\n\n\told(vnode);\n});\n\n/** Set up Updater before rendering a component */\nhook(OptionsTypes.RENDER, (old, vnode) => {\n\tsetCurrentUpdater();\n\n\tlet updater;\n\n\tlet component = vnode.__c;\n\tif (component) {\n\t\tcomponent._updateFlags &= ~HAS_PENDING_UPDATE;\n\n\t\tupdater = component._updater;\n\t\tif (updater === undefined) {\n\t\t\tcomponent._updater = updater = createUpdater(() => {\n\t\t\t\tcomponent._updateFlags |= HAS_PENDING_UPDATE;\n\t\t\t\tcomponent.setState({});\n\t\t\t});\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\n\tlet dom: Element;\n\n\t// vnode._dom is undefined during string rendering,\n\t// so we use this to skip prop subscriptions during SSR.\n\tif (typeof vnode.type === \"string\" && (dom = vnode.__e as Element)) {\n\t\tlet props = vnode.__np;\n\t\tlet renderedProps = vnode.props;\n\t\tif (props) {\n\t\t\tlet updaters = dom._updaters;\n\t\t\tif (updaters) {\n\t\t\t\tfor (let prop in updaters) {\n\t\t\t\t\tlet updater = updaters[prop];\n\t\t\t\t\tif (updater !== undefined && !(prop in props)) {\n\t\t\t\t\t\tupdater._dispose();\n\t\t\t\t\t\t// @todo we could just always invoke _dispose() here\n\t\t\t\t\t\tupdaters[prop] = undefined;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tupdaters = {};\n\t\t\t\tdom._updaters = updaters;\n\t\t\t}\n\t\t\tfor (let prop in props) {\n\t\t\t\tlet updater = updaters[prop];\n\t\t\t\tlet signal = props[prop];\n\t\t\t\tif (updater === undefined) {\n\t\t\t\t\tupdater = createPropUpdater(dom, prop, signal, renderedProps);\n\t\t\t\t\tupdaters[prop] = updater;\n\t\t\t\t} else {\n\t\t\t\t\tupdater._update(signal, renderedProps);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\told(vnode);\n});\n\nfunction createPropUpdater(\n\tdom: Element,\n\tprop: string,\n\tpropSignal: Signal,\n\tprops: Record<string, any>\n): PropertyUpdater {\n\tconst setAsProperty =\n\t\tprop in dom &&\n\t\t// SVG elements need to go through `setAttribute` because they\n\t\t// expect things like SVGAnimatedTransformList instead of strings.\n\t\t// @ts-ignore\n\t\tdom.ownerSVGElement === undefined;\n\n\tconst changeSignal = signal(propSignal);\n\treturn {\n\t\t_update: (newSignal: Signal, newProps: typeof props) => {\n\t\t\tchangeSignal.value = newSignal;\n\t\t\tprops = newProps;\n\t\t},\n\t\t_dispose: effect(() => {\n\t\t\tconst value = changeSignal.value.value;\n\t\t\t// If Preact just rendered this value, don't render it again:\n\t\t\tif (props[prop] === value) return;\n\t\t\tprops[prop] = value;\n\t\t\tif (setAsProperty) {\n\t\t\t\t// @ts-ignore-next-line silly\n\t\t\t\tdom[prop] = value;\n\t\t\t} else if (value) {\n\t\t\t\tdom.setAttribute(prop, value);\n\t\t\t} else {\n\t\t\t\tdom.removeAttribute(prop);\n\t\t\t}\n\t\t}),\n\t};\n}\n\n/** Unsubscribe from Signals when unmounting components/vnodes */\nhook(OptionsTypes.UNMOUNT, (old, vnode: VNode) => {\n\tlet component = vnode.__c;\n\tconst updater = component && component._updater;\n\tif (updater) {\n\t\tupdater._dispose();\n\t}\n\n\tif (typeof vnode.type === \"string\") {\n\t\tconst dom = vnode.__e as Element;\n\n\t\tconst updaters = dom._updaters;\n\t\tif (updaters) {\n\t\t\tdom._updaters = null;\n\t\t\tfor (let prop in updaters) {\n\t\t\t\tlet updater = updaters[prop];\n\t\t\t\tif (updater) updater._dispose();\n\t\t\t}\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)\n\t\t(component as AugmentedComponent)._updateFlags |= HAS_HOOK_STATE;\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 (\n\tthis: AugmentedComponent,\n\tprops,\n\tstate\n) {\n\t// @todo: Once preactjs/preact#3671 lands, this could just use `currentUpdater`:\n\tconst updater = this._updater;\n\tconst hasSignals = updater && updater._sources !== undefined;\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 && !(this._updateFlags & HAS_COMPUTEDS)) return true;\n\n\t// if there is a pending re-render triggered from Signals,\n\t// or if there is hook or class state, update:\n\tif (this._updateFlags & (HAS_PENDING_UPDATE | HAS_HOOK_STATE)) return true;\n\n\t// @ts-ignore\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\t(currentComponent as AugmentedComponent)._updateFlags |= HAS_COMPUTEDS;\n\treturn useMemo(() => computed<T>(() => $compute.current()), []);\n}\n\nexport function useSignalEffect(cb: () => void | (() => void)) {\n\tconst callback = useRef(cb);\n\tcallback.current = cb;\n\n\tuseEffect(() => {\n\t\treturn effect(() => {\n\t\t\tcallback.current();\n\t\t});\n\t}, []);\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","finishUpdate","hook","hookName","hookFn","options","bind","setCurrentUpdater","updater","_start","_ref","_this","data","currentSignal","useSignal","value","s","useMemo","v","__v","__","__c","_updateFlags","_updater","_callback","base","peek","computed","Text","displayName","Object","defineProperties","Signal","prototype","constructor","configurable","type","props","get","__b","old","vnode","signalProps","i","__np","component","undefined","update","effect","setState","createUpdater","error","oldVNode","dom","__e","updaters","_updaters","prop","_dispose","signal","createPropUpdater","renderedProps","_update","propSignal","ownerSVGElement","changeSignal","newSignal","newProps","setAsProperty","setAttribute","removeAttribute","index","Component","shouldComponentUpdate","state","_sources","batch","useComputed","compute","$compute","useRef","current","useSignalEffect","cb","callback","useEffect"],"mappings":"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"}
|
package/dist/signals.mjs
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
import{Component as t,options as
|
|
2
|
-
//# sourceMappingURL=signals.mjs.map
|
|
1
|
+
import{Component as t,options as i}from"preact";import{useMemo as e,useRef as n,useEffect as r}from"preact/hooks";import{Signal as o,computed as f,signal as l,effect as c}from"@preact/signals-core";export{Signal,batch,computed,effect,signal}from"@preact/signals-core";function s(t,e){i[t]=e.bind(null,i[t]||(()=>{}))}let u,a;function p(t){if(a)a();a=t&&t.S()}function d({data:t}){const i=_(t);i.value=t;const n=e(()=>{let t=this.__v;while(t=t.__)if(t.__c){t.__c.__$f|=4;break}this.__$u.c=()=>{this.base.data=n.peek()};return f(()=>{let t=i.value.value;return 0===t?0:!0===t?"":t||""})},[]);return n.value}d.displayName="_st";Object.defineProperties(o.prototype,{constructor:{configurable:!0},type:{configurable:!0,value:d},props:{configurable:!0,get(){return{data:this}}},__b:{configurable:!0,value:1}});s("__b",(t,i)=>{if("string"==typeof i.type){let t,e=i.props;for(let n in e){if("children"===n)continue;let r=e[n];if(r instanceof o){if(!t)i.__np=t={};t[n]=r;e[n]=r.peek()}}}t(i)});s("__r",(t,i)=>{p();let e,n=i.__c;if(n){n.__$f&=-2;e=n.__$u;if(void 0===e)n.__$u=e=function(t){let i;c(function(){i=this});i.c=()=>{n.__$f|=1;n.setState({})};return i}()}u=n;p(e);t(i)});s("__e",(t,i,e,n)=>{p();u=void 0;t(i,e,n)});s("diffed",(t,i)=>{p();u=void 0;let e;if("string"==typeof i.type&&(e=i.__e)){let t=i.__np,n=i.props;if(t){let i=e.U;if(i)for(let e in i){let n=i[e];if(void 0!==n&&!(e in t)){n.d();i[e]=void 0}}else{i={};e.U=i}for(let r in t){let o=i[r],f=t[r];if(void 0===o){o=h(e,r,f,n);i[r]=o}else o.o(f,n)}}}t(i)});function h(t,i,e,n){const r=i in t&&void 0===t.ownerSVGElement,o=l(e);return{o:(t,i)=>{o.value=t;n=i},d:c(()=>{const e=o.value.value;if(n[i]!==e){n[i]=e;if(r)t[i]=e;else if(e)t.setAttribute(i,e);else t.removeAttribute(i)}})}}s("unmount",(t,i)=>{let e=i.__c;const n=e&&e.__$u;if(n)n.d();if("string"==typeof i.type){const t=i.__e,e=t.U;if(e){t.U=null;for(let t in e){let i=e[t];if(i)i.d()}}}t(i)});s("__h",(t,i,e,n)=>{if(n<3)i.__$f|=2;t(i,e,n)});t.prototype.shouldComponentUpdate=function(t,i){const e=this.__$u;if(!(e&&void 0!==e.s||4&this.__$f))return!0;if(3&this.__$f)return!0;for(let t in i)return!0;for(let i in t)if("__source"!==i&&t[i]!==this.props[i])return!0;for(let i in this.props)if(!(i in t))return!0;return!1};function _(t){return e(()=>l(t),[])}function g(t){const i=n(t);i.current=t;u.__$f|=4;return e(()=>f(()=>i.current()),[])}function v(t){const i=n(t);i.current=t;r(()=>c(()=>{i.current()}),[])}export{g as useComputed,_ as useSignal,v as useSignalEffect};//# 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\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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"}
|
|
1
|
+
{"version":3,"file":"signals.mjs","sources":["../src/index.ts"],"sourcesContent":["import { options, Component } from \"preact\";\nimport { useRef, useMemo, useEffect } 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\tOptionsTypes,\n\tHookFn,\n\tEffect,\n\tPropertyUpdater,\n\tAugmentedComponent,\n\tAugmentedElement as Element,\n} from \"./internal\";\n\nexport { signal, computed, batch, effect, Signal, type ReadonlySignal };\n\nconst HAS_PENDING_UPDATE = 1 << 0;\nconst HAS_HOOK_STATE = 1 << 1;\nconst HAS_COMPUTEDS = 1 << 2;\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: AugmentedComponent | undefined;\nlet finishUpdate: (() => void) | undefined;\n\nfunction setCurrentUpdater(updater?: Effect) {\n\t// end tracking for the current update:\n\tif (finishUpdate) finishUpdate();\n\t// start tracking the new update:\n\tfinishUpdate = updater && updater._start();\n}\n\nfunction createUpdater(update: () => void) {\n\tlet updater!: Effect;\n\teffect(function (this: Effect) {\n\t\tupdater = this;\n\t});\n\tupdater._callback = update;\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/**\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: AugmentedComponent, { 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\tv.__c._updateFlags |= HAS_COMPUTEDS;\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\tthis._updater!._callback = () => {\n\t\t\t(this.base as Text).data = s.peek();\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\nObject.defineProperties(Signal.prototype, {\n\tconstructor: { configurable: true },\n\ttype: { configurable: true, value: Text },\n\tprops: {\n\t\tconfigurable: true,\n\t\tget() {\n\t\t\treturn { data: this };\n\t\t},\n\t},\n\t// Setting a VNode's _depth to 1 forces Preact to clone it before modifying:\n\t// https://github.com/preactjs/preact/blob/d7a433ee8463a7dc23a05111bb47de9ec729ad4d/src/diff/children.js#L77\n\t// @todo remove this for Preact 11\n\t__b: { configurable: true, value: 1 },\n});\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\tlet signalProps: Record<string, any> | undefined;\n\n\t\tlet props = vnode.props;\n\t\tfor (let i in props) {\n\t\t\tif (i === \"children\") continue;\n\n\t\t\tlet value = props[i];\n\t\t\tif (value instanceof Signal) {\n\t\t\t\tif (!signalProps) vnode.__np = signalProps = {};\n\t\t\t\tsignalProps[i] = value;\n\t\t\t\tprops[i] = value.peek();\n\t\t\t}\n\t\t}\n\t}\n\n\told(vnode);\n});\n\n/** Set up Updater before rendering a component */\nhook(OptionsTypes.RENDER, (old, vnode) => {\n\tsetCurrentUpdater();\n\n\tlet updater;\n\n\tlet component = vnode.__c;\n\tif (component) {\n\t\tcomponent._updateFlags &= ~HAS_PENDING_UPDATE;\n\n\t\tupdater = component._updater;\n\t\tif (updater === undefined) {\n\t\t\tcomponent._updater = updater = createUpdater(() => {\n\t\t\t\tcomponent._updateFlags |= HAS_PENDING_UPDATE;\n\t\t\t\tcomponent.setState({});\n\t\t\t});\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\n\tlet dom: Element;\n\n\t// vnode._dom is undefined during string rendering,\n\t// so we use this to skip prop subscriptions during SSR.\n\tif (typeof vnode.type === \"string\" && (dom = vnode.__e as Element)) {\n\t\tlet props = vnode.__np;\n\t\tlet renderedProps = vnode.props;\n\t\tif (props) {\n\t\t\tlet updaters = dom._updaters;\n\t\t\tif (updaters) {\n\t\t\t\tfor (let prop in updaters) {\n\t\t\t\t\tlet updater = updaters[prop];\n\t\t\t\t\tif (updater !== undefined && !(prop in props)) {\n\t\t\t\t\t\tupdater._dispose();\n\t\t\t\t\t\t// @todo we could just always invoke _dispose() here\n\t\t\t\t\t\tupdaters[prop] = undefined;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tupdaters = {};\n\t\t\t\tdom._updaters = updaters;\n\t\t\t}\n\t\t\tfor (let prop in props) {\n\t\t\t\tlet updater = updaters[prop];\n\t\t\t\tlet signal = props[prop];\n\t\t\t\tif (updater === undefined) {\n\t\t\t\t\tupdater = createPropUpdater(dom, prop, signal, renderedProps);\n\t\t\t\t\tupdaters[prop] = updater;\n\t\t\t\t} else {\n\t\t\t\t\tupdater._update(signal, renderedProps);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\told(vnode);\n});\n\nfunction createPropUpdater(\n\tdom: Element,\n\tprop: string,\n\tpropSignal: Signal,\n\tprops: Record<string, any>\n): PropertyUpdater {\n\tconst setAsProperty =\n\t\tprop in dom &&\n\t\t// SVG elements need to go through `setAttribute` because they\n\t\t// expect things like SVGAnimatedTransformList instead of strings.\n\t\t// @ts-ignore\n\t\tdom.ownerSVGElement === undefined;\n\n\tconst changeSignal = signal(propSignal);\n\treturn {\n\t\t_update: (newSignal: Signal, newProps: typeof props) => {\n\t\t\tchangeSignal.value = newSignal;\n\t\t\tprops = newProps;\n\t\t},\n\t\t_dispose: effect(() => {\n\t\t\tconst value = changeSignal.value.value;\n\t\t\t// If Preact just rendered this value, don't render it again:\n\t\t\tif (props[prop] === value) return;\n\t\t\tprops[prop] = value;\n\t\t\tif (setAsProperty) {\n\t\t\t\t// @ts-ignore-next-line silly\n\t\t\t\tdom[prop] = value;\n\t\t\t} else if (value) {\n\t\t\t\tdom.setAttribute(prop, value);\n\t\t\t} else {\n\t\t\t\tdom.removeAttribute(prop);\n\t\t\t}\n\t\t}),\n\t};\n}\n\n/** Unsubscribe from Signals when unmounting components/vnodes */\nhook(OptionsTypes.UNMOUNT, (old, vnode: VNode) => {\n\tlet component = vnode.__c;\n\tconst updater = component && component._updater;\n\tif (updater) {\n\t\tupdater._dispose();\n\t}\n\n\tif (typeof vnode.type === \"string\") {\n\t\tconst dom = vnode.__e as Element;\n\n\t\tconst updaters = dom._updaters;\n\t\tif (updaters) {\n\t\t\tdom._updaters = null;\n\t\t\tfor (let prop in updaters) {\n\t\t\t\tlet updater = updaters[prop];\n\t\t\t\tif (updater) updater._dispose();\n\t\t\t}\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)\n\t\t(component as AugmentedComponent)._updateFlags |= HAS_HOOK_STATE;\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 (\n\tthis: AugmentedComponent,\n\tprops,\n\tstate\n) {\n\t// @todo: Once preactjs/preact#3671 lands, this could just use `currentUpdater`:\n\tconst updater = this._updater;\n\tconst hasSignals = updater && updater._sources !== undefined;\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 && !(this._updateFlags & HAS_COMPUTEDS)) return true;\n\n\t// if there is a pending re-render triggered from Signals,\n\t// or if there is hook or class state, update:\n\tif (this._updateFlags & (HAS_PENDING_UPDATE | HAS_HOOK_STATE)) return true;\n\n\t// @ts-ignore\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\t(currentComponent as AugmentedComponent)._updateFlags |= HAS_COMPUTEDS;\n\treturn useMemo(() => computed<T>(() => $compute.current()), []);\n}\n\nexport function useSignalEffect(cb: () => void | (() => void)) {\n\tconst callback = useRef(cb);\n\tcallback.current = cb;\n\n\tuseEffect(() => {\n\t\treturn effect(() => {\n\t\t\tcallback.current();\n\t\t});\n\t}, []);\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","useMemo","useRef","useEffect","Signal","computed","signal","effect","batch","hook","hookName","hookFn","bind","finishUpdate","setCurrentUpdater","updater","_start","Text","data","currentSignal","useSignal","value","s","v","this","__v","__","__c","_updateFlags","_updater","_callback","base","peek","displayName","Object","defineProperties","prototype","constructor","configurable","type","props","get","__b","old","vnode","signalProps","i","__np","component","undefined","update","setState","createUpdater","currentComponent","error","oldVNode","dom","__e","updaters","_updaters","prop","_dispose","createPropUpdater","renderedProps","_update","propSignal","setAsProperty","ownerSVGElement","newSignal","newProps","changeSignal","setAttribute","removeAttribute","index","shouldComponentUpdate","state","_sources","HAS_PENDING_UPDATE","useComputed","compute","$compute","current","useSignalEffect","cb","callback"],"mappings":"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"}
|
package/dist/signals.module.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
import{Component as n,options as r
|
|
2
|
-
//# sourceMappingURL=signals.module.js.map
|
|
1
|
+
import{Component as n,options as r}from"preact";import{useMemo as i,useRef as t,useEffect as f}from"preact/hooks";import{Signal as o,computed as u,signal as e,effect as a}from"@preact/signals-core";export{Signal,batch,computed,effect,signal}from"@preact/signals-core";var c,v;function s(n,i){r[n]=i.bind(null,r[n]||function(){})}function l(n){if(v)v();v=n&&n.S()}function p(n){var r=this,t=n.data,f=_(t);f.value=t;var o=i(function(){var n=r.__v;while(n=n.__)if(n.__c){n.__c.__$f|=4;break}r.__$u.c=function(){r.base.data=o.peek()};return u(function(){var n=f.value.value;return 0===n?0:!0===n?"":n||""})},[]);return o.value}p.displayName="_st";Object.defineProperties(o.prototype,{constructor:{configurable:!0},type:{configurable:!0,value:p},props:{configurable:!0,get:function(){return{data:this}}},__b:{configurable:!0,value:1}});s("__b",function(n,r){if("string"==typeof r.type){var i,t=r.props;for(var f in t)if("children"!==f){var u=t[f];if(u instanceof o){if(!i)r.__np=i={};i[f]=u;t[f]=u.peek()}}}n(r)});s("__r",function(n,r){l();var i,t=r.__c;if(t){t.__$f&=-2;if(void 0===(i=t.__$u))t.__$u=i=function(n){var r;a(function(){r=this});r.c=function(){t.__$f|=1;t.setState({})};return r}()}c=t;l(i);n(r)});s("__e",function(n,r,i,t){l();c=void 0;n(r,i,t)});s("diffed",function(n,r){l();c=void 0;var i;if("string"==typeof r.type&&(i=r.__e)){var t=r.__np,f=r.props;if(t){var o=i.U;if(o)for(var u in o){var e=o[u];if(void 0!==e&&!(u in t)){e.d();o[u]=void 0}}else i.U=o={};for(var a in t){var v=o[a],s=t[a];if(void 0===v){v=d(i,a,s,f);o[a]=v}else v.o(s,f)}}}n(r)});function d(n,r,i,t){var f=r in n&&void 0===n.ownerSVGElement,o=e(i);return{o:function(n,r){o.value=n;t=r},d:a(function(){var i=o.value.value;if(t[r]!==i){t[r]=i;if(f)n[r]=i;else if(i)n.setAttribute(r,i);else n.removeAttribute(r)}})}}s("unmount",function(n,r){var i=r.__c,t=i&&i.__$u;if(t)t.d();if("string"==typeof r.type){var f=r.__e,o=f.U;if(o){f.U=null;for(var u in o){var e=o[u];if(e)e.d()}}}n(r)});s("__h",function(n,r,i,t){if(t<3)r.__$f|=2;n(r,i,t)});n.prototype.shouldComponentUpdate=function(n,r){var i=this.__$u;if(!(i&&void 0!==i.s||4&this.__$f))return!0;if(3&this.__$f)return!0;for(var t in r)return!0;for(var f in n)if("__source"!==f&&n[f]!==this.props[f])return!0;for(var o in this.props)if(!(o in n))return!0;return!1};function _(n){return i(function(){return e(n)},[])}function h(n){var r=t(n);r.current=n;c.__$f|=4;return i(function(){return u(function(){return r.current()})},[])}function g(n){var r=t(n);r.current=n;f(function(){return a(function(){r.current()})},[])}export{h as useComputed,_ as useSignal,g as useSignalEffect};//# 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\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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"}
|
|
1
|
+
{"version":3,"file":"signals.module.js","sources":["../src/index.ts"],"sourcesContent":["import { options, Component } from \"preact\";\nimport { useRef, useMemo, useEffect } 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\tOptionsTypes,\n\tHookFn,\n\tEffect,\n\tPropertyUpdater,\n\tAugmentedComponent,\n\tAugmentedElement as Element,\n} from \"./internal\";\n\nexport { signal, computed, batch, effect, Signal, type ReadonlySignal };\n\nconst HAS_PENDING_UPDATE = 1 << 0;\nconst HAS_HOOK_STATE = 1 << 1;\nconst HAS_COMPUTEDS = 1 << 2;\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: AugmentedComponent | undefined;\nlet finishUpdate: (() => void) | undefined;\n\nfunction setCurrentUpdater(updater?: Effect) {\n\t// end tracking for the current update:\n\tif (finishUpdate) finishUpdate();\n\t// start tracking the new update:\n\tfinishUpdate = updater && updater._start();\n}\n\nfunction createUpdater(update: () => void) {\n\tlet updater!: Effect;\n\teffect(function (this: Effect) {\n\t\tupdater = this;\n\t});\n\tupdater._callback = update;\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/**\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: AugmentedComponent, { 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\tv.__c._updateFlags |= HAS_COMPUTEDS;\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\tthis._updater!._callback = () => {\n\t\t\t(this.base as Text).data = s.peek();\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\nObject.defineProperties(Signal.prototype, {\n\tconstructor: { configurable: true },\n\ttype: { configurable: true, value: Text },\n\tprops: {\n\t\tconfigurable: true,\n\t\tget() {\n\t\t\treturn { data: this };\n\t\t},\n\t},\n\t// Setting a VNode's _depth to 1 forces Preact to clone it before modifying:\n\t// https://github.com/preactjs/preact/blob/d7a433ee8463a7dc23a05111bb47de9ec729ad4d/src/diff/children.js#L77\n\t// @todo remove this for Preact 11\n\t__b: { configurable: true, value: 1 },\n});\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\tlet signalProps: Record<string, any> | undefined;\n\n\t\tlet props = vnode.props;\n\t\tfor (let i in props) {\n\t\t\tif (i === \"children\") continue;\n\n\t\t\tlet value = props[i];\n\t\t\tif (value instanceof Signal) {\n\t\t\t\tif (!signalProps) vnode.__np = signalProps = {};\n\t\t\t\tsignalProps[i] = value;\n\t\t\t\tprops[i] = value.peek();\n\t\t\t}\n\t\t}\n\t}\n\n\told(vnode);\n});\n\n/** Set up Updater before rendering a component */\nhook(OptionsTypes.RENDER, (old, vnode) => {\n\tsetCurrentUpdater();\n\n\tlet updater;\n\n\tlet component = vnode.__c;\n\tif (component) {\n\t\tcomponent._updateFlags &= ~HAS_PENDING_UPDATE;\n\n\t\tupdater = component._updater;\n\t\tif (updater === undefined) {\n\t\t\tcomponent._updater = updater = createUpdater(() => {\n\t\t\t\tcomponent._updateFlags |= HAS_PENDING_UPDATE;\n\t\t\t\tcomponent.setState({});\n\t\t\t});\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\n\tlet dom: Element;\n\n\t// vnode._dom is undefined during string rendering,\n\t// so we use this to skip prop subscriptions during SSR.\n\tif (typeof vnode.type === \"string\" && (dom = vnode.__e as Element)) {\n\t\tlet props = vnode.__np;\n\t\tlet renderedProps = vnode.props;\n\t\tif (props) {\n\t\t\tlet updaters = dom._updaters;\n\t\t\tif (updaters) {\n\t\t\t\tfor (let prop in updaters) {\n\t\t\t\t\tlet updater = updaters[prop];\n\t\t\t\t\tif (updater !== undefined && !(prop in props)) {\n\t\t\t\t\t\tupdater._dispose();\n\t\t\t\t\t\t// @todo we could just always invoke _dispose() here\n\t\t\t\t\t\tupdaters[prop] = undefined;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tupdaters = {};\n\t\t\t\tdom._updaters = updaters;\n\t\t\t}\n\t\t\tfor (let prop in props) {\n\t\t\t\tlet updater = updaters[prop];\n\t\t\t\tlet signal = props[prop];\n\t\t\t\tif (updater === undefined) {\n\t\t\t\t\tupdater = createPropUpdater(dom, prop, signal, renderedProps);\n\t\t\t\t\tupdaters[prop] = updater;\n\t\t\t\t} else {\n\t\t\t\t\tupdater._update(signal, renderedProps);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\told(vnode);\n});\n\nfunction createPropUpdater(\n\tdom: Element,\n\tprop: string,\n\tpropSignal: Signal,\n\tprops: Record<string, any>\n): PropertyUpdater {\n\tconst setAsProperty =\n\t\tprop in dom &&\n\t\t// SVG elements need to go through `setAttribute` because they\n\t\t// expect things like SVGAnimatedTransformList instead of strings.\n\t\t// @ts-ignore\n\t\tdom.ownerSVGElement === undefined;\n\n\tconst changeSignal = signal(propSignal);\n\treturn {\n\t\t_update: (newSignal: Signal, newProps: typeof props) => {\n\t\t\tchangeSignal.value = newSignal;\n\t\t\tprops = newProps;\n\t\t},\n\t\t_dispose: effect(() => {\n\t\t\tconst value = changeSignal.value.value;\n\t\t\t// If Preact just rendered this value, don't render it again:\n\t\t\tif (props[prop] === value) return;\n\t\t\tprops[prop] = value;\n\t\t\tif (setAsProperty) {\n\t\t\t\t// @ts-ignore-next-line silly\n\t\t\t\tdom[prop] = value;\n\t\t\t} else if (value) {\n\t\t\t\tdom.setAttribute(prop, value);\n\t\t\t} else {\n\t\t\t\tdom.removeAttribute(prop);\n\t\t\t}\n\t\t}),\n\t};\n}\n\n/** Unsubscribe from Signals when unmounting components/vnodes */\nhook(OptionsTypes.UNMOUNT, (old, vnode: VNode) => {\n\tlet component = vnode.__c;\n\tconst updater = component && component._updater;\n\tif (updater) {\n\t\tupdater._dispose();\n\t}\n\n\tif (typeof vnode.type === \"string\") {\n\t\tconst dom = vnode.__e as Element;\n\n\t\tconst updaters = dom._updaters;\n\t\tif (updaters) {\n\t\t\tdom._updaters = null;\n\t\t\tfor (let prop in updaters) {\n\t\t\t\tlet updater = updaters[prop];\n\t\t\t\tif (updater) updater._dispose();\n\t\t\t}\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)\n\t\t(component as AugmentedComponent)._updateFlags |= HAS_HOOK_STATE;\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 (\n\tthis: AugmentedComponent,\n\tprops,\n\tstate\n) {\n\t// @todo: Once preactjs/preact#3671 lands, this could just use `currentUpdater`:\n\tconst updater = this._updater;\n\tconst hasSignals = updater && updater._sources !== undefined;\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 && !(this._updateFlags & HAS_COMPUTEDS)) return true;\n\n\t// if there is a pending re-render triggered from Signals,\n\t// or if there is hook or class state, update:\n\tif (this._updateFlags & (HAS_PENDING_UPDATE | HAS_HOOK_STATE)) return true;\n\n\t// @ts-ignore\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\t(currentComponent as AugmentedComponent)._updateFlags |= HAS_COMPUTEDS;\n\treturn useMemo(() => computed<T>(() => $compute.current()), []);\n}\n\nexport function useSignalEffect(cb: () => void | (() => void)) {\n\tconst callback = useRef(cb);\n\tcallback.current = cb;\n\n\tuseEffect(() => {\n\t\treturn effect(() => {\n\t\t\tcallback.current();\n\t\t});\n\t}, []);\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","useMemo","useRef","useEffect","Signal","computed","signal","effect","batch","currentComponent","finishUpdate","hook","hookName","hookFn","bind","setCurrentUpdater","updater","_start","_ref","_this","this","data","currentSignal","useSignal","value","s","v","__v","__","__c","_updateFlags","_updater","_callback","base","peek","Text","displayName","Object","defineProperties","prototype","constructor","configurable","type","props","get","__b","old","vnode","signalProps","i","__np","component","undefined","update","setState","createUpdater","error","oldVNode","dom","__e","updaters","_updaters","prop","_dispose","createPropUpdater","renderedProps","_update","propSignal","ownerSVGElement","changeSignal","newSignal","newProps","setAsProperty","setAttribute","removeAttribute","index","shouldComponentUpdate","state","_sources","compute","$compute","current","useSignalEffect","cb","callback","useComputed"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@preact/signals",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "",
|
|
6
6
|
"keywords": [],
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
],
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
|
12
|
-
"url": "https://github.com/preactjs/signals"
|
|
12
|
+
"url": "https://github.com/preactjs/signals",
|
|
13
|
+
"directory": "packages/preact"
|
|
13
14
|
},
|
|
14
15
|
"bugs": "https://github.com/preactjs/signals/issues",
|
|
15
16
|
"homepage": "https://preactjs.com",
|
|
@@ -25,16 +26,16 @@
|
|
|
25
26
|
"source": "src/index.ts",
|
|
26
27
|
"exports": {
|
|
27
28
|
".": {
|
|
29
|
+
"types": "./dist/signals.d.ts",
|
|
28
30
|
"browser": "./dist/signals.module.js",
|
|
29
31
|
"umd": "./dist/signals.umd.js",
|
|
30
32
|
"import": "./dist/signals.mjs",
|
|
31
|
-
"require": "./dist/signals.js"
|
|
32
|
-
"types": "./dist/signals.d.ts"
|
|
33
|
+
"require": "./dist/signals.js"
|
|
33
34
|
}
|
|
34
35
|
},
|
|
35
36
|
"mangle": "../../mangle.json",
|
|
36
37
|
"dependencies": {
|
|
37
|
-
"@preact/signals-core": "^1.0
|
|
38
|
+
"@preact/signals-core": "^1.2.0"
|
|
38
39
|
},
|
|
39
40
|
"peerDependencies": {
|
|
40
41
|
"preact": "10.x"
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { options, Component
|
|
2
|
-
import { useRef, useMemo } from "preact/hooks";
|
|
1
|
+
import { options, Component } from "preact";
|
|
2
|
+
import { useRef, useMemo, useEffect } from "preact/hooks";
|
|
3
3
|
import {
|
|
4
4
|
signal,
|
|
5
5
|
computed,
|
|
@@ -10,23 +10,19 @@ import {
|
|
|
10
10
|
} from "@preact/signals-core";
|
|
11
11
|
import {
|
|
12
12
|
VNode,
|
|
13
|
-
ComponentType,
|
|
14
13
|
OptionsTypes,
|
|
15
14
|
HookFn,
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
Effect,
|
|
16
|
+
PropertyUpdater,
|
|
17
|
+
AugmentedComponent,
|
|
18
|
+
AugmentedElement as Element,
|
|
18
19
|
} from "./internal";
|
|
19
20
|
|
|
20
21
|
export { signal, computed, batch, effect, Signal, type ReadonlySignal };
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
// Components that have useState()/useReducer() hooks:
|
|
26
|
-
const hasHookState = new WeakSet<Component>();
|
|
27
|
-
|
|
28
|
-
// Components that have useComputed():
|
|
29
|
-
const hasComputeds = new WeakSet<Component>();
|
|
23
|
+
const HAS_PENDING_UPDATE = 1 << 0;
|
|
24
|
+
const HAS_HOOK_STATE = 1 << 1;
|
|
25
|
+
const HAS_COMPUTEDS = 1 << 2;
|
|
30
26
|
|
|
31
27
|
// Install a Preact options hook
|
|
32
28
|
function hook<T extends OptionsTypes>(hookName: T, hookFn: HookFn<T>) {
|
|
@@ -34,53 +30,22 @@ function hook<T extends OptionsTypes>(hookName: T, hookFn: HookFn<T>) {
|
|
|
34
30
|
options[hookName] = hookFn.bind(null, options[hookName] || (() => {}));
|
|
35
31
|
}
|
|
36
32
|
|
|
37
|
-
let currentComponent:
|
|
38
|
-
let
|
|
39
|
-
let finishUpdate: ReturnType<Updater["_setCurrent"]> | undefined;
|
|
40
|
-
const updaterForComponent = new WeakMap<Component | VNode, Updater>();
|
|
33
|
+
let currentComponent: AugmentedComponent | undefined;
|
|
34
|
+
let finishUpdate: (() => void) | undefined;
|
|
41
35
|
|
|
42
|
-
function setCurrentUpdater(updater?:
|
|
36
|
+
function setCurrentUpdater(updater?: Effect) {
|
|
43
37
|
// end tracking for the current update:
|
|
44
|
-
if (finishUpdate) finishUpdate(
|
|
38
|
+
if (finishUpdate) finishUpdate();
|
|
45
39
|
// start tracking the new update:
|
|
46
|
-
|
|
47
|
-
finishUpdate = updater && updater._setCurrent();
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function createUpdater(updater: () => void) {
|
|
51
|
-
const s = signal(undefined) as Updater;
|
|
52
|
-
s._canActivate = true;
|
|
53
|
-
s._updater = updater;
|
|
54
|
-
return s;
|
|
40
|
+
finishUpdate = updater && updater._start();
|
|
55
41
|
}
|
|
56
42
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
let dom = vnode.__e as Element;
|
|
64
|
-
|
|
65
|
-
for (let i = 0; i < signalProps.length; i++) {
|
|
66
|
-
let { _key: prop, _signal: signal } = signalProps[i];
|
|
67
|
-
let value = signal._value;
|
|
68
|
-
if (!dom) return;
|
|
69
|
-
if (prop in dom) {
|
|
70
|
-
// @ts-ignore-next-line silly
|
|
71
|
-
dom[prop] = value;
|
|
72
|
-
} else if (value) {
|
|
73
|
-
dom.setAttribute(prop, value);
|
|
74
|
-
} else {
|
|
75
|
-
dom.removeAttribute(prop);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}) as ElementUpdater;
|
|
79
|
-
updater._props = signalProps;
|
|
80
|
-
updaterForComponent.set(vnode, updater);
|
|
81
|
-
} else {
|
|
82
|
-
updater._props.length = 0;
|
|
83
|
-
}
|
|
43
|
+
function createUpdater(update: () => void) {
|
|
44
|
+
let updater!: Effect;
|
|
45
|
+
effect(function (this: Effect) {
|
|
46
|
+
updater = this;
|
|
47
|
+
});
|
|
48
|
+
updater._callback = update;
|
|
84
49
|
return updater;
|
|
85
50
|
}
|
|
86
51
|
|
|
@@ -93,23 +58,11 @@ function getElementUpdater(vnode: VNode) {
|
|
|
93
58
|
// return false;
|
|
94
59
|
// }
|
|
95
60
|
|
|
96
|
-
/** Convert Signals within (nested) props.children into Text components */
|
|
97
|
-
function childToSignal<T>(child: any, i: keyof T, arr: T) {
|
|
98
|
-
if (typeof child !== "object" || child == null) {
|
|
99
|
-
// can't be a signal
|
|
100
|
-
} else if (Array.isArray(child)) {
|
|
101
|
-
child.forEach(childToSignal);
|
|
102
|
-
} else if (child instanceof Signal) {
|
|
103
|
-
// @ts-ignore-next-line yes, arr can accept VNodes:
|
|
104
|
-
arr[i] = createElement(Text, { data: child });
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
61
|
/**
|
|
109
62
|
* A wrapper component that renders a Signal directly as a Text node.
|
|
110
63
|
* @todo: in Preact 11, just decorate Signal with `type:null`
|
|
111
64
|
*/
|
|
112
|
-
function Text(this:
|
|
65
|
+
function Text(this: AugmentedComponent, { data }: { data: Signal }) {
|
|
113
66
|
// hasComputeds.add(this);
|
|
114
67
|
|
|
115
68
|
// Store the props.data signal in another signal so that
|
|
@@ -122,14 +75,14 @@ function Text(this: ComponentType, { data }: { data: Signal }) {
|
|
|
122
75
|
let v = this.__v;
|
|
123
76
|
while ((v = v.__!)) {
|
|
124
77
|
if (v.__c) {
|
|
125
|
-
|
|
78
|
+
v.__c._updateFlags |= HAS_COMPUTEDS;
|
|
126
79
|
break;
|
|
127
80
|
}
|
|
128
81
|
}
|
|
129
82
|
|
|
130
83
|
// Replace this component's vdom updater with a direct text one:
|
|
131
|
-
|
|
132
|
-
(this.base as Text).data = s.
|
|
84
|
+
this._updater!._callback = () => {
|
|
85
|
+
(this.base as Text).data = s.peek();
|
|
133
86
|
};
|
|
134
87
|
|
|
135
88
|
return computed(() => {
|
|
@@ -143,37 +96,37 @@ function Text(this: ComponentType, { data }: { data: Signal }) {
|
|
|
143
96
|
}
|
|
144
97
|
Text.displayName = "_st";
|
|
145
98
|
|
|
99
|
+
Object.defineProperties(Signal.prototype, {
|
|
100
|
+
constructor: { configurable: true },
|
|
101
|
+
type: { configurable: true, value: Text },
|
|
102
|
+
props: {
|
|
103
|
+
configurable: true,
|
|
104
|
+
get() {
|
|
105
|
+
return { data: this };
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
// Setting a VNode's _depth to 1 forces Preact to clone it before modifying:
|
|
109
|
+
// https://github.com/preactjs/preact/blob/d7a433ee8463a7dc23a05111bb47de9ec729ad4d/src/diff/children.js#L77
|
|
110
|
+
// @todo remove this for Preact 11
|
|
111
|
+
__b: { configurable: true, value: 1 },
|
|
112
|
+
});
|
|
113
|
+
|
|
146
114
|
/** Inject low-level property/attribute bindings for Signals into Preact's diff */
|
|
147
115
|
hook(OptionsTypes.DIFF, (old, vnode) => {
|
|
148
116
|
if (typeof vnode.type === "string") {
|
|
149
|
-
|
|
150
|
-
let props = vnode.props;
|
|
151
|
-
let updater;
|
|
117
|
+
let signalProps: Record<string, any> | undefined;
|
|
152
118
|
|
|
119
|
+
let props = vnode.props;
|
|
153
120
|
for (let i in props) {
|
|
121
|
+
if (i === "children") continue;
|
|
122
|
+
|
|
154
123
|
let value = props[i];
|
|
155
|
-
if (
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
// first Signal prop triggers creation/cleanup of the updater:
|
|
159
|
-
if (!updater) updater = getElementUpdater(vnode);
|
|
160
|
-
// track which props are Signals for precise updates:
|
|
161
|
-
updater._props.push({ _key: i, _signal: value });
|
|
162
|
-
let newUpdater = updater._updater;
|
|
163
|
-
if (value._updater) {
|
|
164
|
-
let oldUpdater = value._updater;
|
|
165
|
-
value._updater = () => {
|
|
166
|
-
newUpdater();
|
|
167
|
-
oldUpdater();
|
|
168
|
-
};
|
|
169
|
-
} else {
|
|
170
|
-
value._updater = newUpdater;
|
|
171
|
-
}
|
|
124
|
+
if (value instanceof Signal) {
|
|
125
|
+
if (!signalProps) vnode.__np = signalProps = {};
|
|
126
|
+
signalProps[i] = value;
|
|
172
127
|
props[i] = value.peek();
|
|
173
128
|
}
|
|
174
129
|
}
|
|
175
|
-
|
|
176
|
-
setCurrentUpdater(updater);
|
|
177
130
|
}
|
|
178
131
|
|
|
179
132
|
old(vnode);
|
|
@@ -181,19 +134,20 @@ hook(OptionsTypes.DIFF, (old, vnode) => {
|
|
|
181
134
|
|
|
182
135
|
/** Set up Updater before rendering a component */
|
|
183
136
|
hook(OptionsTypes.RENDER, (old, vnode) => {
|
|
137
|
+
setCurrentUpdater();
|
|
138
|
+
|
|
184
139
|
let updater;
|
|
185
140
|
|
|
186
141
|
let component = vnode.__c;
|
|
187
142
|
if (component) {
|
|
188
|
-
|
|
143
|
+
component._updateFlags &= ~HAS_PENDING_UPDATE;
|
|
189
144
|
|
|
190
|
-
updater =
|
|
145
|
+
updater = component._updater;
|
|
191
146
|
if (updater === undefined) {
|
|
192
|
-
updater = createUpdater(() => {
|
|
193
|
-
|
|
147
|
+
component._updater = updater = createUpdater(() => {
|
|
148
|
+
component._updateFlags |= HAS_PENDING_UPDATE;
|
|
194
149
|
component.setState({});
|
|
195
150
|
});
|
|
196
|
-
updaterForComponent.set(component, updater);
|
|
197
151
|
}
|
|
198
152
|
}
|
|
199
153
|
|
|
@@ -213,19 +167,98 @@ hook(OptionsTypes.CATCH_ERROR, (old, error, vnode, oldVNode) => {
|
|
|
213
167
|
hook(OptionsTypes.DIFFED, (old, vnode) => {
|
|
214
168
|
setCurrentUpdater();
|
|
215
169
|
currentComponent = undefined;
|
|
170
|
+
|
|
171
|
+
let dom: Element;
|
|
172
|
+
|
|
173
|
+
// vnode._dom is undefined during string rendering,
|
|
174
|
+
// so we use this to skip prop subscriptions during SSR.
|
|
175
|
+
if (typeof vnode.type === "string" && (dom = vnode.__e as Element)) {
|
|
176
|
+
let props = vnode.__np;
|
|
177
|
+
let renderedProps = vnode.props;
|
|
178
|
+
if (props) {
|
|
179
|
+
let updaters = dom._updaters;
|
|
180
|
+
if (updaters) {
|
|
181
|
+
for (let prop in updaters) {
|
|
182
|
+
let updater = updaters[prop];
|
|
183
|
+
if (updater !== undefined && !(prop in props)) {
|
|
184
|
+
updater._dispose();
|
|
185
|
+
// @todo we could just always invoke _dispose() here
|
|
186
|
+
updaters[prop] = undefined;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} else {
|
|
190
|
+
updaters = {};
|
|
191
|
+
dom._updaters = updaters;
|
|
192
|
+
}
|
|
193
|
+
for (let prop in props) {
|
|
194
|
+
let updater = updaters[prop];
|
|
195
|
+
let signal = props[prop];
|
|
196
|
+
if (updater === undefined) {
|
|
197
|
+
updater = createPropUpdater(dom, prop, signal, renderedProps);
|
|
198
|
+
updaters[prop] = updater;
|
|
199
|
+
} else {
|
|
200
|
+
updater._update(signal, renderedProps);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
216
205
|
old(vnode);
|
|
217
206
|
});
|
|
218
207
|
|
|
208
|
+
function createPropUpdater(
|
|
209
|
+
dom: Element,
|
|
210
|
+
prop: string,
|
|
211
|
+
propSignal: Signal,
|
|
212
|
+
props: Record<string, any>
|
|
213
|
+
): PropertyUpdater {
|
|
214
|
+
const setAsProperty =
|
|
215
|
+
prop in dom &&
|
|
216
|
+
// SVG elements need to go through `setAttribute` because they
|
|
217
|
+
// expect things like SVGAnimatedTransformList instead of strings.
|
|
218
|
+
// @ts-ignore
|
|
219
|
+
dom.ownerSVGElement === undefined;
|
|
220
|
+
|
|
221
|
+
const changeSignal = signal(propSignal);
|
|
222
|
+
return {
|
|
223
|
+
_update: (newSignal: Signal, newProps: typeof props) => {
|
|
224
|
+
changeSignal.value = newSignal;
|
|
225
|
+
props = newProps;
|
|
226
|
+
},
|
|
227
|
+
_dispose: effect(() => {
|
|
228
|
+
const value = changeSignal.value.value;
|
|
229
|
+
// If Preact just rendered this value, don't render it again:
|
|
230
|
+
if (props[prop] === value) return;
|
|
231
|
+
props[prop] = value;
|
|
232
|
+
if (setAsProperty) {
|
|
233
|
+
// @ts-ignore-next-line silly
|
|
234
|
+
dom[prop] = value;
|
|
235
|
+
} else if (value) {
|
|
236
|
+
dom.setAttribute(prop, value);
|
|
237
|
+
} else {
|
|
238
|
+
dom.removeAttribute(prop);
|
|
239
|
+
}
|
|
240
|
+
}),
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
219
244
|
/** Unsubscribe from Signals when unmounting components/vnodes */
|
|
220
245
|
hook(OptionsTypes.UNMOUNT, (old, vnode: VNode) => {
|
|
221
|
-
let
|
|
222
|
-
const updater =
|
|
246
|
+
let component = vnode.__c;
|
|
247
|
+
const updater = component && component._updater;
|
|
223
248
|
if (updater) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
249
|
+
updater._dispose();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (typeof vnode.type === "string") {
|
|
253
|
+
const dom = vnode.__e as Element;
|
|
254
|
+
|
|
255
|
+
const updaters = dom._updaters;
|
|
256
|
+
if (updaters) {
|
|
257
|
+
dom._updaters = null;
|
|
258
|
+
for (let prop in updaters) {
|
|
259
|
+
let updater = updaters[prop];
|
|
260
|
+
if (updater) updater._dispose();
|
|
261
|
+
}
|
|
229
262
|
}
|
|
230
263
|
}
|
|
231
264
|
old(vnode);
|
|
@@ -233,7 +266,8 @@ hook(OptionsTypes.UNMOUNT, (old, vnode: VNode) => {
|
|
|
233
266
|
|
|
234
267
|
/** Mark components that use hook state so we can skip sCU optimization. */
|
|
235
268
|
hook(OptionsTypes.HOOK, (old, component, index, type) => {
|
|
236
|
-
if (type < 3)
|
|
269
|
+
if (type < 3)
|
|
270
|
+
(component as AugmentedComponent)._updateFlags |= HAS_HOOK_STATE;
|
|
237
271
|
old(component, index, type);
|
|
238
272
|
});
|
|
239
273
|
|
|
@@ -241,11 +275,14 @@ hook(OptionsTypes.HOOK, (old, component, index, type) => {
|
|
|
241
275
|
* Auto-memoize components that use Signals/Computeds.
|
|
242
276
|
* Note: Does _not_ optimize components that use hook/class state.
|
|
243
277
|
*/
|
|
244
|
-
Component.prototype.shouldComponentUpdate = function (
|
|
278
|
+
Component.prototype.shouldComponentUpdate = function (
|
|
279
|
+
this: AugmentedComponent,
|
|
280
|
+
props,
|
|
281
|
+
state
|
|
282
|
+
) {
|
|
245
283
|
// @todo: Once preactjs/preact#3671 lands, this could just use `currentUpdater`:
|
|
246
|
-
const updater =
|
|
247
|
-
|
|
248
|
-
const hasSignals = updater && updater._deps?.size !== 0;
|
|
284
|
+
const updater = this._updater;
|
|
285
|
+
const hasSignals = updater && updater._sources !== undefined;
|
|
249
286
|
|
|
250
287
|
// let reason;
|
|
251
288
|
// if (!hasSignals && !hasComputeds.has(this)) {
|
|
@@ -270,13 +307,13 @@ Component.prototype.shouldComponentUpdate = function (props, state) {
|
|
|
270
307
|
// }
|
|
271
308
|
|
|
272
309
|
// if this component used no signals or computeds, update:
|
|
273
|
-
if (!hasSignals && !
|
|
310
|
+
if (!hasSignals && !(this._updateFlags & HAS_COMPUTEDS)) return true;
|
|
274
311
|
|
|
275
|
-
// if there is a pending re-render triggered from Signals,
|
|
276
|
-
if
|
|
312
|
+
// if there is a pending re-render triggered from Signals,
|
|
313
|
+
// or if there is hook or class state, update:
|
|
314
|
+
if (this._updateFlags & (HAS_PENDING_UPDATE | HAS_HOOK_STATE)) return true;
|
|
277
315
|
|
|
278
|
-
//
|
|
279
|
-
if (hasHookState.has(this)) return true;
|
|
316
|
+
// @ts-ignore
|
|
280
317
|
for (let i in state) return true;
|
|
281
318
|
|
|
282
319
|
// if any non-Signal props changed, update:
|
|
@@ -296,10 +333,21 @@ export function useSignal<T>(value: T) {
|
|
|
296
333
|
export function useComputed<T>(compute: () => T) {
|
|
297
334
|
const $compute = useRef(compute);
|
|
298
335
|
$compute.current = compute;
|
|
299
|
-
|
|
336
|
+
(currentComponent as AugmentedComponent)._updateFlags |= HAS_COMPUTEDS;
|
|
300
337
|
return useMemo(() => computed<T>(() => $compute.current()), []);
|
|
301
338
|
}
|
|
302
339
|
|
|
340
|
+
export function useSignalEffect(cb: () => void | (() => void)) {
|
|
341
|
+
const callback = useRef(cb);
|
|
342
|
+
callback.current = cb;
|
|
343
|
+
|
|
344
|
+
useEffect(() => {
|
|
345
|
+
return effect(() => {
|
|
346
|
+
callback.current();
|
|
347
|
+
});
|
|
348
|
+
}, []);
|
|
349
|
+
}
|
|
350
|
+
|
|
303
351
|
/**
|
|
304
352
|
* @todo Determine which Reactive implementation we'll be using.
|
|
305
353
|
* @internal
|
package/src/internal.d.ts
CHANGED
|
@@ -1,24 +1,37 @@
|
|
|
1
1
|
import { Component } from "preact";
|
|
2
2
|
import { Signal } from "@preact/signals-core";
|
|
3
3
|
|
|
4
|
+
export interface Effect {
|
|
5
|
+
_sources: object | undefined;
|
|
6
|
+
_start(): () => void;
|
|
7
|
+
_callback(): void;
|
|
8
|
+
_dispose(): void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface PropertyUpdater {
|
|
12
|
+
_update: (newSignal: Signal, newProps: Record<string, any>) => void;
|
|
13
|
+
_dispose: () => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface AugmentedElement extends HTMLElement {
|
|
17
|
+
_updaters?: Record<string, PropertyUpdater | undefined> | null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface AugmentedComponent extends Component<any, any> {
|
|
21
|
+
__v: VNode;
|
|
22
|
+
_updater?: Effect;
|
|
23
|
+
_updateFlags: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
4
26
|
export interface VNode<P = any> extends preact.VNode<P> {
|
|
5
27
|
/** The component instance for this VNode */
|
|
6
|
-
__c:
|
|
28
|
+
__c: AugmentedComponent;
|
|
7
29
|
/** The parent VNode */
|
|
8
30
|
__?: VNode;
|
|
9
31
|
/** The DOM node for this VNode */
|
|
10
32
|
__e?: Element | Text;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
export interface ComponentType extends Component {
|
|
14
|
-
/** This component's owner VNode */
|
|
15
|
-
__v: VNode;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export type Updater = Signal<unknown>;
|
|
19
|
-
|
|
20
|
-
export interface ElementUpdater extends Updater {
|
|
21
|
-
_props: Array<{ _key: string, _signal: Signal }>;
|
|
33
|
+
/** Props that had Signal values before diffing (used after diffing to subscribe) */
|
|
34
|
+
__np?: Record<string, any> | null;
|
|
22
35
|
}
|
|
23
36
|
|
|
24
37
|
export const enum OptionsTypes {
|
|
File without changes
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { signal, useComputed } from "@preact/signals";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { setupRerender } from "preact/test-utils";
|
|
2
|
+
import { createElement, render } from "preact";
|
|
3
|
+
import { setupRerender, act } from "preact/test-utils";
|
|
5
4
|
|
|
6
5
|
const sleep = (ms?: number) => new Promise(r => setTimeout(r, ms));
|
|
7
6
|
|
|
@@ -20,7 +19,7 @@ describe("@preact/signals", () => {
|
|
|
20
19
|
|
|
21
20
|
describe("Text bindings", () => {
|
|
22
21
|
it("should render text without signals", () => {
|
|
23
|
-
render(
|
|
22
|
+
render(<span>test</span>, scratch);
|
|
24
23
|
const span = scratch.firstChild;
|
|
25
24
|
const text = span?.firstChild;
|
|
26
25
|
expect(text).to.have.property("data", "test");
|
|
@@ -28,7 +27,7 @@ describe("@preact/signals", () => {
|
|
|
28
27
|
|
|
29
28
|
it("should render Signals as Text", () => {
|
|
30
29
|
const sig = signal("test");
|
|
31
|
-
render(
|
|
30
|
+
render(<span>{sig}</span>, scratch);
|
|
32
31
|
const span = scratch.firstChild;
|
|
33
32
|
expect(span).to.have.property("firstChild").that.is.an.instanceOf(Text);
|
|
34
33
|
const text = span?.firstChild;
|
|
@@ -37,7 +36,7 @@ describe("@preact/signals", () => {
|
|
|
37
36
|
|
|
38
37
|
it("should update Signal-based Text (no parent component)", () => {
|
|
39
38
|
const sig = signal("test");
|
|
40
|
-
render(
|
|
39
|
+
render(<span>{sig}</span>, scratch);
|
|
41
40
|
|
|
42
41
|
const text = scratch.firstChild!.firstChild!;
|
|
43
42
|
expect(text).to.have.property("data", "test");
|
|
@@ -55,9 +54,9 @@ describe("@preact/signals", () => {
|
|
|
55
54
|
const spy = sinon.spy();
|
|
56
55
|
function App({ x }: { x: typeof sig }) {
|
|
57
56
|
spy();
|
|
58
|
-
return
|
|
57
|
+
return <span>{x}</span>;
|
|
59
58
|
}
|
|
60
|
-
render(
|
|
59
|
+
render(<App x={sig} />, scratch);
|
|
61
60
|
spy.resetHistory();
|
|
62
61
|
|
|
63
62
|
const text = scratch.firstChild!.firstChild!;
|
|
@@ -79,16 +78,16 @@ describe("@preact/signals", () => {
|
|
|
79
78
|
const spy = sinon.spy();
|
|
80
79
|
function App({ x }: { x: typeof sig }) {
|
|
81
80
|
spy();
|
|
82
|
-
return
|
|
81
|
+
return <span>{x}</span>;
|
|
83
82
|
}
|
|
84
|
-
render(
|
|
83
|
+
render(<App x={sig} />, scratch);
|
|
85
84
|
spy.resetHistory();
|
|
86
85
|
|
|
87
86
|
const text = scratch.firstChild!.firstChild!;
|
|
88
87
|
expect(text).to.have.property("data", "test");
|
|
89
88
|
|
|
90
89
|
const sig2 = signal("different");
|
|
91
|
-
render(
|
|
90
|
+
render(<App x={sig2} />, scratch);
|
|
92
91
|
expect(spy).to.have.been.called;
|
|
93
92
|
spy.resetHistory();
|
|
94
93
|
|
|
@@ -123,10 +122,10 @@ describe("@preact/signals", () => {
|
|
|
123
122
|
|
|
124
123
|
function App() {
|
|
125
124
|
const value = sig.value;
|
|
126
|
-
return
|
|
125
|
+
return <p>{value}</p>;
|
|
127
126
|
}
|
|
128
127
|
|
|
129
|
-
render(
|
|
128
|
+
render(<App />, scratch);
|
|
130
129
|
expect(scratch.textContent).to.equal("foo");
|
|
131
130
|
|
|
132
131
|
sig.value = "bar";
|
|
@@ -146,10 +145,10 @@ describe("@preact/signals", () => {
|
|
|
146
145
|
});
|
|
147
146
|
|
|
148
147
|
const str = arr.value.join(", ");
|
|
149
|
-
return
|
|
148
|
+
return <p>{str}</p>;
|
|
150
149
|
}
|
|
151
150
|
|
|
152
|
-
const fn = () => render(
|
|
151
|
+
const fn = () => render(<App />, scratch);
|
|
153
152
|
expect(fn).not.to.throw;
|
|
154
153
|
});
|
|
155
154
|
|
|
@@ -158,16 +157,16 @@ describe("@preact/signals", () => {
|
|
|
158
157
|
|
|
159
158
|
function Child() {
|
|
160
159
|
const value = sig.value;
|
|
161
|
-
return
|
|
160
|
+
return <p>{value}</p>;
|
|
162
161
|
}
|
|
163
162
|
|
|
164
163
|
const spy = sinon.spy();
|
|
165
164
|
function App() {
|
|
166
165
|
spy();
|
|
167
|
-
return
|
|
166
|
+
return <Child />;
|
|
168
167
|
}
|
|
169
168
|
|
|
170
|
-
render(
|
|
169
|
+
render(<App />, scratch);
|
|
171
170
|
expect(scratch.textContent).to.equal("foo");
|
|
172
171
|
|
|
173
172
|
sig.value = "bar";
|
|
@@ -180,7 +179,7 @@ describe("@preact/signals", () => {
|
|
|
180
179
|
it("should set the initial value of the checked property", () => {
|
|
181
180
|
const s = signal(true);
|
|
182
181
|
// @ts-ignore
|
|
183
|
-
render(
|
|
182
|
+
render(<input checked={s} />, scratch);
|
|
184
183
|
|
|
185
184
|
expect(scratch.firstChild).to.have.property("checked", true);
|
|
186
185
|
expect(s.value).to.equal(true);
|
|
@@ -189,7 +188,7 @@ describe("@preact/signals", () => {
|
|
|
189
188
|
it("should update the checked property on change", () => {
|
|
190
189
|
const s = signal(true);
|
|
191
190
|
// @ts-ignore
|
|
192
|
-
render(
|
|
191
|
+
render(<input checked={s} />, scratch);
|
|
193
192
|
|
|
194
193
|
expect(scratch.firstChild).to.have.property("checked", true);
|
|
195
194
|
|
|
@@ -204,9 +203,9 @@ describe("@preact/signals", () => {
|
|
|
204
203
|
function Wrap() {
|
|
205
204
|
spy();
|
|
206
205
|
// @ts-ignore
|
|
207
|
-
return
|
|
206
|
+
return <input value={s} />;
|
|
208
207
|
}
|
|
209
|
-
render(
|
|
208
|
+
render(<Wrap />, scratch);
|
|
210
209
|
spy.resetHistory();
|
|
211
210
|
|
|
212
211
|
expect(scratch.firstChild).to.have.property("value", "initial");
|
|
@@ -234,9 +233,9 @@ describe("@preact/signals", () => {
|
|
|
234
233
|
function Wrap() {
|
|
235
234
|
spy();
|
|
236
235
|
// @ts-ignore
|
|
237
|
-
return
|
|
236
|
+
return <div style={style} />;
|
|
238
237
|
}
|
|
239
|
-
render(
|
|
238
|
+
render(<Wrap />, scratch);
|
|
240
239
|
spy.resetHistory();
|
|
241
240
|
|
|
242
241
|
const div = scratch.firstChild as HTMLDivElement;
|
|
@@ -255,5 +254,86 @@ describe("@preact/signals", () => {
|
|
|
255
254
|
await sleep();
|
|
256
255
|
expect(spy).not.to.have.been.called;
|
|
257
256
|
});
|
|
257
|
+
|
|
258
|
+
it("should set updated signal prop values at most once", async () => {
|
|
259
|
+
const s = signal("initial");
|
|
260
|
+
const spy = sinon.spy();
|
|
261
|
+
function Wrap() {
|
|
262
|
+
spy();
|
|
263
|
+
// @ts-ignore
|
|
264
|
+
return <span ariaLabel={s} ariaDescription={s.value} />;
|
|
265
|
+
}
|
|
266
|
+
render(<Wrap />, scratch);
|
|
267
|
+
spy.resetHistory();
|
|
268
|
+
|
|
269
|
+
const span = scratch.firstElementChild as HTMLSpanElement;
|
|
270
|
+
const ariaLabel = sinon.spy();
|
|
271
|
+
Object.defineProperty(span, "ariaLabel", {
|
|
272
|
+
set: ariaLabel,
|
|
273
|
+
});
|
|
274
|
+
const ariaDescription = sinon.spy();
|
|
275
|
+
Object.defineProperty(span, "ariaDescription", {
|
|
276
|
+
set: ariaDescription,
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
act(() => {
|
|
280
|
+
s.value = "updated";
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
expect(spy).to.have.been.calledOnce;
|
|
284
|
+
|
|
285
|
+
expect(ariaLabel).to.have.been.calledOnce;
|
|
286
|
+
expect(ariaLabel).to.have.been.calledWith("updated");
|
|
287
|
+
ariaLabel.resetHistory();
|
|
288
|
+
|
|
289
|
+
expect(ariaDescription).to.have.been.calledOnce;
|
|
290
|
+
expect(ariaDescription).to.have.been.calledWith("updated");
|
|
291
|
+
ariaDescription.resetHistory();
|
|
292
|
+
|
|
293
|
+
// ensure the component was never re-rendered: (even after a tick)
|
|
294
|
+
await sleep();
|
|
295
|
+
|
|
296
|
+
expect(ariaLabel).not.to.have.been.called;
|
|
297
|
+
expect(ariaDescription).not.to.have.been.called;
|
|
298
|
+
|
|
299
|
+
act(() => {
|
|
300
|
+
s.value = "second update";
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
expect(ariaLabel).to.have.been.calledOnce;
|
|
304
|
+
expect(ariaLabel).to.have.been.calledWith("second update");
|
|
305
|
+
ariaLabel.resetHistory();
|
|
306
|
+
|
|
307
|
+
expect(ariaDescription).to.have.been.calledOnce;
|
|
308
|
+
expect(ariaDescription).to.have.been.calledWith("second update");
|
|
309
|
+
ariaDescription.resetHistory();
|
|
310
|
+
|
|
311
|
+
// ensure the component was never re-rendered: (even after a tick)
|
|
312
|
+
await sleep();
|
|
313
|
+
|
|
314
|
+
expect(ariaLabel).not.to.have.been.called;
|
|
315
|
+
expect(ariaDescription).not.to.have.been.called;
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it("should set SVG values", async () => {
|
|
319
|
+
const s = signal("scale(1 1)");
|
|
320
|
+
|
|
321
|
+
function App() {
|
|
322
|
+
return (
|
|
323
|
+
<svg>
|
|
324
|
+
<line
|
|
325
|
+
// @ts-ignore
|
|
326
|
+
transform={s}
|
|
327
|
+
/>
|
|
328
|
+
</svg>
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
render(<App />, scratch);
|
|
332
|
+
|
|
333
|
+
act(() => {
|
|
334
|
+
// This should not crash
|
|
335
|
+
s.value = "scale(1, 2)";
|
|
336
|
+
});
|
|
337
|
+
});
|
|
258
338
|
});
|
|
259
339
|
});
|