@preact/signals 1.0.4 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/dist/signals.d.ts +1 -0
- package/dist/signals.js +1 -1
- package/dist/signals.js.map +1 -1
- package/dist/signals.min.js +1 -1
- package/dist/signals.min.js.map +1 -1
- package/dist/signals.mjs +1 -1
- package/dist/signals.mjs.map +1 -1
- package/dist/signals.module.js +1 -1
- package/dist/signals.module.js.map +1 -1
- package/package.json +4 -3
- package/src/index.ts +132 -95
- package/src/internal.d.ts +23 -12
- package/test/index.test.tsx +82 -1
- package/test/ssr.test.tsx +151 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# @preact/signals
|
|
2
2
|
|
|
3
|
+
## 1.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#198](https://github.com/preactjs/signals/pull/198) [`3db7500`](https://github.com/preactjs/signals/commit/3db7500beea4c447f22fbde80af7b5171afa171c) Thanks [@JoviDeCroock](https://github.com/JoviDeCroock)! - Fix server-sider-render error when unmounting a signal passed as text into JSX.
|
|
8
|
+
|
|
9
|
+
## 1.1.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [#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
|
|
14
|
+
|
|
15
|
+
* [#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.
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
effect(() => {
|
|
19
|
+
console.log("This runs whenever a dependency changes");
|
|
20
|
+
return () => {
|
|
21
|
+
console.log("This runs when the effect is disposed");
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- [#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
|
|
29
|
+
|
|
30
|
+
* [#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.
|
|
31
|
+
|
|
32
|
+
- [#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.
|
|
33
|
+
|
|
34
|
+
- 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)]:
|
|
35
|
+
- @preact/signals-core@1.2.0
|
|
36
|
+
|
|
3
37
|
## 1.0.4
|
|
4
38
|
|
|
5
39
|
### 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 +1 @@
|
|
|
1
|
-
var r,n,
|
|
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],d=f[s];if(void 0===p){p=a(t,s,d,o);u[s]=p}else p.o(d,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){if("string"==typeof n.type){var i=n.__e;if(i){var t=i.U;if(t){i.U=void 0;for(var f in t){var o=t[f];if(o)o.d()}}}}else{var e=n.__c;if(e){var u=e.__$u;if(u){e.__$u=void 0;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 } 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._updater = updater;\n\treturn s;\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: 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\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\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\n\tlet dom: Element;\n\tlet updater: ElementUpdater;\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\tif (props) {\n\t\t\t// @ts-ignore-next\n\t\t\tupdater = dom._updater;\n\t\t\tif (!updater) {\n\t\t\t\tupdater = createElementUpdater(dom);\n\t\t\t\t// @ts-ignore-next\n\t\t\t\tdom._updater = updater;\n\t\t\t}\n\t\t\tupdater!._props = props;\n\t\t\tsetCurrentUpdater(updater);\n\t\t\t// @ts-ignore-next we're adding an argument here\n\t\t\tupdater._updater(true);\n\t\t}\n\t}\n\told(vnode);\n});\n\n// per-element updater for 1+ signal bindings\nfunction createElementUpdater(dom: Element) {\n\tconst cache: Record<string, any> = { __proto__: null };\n\tconst updater = createUpdater((skip?: boolean) => {\n\t\tconst props = updater._props;\n\t\tfor (let prop in props) {\n\t\t\tif (prop === \"children\") continue;\n\t\t\tlet signal = props[prop];\n\t\t\tif (signal instanceof Signal) {\n\t\t\t\tlet value = signal.value;\n\t\t\t\tlet cached = cache[prop];\n\t\t\t\tcache[prop] = value;\n\t\t\t\tif (skip === true || cached === value) {\n\t\t\t\t\t// this is just a subscribe run, not an update\n\t\t\t\t} else if (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}\n\t}) as ElementUpdater;\n\treturn updater;\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 && updaterForComponent.get(component);\n\tif (updater) {\n\t\tupdaterForComponent.delete(component);\n\t\tupdater._setCurrent()(true, true);\n\t}\n\n\tif (typeof vnode.type === \"string\") {\n\t\tconst dom = vnode.__e as Element;\n\n\t\t// @ts-ignore-next\n\t\tconst updater = dom._updater;\n\t\tif (updater) {\n\t\t\tupdater._setCurrent()(true, true);\n\t\t\t// @ts-ignore-next\n\t\t\tdom._updater = null;\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\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\thasComputeds.add(currentComponent!);\n\treturn useMemo(() => computed<T>(() => $compute.current()), []);\n}\n\n/**\n * @todo Determine which Reactive implementation we'll be using.\n * @internal\n */\n// export function useReactive<T extends object>(value: T): Reactive<T> {\n// \treturn useMemo(() => reactive<T>(value), []);\n// }\n\n/**\n * @internal\n * Update a Reactive's using the properties of an object or other Reactive.\n * Also works for Signals.\n * @example\n * // Update a Reactive with Object.assign()-like syntax:\n * const r = reactive({ name: \"Alice\" });\n * update(r, { name: \"Bob\" });\n * update(r, { age: 42 }); // property 'age' does not exist in type '{ name?: string }'\n * update(r, 2); // '2' has no properties in common with '{ name?: string }'\n * console.log(r.name.value); // \"Bob\"\n *\n * @example\n * // Update a Reactive with the properties of another Reactive:\n * const A = reactive({ name: \"Alice\" });\n * const B = reactive({ name: \"Bob\", age: 42 });\n * update(A, B);\n * console.log(`${A.name} is ${A.age}`); // \"Bob is 42\"\n *\n * @example\n * // Update a signal with assign()-like syntax:\n * const s = signal(42);\n * update(s, \"hi\"); // Argument type 'string' not assignable to type 'number'\n * update(s, {}); // Argument type '{}' not assignable to type 'number'\n * update(s, 43);\n * console.log(s.value); // 43\n *\n * @param obj The Reactive or Signal to be updated\n * @param update The value, Signal, object or Reactive to update `obj` to match\n * @param overwrite If `true`, any properties `obj` missing from `update` are set to `undefined`\n */\n/*\nexport function update<T extends SignalOrReactive>(\n\tobj: T,\n\tupdate: Partial<Unwrap<T>>,\n\toverwrite = false\n) {\n\tif (obj instanceof Signal) {\n\t\tobj.value = peekValue(update);\n\t} else {\n\t\tfor (let i in update) {\n\t\t\tif (i in obj) {\n\t\t\t\tobj[i].value = peekValue(update[i]);\n\t\t\t} else {\n\t\t\t\tlet sig = signal(peekValue(update[i]));\n\t\t\t\tsig[KEY] = i;\n\t\t\t\tobj[i] = sig;\n\t\t\t}\n\t\t}\n\t\tif (overwrite) {\n\t\t\tfor (let i in obj) {\n\t\t\t\tif (!(i in update)) {\n\t\t\t\t\tobj[i].value = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n*/\n"],"names":["currentComponent","currentUpdater","preact","require","hooks","signalsCore","hasPendingUpdate","WeakSet","hasHookState","hasComputeds","hookName","hookFn","options","bind","updaterForComponent","WeakMap","updater","finishUpdate","_setCurrent","createUpdater","signal","undefined","s","_updater","_ref","_this","this","data","currentSignal","useSignal","value","useMemo","v","__v","__","__c","add","base","_value","computed","createElementUpdater","dom","cache","__proto__","skip","props","_props","prop","_signal","cached","setAttribute","removeAttribute","Text","displayName","Object","defineProperties","Signal","prototype","constructor","configurable","type","get","__b","hook","old","vnode","signalProps","i","__np","peek","component","setState","set","setCurrentUpdater","error","oldVNode","__e","index","Component","shouldComponentUpdate","state","_updater$_deps","_deps","size","has","exports","batch","effect","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\tif (typeof vnode.type === \"string\") {\n\t\tlet dom = vnode.__e as Element | undefined;\n\t\t// vnode._dom is undefined during string rendering\n\t\tif (dom) {\n\t\t\tconst updaters = dom._updaters;\n\t\t\tif (updaters) {\n\t\t\t\tdom._updaters = undefined;\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) updater._dispose();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\tlet component = vnode.__c;\n\t\tif (component) {\n\t\t\tconst updater = component._updater;\n\t\t\tif (updater) {\n\t\t\t\tcomponent._updater = undefined;\n\t\t\t\tupdater._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","preact","require","hooks","signalsCore","hook","hookName","hookFn","options","bind","setCurrentUpdater","updater","finishUpdate","_start","Text","_ref","data","currentSignal","useSignal","value","s","useMemo","v","_this","__v","__","__c","_updateFlags","_updater","_callback","base","peek","computed","displayName","Object","defineProperties","Signal","prototype","constructor","configurable","type","props","get","this","__b","old","vnode","signalProps","i","__np","component","undefined","update","effect","setState","createUpdater","error","oldVNode","dom","__e","renderedProps","updaters","_updaters","prop","_dispose","signal","createPropUpdater","_update","propSignal","setAsProperty","ownerSVGElement","changeSignal","newSignal","newProps","setAttribute","removeAttribute","index","Component","shouldComponentUpdate","state","_sources","HAS_PENDING_UPDATE","exports","batch","useComputed","compute","$compute","useRef","current","useSignalEffect","cb","callback","useEffect"],"mappings":"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"}
|
package/dist/signals.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(n,
|
|
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 a(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}a.displayName="_st";Object.defineProperties(t.Signal.prototype,{constructor:{configurable:!0},type:{configurable:!0,value:a},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 a in o){var v=o[a];if(void 0!==v&&!(a in t)){v.d();o[a]=void 0}}else r.U=o={};for(var s in t){var d=o[s],l=t[s];if(void 0===d){d=c(r,s,l,e);o[s]=d}else d.o(l,e)}}}n(i)});function c(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){if("string"==typeof i.type){var r=i.__e;if(r){var t=r.U;if(t){r.U=void 0;for(var f in t){var e=t[f];if(e)e.d()}}}}else{var o=i.__c;if(o){var u=o.__$u;if(u){o.__$u=void 0;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 } 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._updater = updater;\n\treturn s;\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: 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\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\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\n\tlet dom: Element;\n\tlet updater: ElementUpdater;\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\tif (props) {\n\t\t\t// @ts-ignore-next\n\t\t\tupdater = dom._updater;\n\t\t\tif (!updater) {\n\t\t\t\tupdater = createElementUpdater(dom);\n\t\t\t\t// @ts-ignore-next\n\t\t\t\tdom._updater = updater;\n\t\t\t}\n\t\t\tupdater!._props = props;\n\t\t\tsetCurrentUpdater(updater);\n\t\t\t// @ts-ignore-next we're adding an argument here\n\t\t\tupdater._updater(true);\n\t\t}\n\t}\n\told(vnode);\n});\n\n// per-element updater for 1+ signal bindings\nfunction createElementUpdater(dom: Element) {\n\tconst cache: Record<string, any> = { __proto__: null };\n\tconst updater = createUpdater((skip?: boolean) => {\n\t\tconst props = updater._props;\n\t\tfor (let prop in props) {\n\t\t\tif (prop === \"children\") continue;\n\t\t\tlet signal = props[prop];\n\t\t\tif (signal instanceof Signal) {\n\t\t\t\tlet value = signal.value;\n\t\t\t\tlet cached = cache[prop];\n\t\t\t\tcache[prop] = value;\n\t\t\t\tif (skip === true || cached === value) {\n\t\t\t\t\t// this is just a subscribe run, not an update\n\t\t\t\t} else if (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}\n\t}) as ElementUpdater;\n\treturn updater;\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 && updaterForComponent.get(component);\n\tif (updater) {\n\t\tupdaterForComponent.delete(component);\n\t\tupdater._setCurrent()(true, true);\n\t}\n\n\tif (typeof vnode.type === \"string\") {\n\t\tconst dom = vnode.__e as Element;\n\n\t\t// @ts-ignore-next\n\t\tconst updater = dom._updater;\n\t\tif (updater) {\n\t\t\tupdater._setCurrent()(true, true);\n\t\t\t// @ts-ignore-next\n\t\t\tdom._updater = null;\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\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\thasComputeds.add(currentComponent!);\n\treturn useMemo(() => computed<T>(() => $compute.current()), []);\n}\n\n/**\n * @todo Determine which Reactive implementation we'll be using.\n * @internal\n */\n// export function useReactive<T extends object>(value: T): Reactive<T> {\n// \treturn useMemo(() => reactive<T>(value), []);\n// }\n\n/**\n * @internal\n * Update a Reactive's using the properties of an object or other Reactive.\n * Also works for Signals.\n * @example\n * // Update a Reactive with Object.assign()-like syntax:\n * const r = reactive({ name: \"Alice\" });\n * update(r, { name: \"Bob\" });\n * update(r, { age: 42 }); // property 'age' does not exist in type '{ name?: string }'\n * update(r, 2); // '2' has no properties in common with '{ name?: string }'\n * console.log(r.name.value); // \"Bob\"\n *\n * @example\n * // Update a Reactive with the properties of another Reactive:\n * const A = reactive({ name: \"Alice\" });\n * const B = reactive({ name: \"Bob\", age: 42 });\n * update(A, B);\n * console.log(`${A.name} is ${A.age}`); // \"Bob is 42\"\n *\n * @example\n * // Update a signal with assign()-like syntax:\n * const s = signal(42);\n * update(s, \"hi\"); // Argument type 'string' not assignable to type 'number'\n * update(s, {}); // Argument type '{}' not assignable to type 'number'\n * update(s, 43);\n * console.log(s.value); // 43\n *\n * @param obj The Reactive or Signal to be updated\n * @param update The value, Signal, object or Reactive to update `obj` to match\n * @param overwrite If `true`, any properties `obj` missing from `update` are set to `undefined`\n */\n/*\nexport function update<T extends SignalOrReactive>(\n\tobj: T,\n\tupdate: Partial<Unwrap<T>>,\n\toverwrite = false\n) {\n\tif (obj instanceof Signal) {\n\t\tobj.value = peekValue(update);\n\t} else {\n\t\tfor (let i in update) {\n\t\t\tif (i in obj) {\n\t\t\t\tobj[i].value = peekValue(update[i]);\n\t\t\t} else {\n\t\t\t\tlet sig = signal(peekValue(update[i]));\n\t\t\t\tsig[KEY] = i;\n\t\t\t\tobj[i] = sig;\n\t\t\t}\n\t\t}\n\t\tif (overwrite) {\n\t\t\tfor (let i in obj) {\n\t\t\t\tif (!(i in update)) {\n\t\t\t\t\tobj[i].value = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n*/\n"],"names":["g","f","exports","module","require","define","amd","globalThis","self","preactSignals","preact","hooks","signalsCore","this","currentComponent","currentUpdater","hasPendingUpdate","WeakSet","hasHookState","hasComputeds","hookName","hookFn","options","bind","updaterForComponent","WeakMap","updater","finishUpdate","_setCurrent","createUpdater","signal","undefined","s","_updater","_ref","_this","data","currentSignal","useSignal","value","useMemo","v","__v","__","__c","add","base","_value","computed","createElementUpdater","dom","cache","__proto__","skip","props","_props","prop","_signal","cached","setAttribute","removeAttribute","Text","displayName","Object","defineProperties","Signal","prototype","constructor","configurable","type","get","__b","hook","old","vnode","signalProps","i","__np","peek","component","setState","set","setCurrentUpdater","error","oldVNode","__e","index","Component","shouldComponentUpdate","state","_updater$_deps","_deps","size","has","batch","effect","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\tif (typeof vnode.type === \"string\") {\n\t\tlet dom = vnode.__e as Element | undefined;\n\t\t// vnode._dom is undefined during string rendering\n\t\tif (dom) {\n\t\t\tconst updaters = dom._updaters;\n\t\t\tif (updaters) {\n\t\t\t\tdom._updaters = undefined;\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) updater._dispose();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\tlet component = vnode.__c;\n\t\tif (component) {\n\t\t\tconst updater = component._updater;\n\t\t\tif (updater) {\n\t\t\t\tcomponent._updater = undefined;\n\t\t\t\tupdater._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","hook","hookName","hookFn","options","bind","setCurrentUpdater","updater","finishUpdate","_start","Text","_ref","data","currentSignal","useSignal","value","s","useMemo","v","_this","__v","__","__c","_updateFlags","_updater","_callback","base","peek","computed","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","renderedProps","updaters","_updaters","prop","_dispose","signal","createPropUpdater","_update","propSignal","setAsProperty","ownerSVGElement","changeSignal","newSignal","newProps","setAttribute","removeAttribute","index","Component","shouldComponentUpdate","state","_sources","HAS_PENDING_UPDATE","batch","useComputed","compute","$compute","useRef","current","useSignalEffect","cb","callback","useEffect"],"mappings":"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"}
|
package/dist/signals.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Component as t,options as
|
|
1
|
+
import{Component as t,options as i}from"preact";import{useMemo as e,useRef as n,useEffect as o}from"preact/hooks";import{Signal as r,computed as f,signal as l,effect as s}from"@preact/signals-core";export{Signal,batch,computed,effect,signal}from"@preact/signals-core";function c(t,e){i[t]=e.bind(null,i[t]||(()=>{}))}let u,a;function d(t){if(a)a();a=t&&t.S()}function p({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}p.displayName="_st";Object.defineProperties(r.prototype,{constructor:{configurable:!0},type:{configurable:!0,value:p},props:{configurable:!0,get(){return{data:this}}},__b:{configurable:!0,value:1}});c("__b",(t,i)=>{if("string"==typeof i.type){let t,e=i.props;for(let n in e){if("children"===n)continue;let o=e[n];if(o instanceof r){if(!t)i.__np=t={};t[n]=o;e[n]=o.peek()}}}t(i)});c("__r",(t,i)=>{d();let e,n=i.__c;if(n){n.__$f&=-2;e=n.__$u;if(void 0===e)n.__$u=e=function(t){let i;s(function(){i=this});i.c=()=>{n.__$f|=1;n.setState({})};return i}()}u=n;d(e);t(i)});c("__e",(t,i,e,n)=>{d();u=void 0;t(i,e,n)});c("diffed",(t,i)=>{d();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 o in t){let r=i[o],f=t[o];if(void 0===r){r=h(e,o,f,n);i[o]=r}else r.o(f,n)}}}t(i)});function h(t,i,e,n){const o=i in t&&void 0===t.ownerSVGElement,r=l(e);return{o:(t,i)=>{r.value=t;n=i},d:s(()=>{const e=r.value.value;if(n[i]!==e){n[i]=e;if(o)t[i]=e;else if(e)t.setAttribute(i,e);else t.removeAttribute(i)}})}}c("unmount",(t,i)=>{if("string"==typeof i.type){let t=i.__e;if(t){const i=t.U;if(i){t.U=void 0;for(let t in i){let e=i[t];if(e)e.d()}}}}else{let t=i.__c;if(t){const i=t.__$u;if(i){t.__$u=void 0;i.d()}}}t(i)});c("__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;o(()=>s(()=>{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 } 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._updater = updater;\n\treturn s;\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: 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\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\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\n\tlet dom: Element;\n\tlet updater: ElementUpdater;\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\tif (props) {\n\t\t\t// @ts-ignore-next\n\t\t\tupdater = dom._updater;\n\t\t\tif (!updater) {\n\t\t\t\tupdater = createElementUpdater(dom);\n\t\t\t\t// @ts-ignore-next\n\t\t\t\tdom._updater = updater;\n\t\t\t}\n\t\t\tupdater!._props = props;\n\t\t\tsetCurrentUpdater(updater);\n\t\t\t// @ts-ignore-next we're adding an argument here\n\t\t\tupdater._updater(true);\n\t\t}\n\t}\n\told(vnode);\n});\n\n// per-element updater for 1+ signal bindings\nfunction createElementUpdater(dom: Element) {\n\tconst cache: Record<string, any> = { __proto__: null };\n\tconst updater = createUpdater((skip?: boolean) => {\n\t\tconst props = updater._props;\n\t\tfor (let prop in props) {\n\t\t\tif (prop === \"children\") continue;\n\t\t\tlet signal = props[prop];\n\t\t\tif (signal instanceof Signal) {\n\t\t\t\tlet value = signal.value;\n\t\t\t\tlet cached = cache[prop];\n\t\t\t\tcache[prop] = value;\n\t\t\t\tif (skip === true || cached === value) {\n\t\t\t\t\t// this is just a subscribe run, not an update\n\t\t\t\t} else if (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}\n\t}) as ElementUpdater;\n\treturn updater;\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 && updaterForComponent.get(component);\n\tif (updater) {\n\t\tupdaterForComponent.delete(component);\n\t\tupdater._setCurrent()(true, true);\n\t}\n\n\tif (typeof vnode.type === \"string\") {\n\t\tconst dom = vnode.__e as Element;\n\n\t\t// @ts-ignore-next\n\t\tconst updater = dom._updater;\n\t\tif (updater) {\n\t\t\tupdater._setCurrent()(true, true);\n\t\t\t// @ts-ignore-next\n\t\t\tdom._updater = null;\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\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\thasComputeds.add(currentComponent!);\n\treturn useMemo(() => computed<T>(() => $compute.current()), []);\n}\n\n/**\n * @todo Determine which Reactive implementation we'll be using.\n * @internal\n */\n// export function useReactive<T extends object>(value: T): Reactive<T> {\n// \treturn useMemo(() => reactive<T>(value), []);\n// }\n\n/**\n * @internal\n * Update a Reactive's using the properties of an object or other Reactive.\n * Also works for Signals.\n * @example\n * // Update a Reactive with Object.assign()-like syntax:\n * const r = reactive({ name: \"Alice\" });\n * update(r, { name: \"Bob\" });\n * update(r, { age: 42 }); // property 'age' does not exist in type '{ name?: string }'\n * update(r, 2); // '2' has no properties in common with '{ name?: string }'\n * console.log(r.name.value); // \"Bob\"\n *\n * @example\n * // Update a Reactive with the properties of another Reactive:\n * const A = reactive({ name: \"Alice\" });\n * const B = reactive({ name: \"Bob\", age: 42 });\n * update(A, B);\n * console.log(`${A.name} is ${A.age}`); // \"Bob is 42\"\n *\n * @example\n * // Update a signal with assign()-like syntax:\n * const s = signal(42);\n * update(s, \"hi\"); // Argument type 'string' not assignable to type 'number'\n * update(s, {}); // Argument type '{}' not assignable to type 'number'\n * update(s, 43);\n * console.log(s.value); // 43\n *\n * @param obj The Reactive or Signal to be updated\n * @param update The value, Signal, object or Reactive to update `obj` to match\n * @param overwrite If `true`, any properties `obj` missing from `update` are set to `undefined`\n */\n/*\nexport function update<T extends SignalOrReactive>(\n\tobj: T,\n\tupdate: Partial<Unwrap<T>>,\n\toverwrite = false\n) {\n\tif (obj instanceof Signal) {\n\t\tobj.value = peekValue(update);\n\t} else {\n\t\tfor (let i in update) {\n\t\t\tif (i in obj) {\n\t\t\t\tobj[i].value = peekValue(update[i]);\n\t\t\t} else {\n\t\t\t\tlet sig = signal(peekValue(update[i]));\n\t\t\t\tsig[KEY] = i;\n\t\t\t\tobj[i] = sig;\n\t\t\t}\n\t\t}\n\t\tif (overwrite) {\n\t\t\tfor (let i in obj) {\n\t\t\t\tif (!(i in update)) {\n\t\t\t\t\tobj[i].value = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n*/\n"],"names":["Component","options","useMemo","useRef","Signal","computed","signal","batch","effect","hasPendingUpdate","WeakSet","hasHookState","hasComputeds","hook","hookName","hookFn","bind","currentComponent","currentUpdater","finishUpdate","updaterForComponent","WeakMap","setCurrentUpdater","updater","_setCurrent","createUpdater","s","undefined","_updater","Text","data","currentSignal","useSignal","value","v","this","__v","__","__c","add","base","_value","dom","cache","__proto__","skip","props","_props","prop","cached","setAttribute","removeAttribute","useComputed","compute","$compute","current","displayName","Object","defineProperties","prototype","constructor","configurable","type","get","__b","old","vnode","signalProps","i","__np","peek","component","delete","setState","set","error","oldVNode","__e","createElementUpdater","index","shouldComponentUpdate","state","_updater$_deps","size","_deps","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\tif (typeof vnode.type === \"string\") {\n\t\tlet dom = vnode.__e as Element | undefined;\n\t\t// vnode._dom is undefined during string rendering\n\t\tif (dom) {\n\t\t\tconst updaters = dom._updaters;\n\t\t\tif (updaters) {\n\t\t\t\tdom._updaters = undefined;\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) updater._dispose();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\tlet component = vnode.__c;\n\t\tif (component) {\n\t\t\tconst updater = component._updater;\n\t\t\tif (updater) {\n\t\t\t\tcomponent._updater = undefined;\n\t\t\t\tupdater._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","currentComponent","finishUpdate","setCurrentUpdater","updater","_start","data","currentSignal","useSignal","value","s","v","this","__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","renderedProps","updaters","_updaters","prop","_dispose","createPropUpdater","_update","propSignal","ownerSVGElement","changeSignal","newSignal","newProps","setAsProperty","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 +1 @@
|
|
|
1
|
-
import{Component as n,options as
|
|
1
|
+
import{Component as n,options as i}from"preact";import{useMemo as r,useRef as t,useEffect as f}from"preact/hooks";import{Signal as o,computed as e,signal as u,effect as a}from"@preact/signals-core";export{Signal,batch,computed,effect,signal}from"@preact/signals-core";var c,v;function s(n,r){i[n]=r.bind(null,i[n]||function(){})}function l(n){if(v)v();v=n&&n.S()}function p(n){var i=this,t=n.data,f=_(t);f.value=t;var o=r(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 e(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,i){if("string"==typeof i.type){var r,t=i.props;for(var f in t)if("children"!==f){var e=t[f];if(e instanceof o){if(!r)i.__np=r={};r[f]=e;t[f]=e.peek()}}}n(i)});s("__r",function(n,i){l();var r,t=i.__c;if(t){t.__$f&=-2;if(void 0===(r=t.__$u))t.__$u=r=function(n){var i;a(function(){i=this});i.c=function(){t.__$f|=1;t.setState({})};return i}()}c=t;l(r);n(i)});s("__e",function(n,i,r,t){l();c=void 0;n(i,r,t)});s("diffed",function(n,i){l();c=void 0;var r;if("string"==typeof i.type&&(r=i.__e)){var t=i.__np,f=i.props;if(t){var o=r.U;if(o)for(var e in o){var u=o[e];if(void 0!==u&&!(e in t)){u.d();o[e]=void 0}}else r.U=o={};for(var a in t){var v=o[a],s=t[a];if(void 0===v){v=d(r,a,s,f);o[a]=v}else v.o(s,f)}}}n(i)});function d(n,i,r,t){var f=i in n&&void 0===n.ownerSVGElement,o=u(r);return{o:function(n,i){o.value=n;t=i},d:a(function(){var r=o.value.value;if(t[i]!==r){t[i]=r;if(f)n[i]=r;else if(r)n.setAttribute(i,r);else n.removeAttribute(i)}})}}s("unmount",function(n,i){if("string"==typeof i.type){var r=i.__e;if(r){var t=r.U;if(t){r.U=void 0;for(var f in t){var o=t[f];if(o)o.d()}}}}else{var e=i.__c;if(e){var u=e.__$u;if(u){e.__$u=void 0;u.d()}}}n(i)});s("__h",function(n,i,r,t){if(t<3)i.__$f|=2;n(i,r,t)});n.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 o in this.props)if(!(o in n))return!0;return!1};function _(n){return r(function(){return u(n)},[])}function h(n){var i=t(n);i.current=n;c.__$f|=4;return r(function(){return e(function(){return i.current()})},[])}function g(n){var i=t(n);i.current=n;f(function(){return a(function(){i.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 } 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._updater = updater;\n\treturn s;\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: 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\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\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\n\tlet dom: Element;\n\tlet updater: ElementUpdater;\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\tif (props) {\n\t\t\t// @ts-ignore-next\n\t\t\tupdater = dom._updater;\n\t\t\tif (!updater) {\n\t\t\t\tupdater = createElementUpdater(dom);\n\t\t\t\t// @ts-ignore-next\n\t\t\t\tdom._updater = updater;\n\t\t\t}\n\t\t\tupdater!._props = props;\n\t\t\tsetCurrentUpdater(updater);\n\t\t\t// @ts-ignore-next we're adding an argument here\n\t\t\tupdater._updater(true);\n\t\t}\n\t}\n\told(vnode);\n});\n\n// per-element updater for 1+ signal bindings\nfunction createElementUpdater(dom: Element) {\n\tconst cache: Record<string, any> = { __proto__: null };\n\tconst updater = createUpdater((skip?: boolean) => {\n\t\tconst props = updater._props;\n\t\tfor (let prop in props) {\n\t\t\tif (prop === \"children\") continue;\n\t\t\tlet signal = props[prop];\n\t\t\tif (signal instanceof Signal) {\n\t\t\t\tlet value = signal.value;\n\t\t\t\tlet cached = cache[prop];\n\t\t\t\tcache[prop] = value;\n\t\t\t\tif (skip === true || cached === value) {\n\t\t\t\t\t// this is just a subscribe run, not an update\n\t\t\t\t} else if (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}\n\t}) as ElementUpdater;\n\treturn updater;\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 && updaterForComponent.get(component);\n\tif (updater) {\n\t\tupdaterForComponent.delete(component);\n\t\tupdater._setCurrent()(true, true);\n\t}\n\n\tif (typeof vnode.type === \"string\") {\n\t\tconst dom = vnode.__e as Element;\n\n\t\t// @ts-ignore-next\n\t\tconst updater = dom._updater;\n\t\tif (updater) {\n\t\t\tupdater._setCurrent()(true, true);\n\t\t\t// @ts-ignore-next\n\t\t\tdom._updater = null;\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\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\thasComputeds.add(currentComponent!);\n\treturn useMemo(() => computed<T>(() => $compute.current()), []);\n}\n\n/**\n * @todo Determine which Reactive implementation we'll be using.\n * @internal\n */\n// export function useReactive<T extends object>(value: T): Reactive<T> {\n// \treturn useMemo(() => reactive<T>(value), []);\n// }\n\n/**\n * @internal\n * Update a Reactive's using the properties of an object or other Reactive.\n * Also works for Signals.\n * @example\n * // Update a Reactive with Object.assign()-like syntax:\n * const r = reactive({ name: \"Alice\" });\n * update(r, { name: \"Bob\" });\n * update(r, { age: 42 }); // property 'age' does not exist in type '{ name?: string }'\n * update(r, 2); // '2' has no properties in common with '{ name?: string }'\n * console.log(r.name.value); // \"Bob\"\n *\n * @example\n * // Update a Reactive with the properties of another Reactive:\n * const A = reactive({ name: \"Alice\" });\n * const B = reactive({ name: \"Bob\", age: 42 });\n * update(A, B);\n * console.log(`${A.name} is ${A.age}`); // \"Bob is 42\"\n *\n * @example\n * // Update a signal with assign()-like syntax:\n * const s = signal(42);\n * update(s, \"hi\"); // Argument type 'string' not assignable to type 'number'\n * update(s, {}); // Argument type '{}' not assignable to type 'number'\n * update(s, 43);\n * console.log(s.value); // 43\n *\n * @param obj The Reactive or Signal to be updated\n * @param update The value, Signal, object or Reactive to update `obj` to match\n * @param overwrite If `true`, any properties `obj` missing from `update` are set to `undefined`\n */\n/*\nexport function update<T extends SignalOrReactive>(\n\tobj: T,\n\tupdate: Partial<Unwrap<T>>,\n\toverwrite = false\n) {\n\tif (obj instanceof Signal) {\n\t\tobj.value = peekValue(update);\n\t} else {\n\t\tfor (let i in update) {\n\t\t\tif (i in obj) {\n\t\t\t\tobj[i].value = peekValue(update[i]);\n\t\t\t} else {\n\t\t\t\tlet sig = signal(peekValue(update[i]));\n\t\t\t\tsig[KEY] = i;\n\t\t\t\tobj[i] = sig;\n\t\t\t}\n\t\t}\n\t\tif (overwrite) {\n\t\t\tfor (let i in obj) {\n\t\t\t\tif (!(i in update)) {\n\t\t\t\t\tobj[i].value = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n*/\n"],"names":["Component","options","useMemo","useRef","Signal","computed","signal","batch","effect","currentComponent","currentUpdater","hasPendingUpdate","WeakSet","hasHookState","hasComputeds","hookName","hookFn","bind","updaterForComponent","WeakMap","updater","finishUpdate","_setCurrent","createUpdater","undefined","s","_updater","_ref","_this","this","data","currentSignal","useSignal","value","v","__v","__","__c","add","base","_value","createElementUpdater","dom","cache","__proto__","skip","props","_props","prop","_signal","cached","setAttribute","removeAttribute","useComputed","compute","$compute","current","Text","displayName","Object","defineProperties","prototype","constructor","configurable","type","get","__b","hook","old","vnode","signalProps","i","__np","peek","component","setState","set","setCurrentUpdater","error","oldVNode","__e","index","shouldComponentUpdate","state","_updater$_deps","_deps","size","has"],"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\tif (typeof vnode.type === \"string\") {\n\t\tlet dom = vnode.__e as Element | undefined;\n\t\t// vnode._dom is undefined during string rendering\n\t\tif (dom) {\n\t\t\tconst updaters = dom._updaters;\n\t\t\tif (updaters) {\n\t\t\t\tdom._updaters = undefined;\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) updater._dispose();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\tlet component = vnode.__c;\n\t\tif (component) {\n\t\t\tconst updater = component._updater;\n\t\t\tif (updater) {\n\t\t\t\tcomponent._updater = undefined;\n\t\t\t\tupdater._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","hook","hookName","hookFn","bind","setCurrentUpdater","updater","finishUpdate","_start","Text","_ref","data","currentSignal","useSignal","value","s","v","_this","__v","__","__c","_updateFlags","_updater","_callback","base","peek","displayName","Object","defineProperties","prototype","constructor","configurable","type","props","get","this","__b","old","vnode","signalProps","i","__np","component","undefined","update","setState","createUpdater","error","oldVNode","dom","__e","renderedProps","updaters","_updaters","prop","_dispose","createPropUpdater","_update","propSignal","setAsProperty","ownerSVGElement","changeSignal","newSignal","newProps","setAttribute","removeAttribute","index","shouldComponentUpdate","state","_sources","HAS_PENDING_UPDATE","useComputed","compute","$compute","current","useSignalEffect","cb","callback"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@preact/signals",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "",
|
|
6
6
|
"keywords": [],
|
|
@@ -35,13 +35,14 @@
|
|
|
35
35
|
},
|
|
36
36
|
"mangle": "../../mangle.json",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@preact/signals-core": "^1.
|
|
38
|
+
"@preact/signals-core": "^1.2.0"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"preact": "10.x"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"preact": "10.9.0"
|
|
44
|
+
"preact": "10.9.0",
|
|
45
|
+
"preact-render-to-string": "^5.2.4"
|
|
45
46
|
},
|
|
46
47
|
"scripts": {}
|
|
47
48
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { options, Component } from "preact";
|
|
2
|
-
import { useRef, useMemo } from "preact/hooks";
|
|
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,23 +30,23 @@ 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();
|
|
40
|
+
finishUpdate = updater && updater._start();
|
|
48
41
|
}
|
|
49
42
|
|
|
50
|
-
function createUpdater(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
43
|
+
function createUpdater(update: () => void) {
|
|
44
|
+
let updater!: Effect;
|
|
45
|
+
effect(function (this: Effect) {
|
|
46
|
+
updater = this;
|
|
47
|
+
});
|
|
48
|
+
updater._callback = update;
|
|
49
|
+
return updater;
|
|
54
50
|
}
|
|
55
51
|
|
|
56
52
|
/** @todo This may be needed for complex prop value detection. */
|
|
@@ -66,7 +62,7 @@ function createUpdater(updater: () => void) {
|
|
|
66
62
|
* A wrapper component that renders a Signal directly as a Text node.
|
|
67
63
|
* @todo: in Preact 11, just decorate Signal with `type:null`
|
|
68
64
|
*/
|
|
69
|
-
function Text(this:
|
|
65
|
+
function Text(this: AugmentedComponent, { data }: { data: Signal }) {
|
|
70
66
|
// hasComputeds.add(this);
|
|
71
67
|
|
|
72
68
|
// Store the props.data signal in another signal so that
|
|
@@ -79,14 +75,14 @@ function Text(this: ComponentType, { data }: { data: Signal }) {
|
|
|
79
75
|
let v = this.__v;
|
|
80
76
|
while ((v = v.__!)) {
|
|
81
77
|
if (v.__c) {
|
|
82
|
-
|
|
78
|
+
v.__c._updateFlags |= HAS_COMPUTEDS;
|
|
83
79
|
break;
|
|
84
80
|
}
|
|
85
81
|
}
|
|
86
82
|
|
|
87
83
|
// Replace this component's vdom updater with a direct text one:
|
|
88
|
-
|
|
89
|
-
(this.base as Text).data = s.
|
|
84
|
+
this._updater!._callback = () => {
|
|
85
|
+
(this.base as Text).data = s.peek();
|
|
90
86
|
};
|
|
91
87
|
|
|
92
88
|
return computed(() => {
|
|
@@ -138,19 +134,20 @@ hook(OptionsTypes.DIFF, (old, vnode) => {
|
|
|
138
134
|
|
|
139
135
|
/** Set up Updater before rendering a component */
|
|
140
136
|
hook(OptionsTypes.RENDER, (old, vnode) => {
|
|
137
|
+
setCurrentUpdater();
|
|
138
|
+
|
|
141
139
|
let updater;
|
|
142
140
|
|
|
143
141
|
let component = vnode.__c;
|
|
144
142
|
if (component) {
|
|
145
|
-
|
|
143
|
+
component._updateFlags &= ~HAS_PENDING_UPDATE;
|
|
146
144
|
|
|
147
|
-
updater =
|
|
145
|
+
updater = component._updater;
|
|
148
146
|
if (updater === undefined) {
|
|
149
|
-
updater = createUpdater(() => {
|
|
150
|
-
|
|
147
|
+
component._updater = updater = createUpdater(() => {
|
|
148
|
+
component._updateFlags |= HAS_PENDING_UPDATE;
|
|
151
149
|
component.setState({});
|
|
152
150
|
});
|
|
153
|
-
updaterForComponent.set(component, updater);
|
|
154
151
|
}
|
|
155
152
|
}
|
|
156
153
|
|
|
@@ -172,75 +169,101 @@ hook(OptionsTypes.DIFFED, (old, vnode) => {
|
|
|
172
169
|
currentComponent = undefined;
|
|
173
170
|
|
|
174
171
|
let dom: Element;
|
|
175
|
-
let updater: ElementUpdater;
|
|
176
172
|
|
|
177
173
|
// vnode._dom is undefined during string rendering,
|
|
178
174
|
// so we use this to skip prop subscriptions during SSR.
|
|
179
175
|
if (typeof vnode.type === "string" && (dom = vnode.__e as Element)) {
|
|
180
176
|
let props = vnode.__np;
|
|
177
|
+
let renderedProps = vnode.props;
|
|
181
178
|
if (props) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
+
}
|
|
188
202
|
}
|
|
189
|
-
updater!._props = props;
|
|
190
|
-
setCurrentUpdater(updater);
|
|
191
|
-
// @ts-ignore-next we're adding an argument here
|
|
192
|
-
updater._updater(true);
|
|
193
203
|
}
|
|
194
204
|
}
|
|
195
205
|
old(vnode);
|
|
196
206
|
});
|
|
197
207
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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);
|
|
220
239
|
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
return updater;
|
|
240
|
+
}),
|
|
241
|
+
};
|
|
224
242
|
}
|
|
225
243
|
|
|
226
244
|
/** Unsubscribe from Signals when unmounting components/vnodes */
|
|
227
245
|
hook(OptionsTypes.UNMOUNT, (old, vnode: VNode) => {
|
|
228
|
-
let component = vnode.__c;
|
|
229
|
-
const updater = component && updaterForComponent.get(component);
|
|
230
|
-
if (updater) {
|
|
231
|
-
updaterForComponent.delete(component);
|
|
232
|
-
updater._setCurrent()(true, true);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
246
|
if (typeof vnode.type === "string") {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
247
|
+
let dom = vnode.__e as Element | undefined;
|
|
248
|
+
// vnode._dom is undefined during string rendering
|
|
249
|
+
if (dom) {
|
|
250
|
+
const updaters = dom._updaters;
|
|
251
|
+
if (updaters) {
|
|
252
|
+
dom._updaters = undefined;
|
|
253
|
+
for (let prop in updaters) {
|
|
254
|
+
let updater = updaters[prop];
|
|
255
|
+
if (updater) updater._dispose();
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
} else {
|
|
260
|
+
let component = vnode.__c;
|
|
261
|
+
if (component) {
|
|
262
|
+
const updater = component._updater;
|
|
263
|
+
if (updater) {
|
|
264
|
+
component._updater = undefined;
|
|
265
|
+
updater._dispose();
|
|
266
|
+
}
|
|
244
267
|
}
|
|
245
268
|
}
|
|
246
269
|
old(vnode);
|
|
@@ -248,7 +271,8 @@ hook(OptionsTypes.UNMOUNT, (old, vnode: VNode) => {
|
|
|
248
271
|
|
|
249
272
|
/** Mark components that use hook state so we can skip sCU optimization. */
|
|
250
273
|
hook(OptionsTypes.HOOK, (old, component, index, type) => {
|
|
251
|
-
if (type < 3)
|
|
274
|
+
if (type < 3)
|
|
275
|
+
(component as AugmentedComponent)._updateFlags |= HAS_HOOK_STATE;
|
|
252
276
|
old(component, index, type);
|
|
253
277
|
});
|
|
254
278
|
|
|
@@ -256,11 +280,14 @@ hook(OptionsTypes.HOOK, (old, component, index, type) => {
|
|
|
256
280
|
* Auto-memoize components that use Signals/Computeds.
|
|
257
281
|
* Note: Does _not_ optimize components that use hook/class state.
|
|
258
282
|
*/
|
|
259
|
-
Component.prototype.shouldComponentUpdate = function (
|
|
283
|
+
Component.prototype.shouldComponentUpdate = function (
|
|
284
|
+
this: AugmentedComponent,
|
|
285
|
+
props,
|
|
286
|
+
state
|
|
287
|
+
) {
|
|
260
288
|
// @todo: Once preactjs/preact#3671 lands, this could just use `currentUpdater`:
|
|
261
|
-
const updater =
|
|
262
|
-
|
|
263
|
-
const hasSignals = updater && updater._deps?.size !== 0;
|
|
289
|
+
const updater = this._updater;
|
|
290
|
+
const hasSignals = updater && updater._sources !== undefined;
|
|
264
291
|
|
|
265
292
|
// let reason;
|
|
266
293
|
// if (!hasSignals && !hasComputeds.has(this)) {
|
|
@@ -285,13 +312,12 @@ Component.prototype.shouldComponentUpdate = function (props, state) {
|
|
|
285
312
|
// }
|
|
286
313
|
|
|
287
314
|
// if this component used no signals or computeds, update:
|
|
288
|
-
if (!hasSignals && !
|
|
315
|
+
if (!hasSignals && !(this._updateFlags & HAS_COMPUTEDS)) return true;
|
|
289
316
|
|
|
290
|
-
// if there is a pending re-render triggered from Signals,
|
|
291
|
-
if
|
|
317
|
+
// if there is a pending re-render triggered from Signals,
|
|
318
|
+
// or if there is hook or class state, update:
|
|
319
|
+
if (this._updateFlags & (HAS_PENDING_UPDATE | HAS_HOOK_STATE)) return true;
|
|
292
320
|
|
|
293
|
-
// if there is hook or class state, update:
|
|
294
|
-
if (hasHookState.has(this)) return true;
|
|
295
321
|
// @ts-ignore
|
|
296
322
|
for (let i in state) return true;
|
|
297
323
|
|
|
@@ -312,10 +338,21 @@ export function useSignal<T>(value: T) {
|
|
|
312
338
|
export function useComputed<T>(compute: () => T) {
|
|
313
339
|
const $compute = useRef(compute);
|
|
314
340
|
$compute.current = compute;
|
|
315
|
-
|
|
341
|
+
(currentComponent as AugmentedComponent)._updateFlags |= HAS_COMPUTEDS;
|
|
316
342
|
return useMemo(() => computed<T>(() => $compute.current()), []);
|
|
317
343
|
}
|
|
318
344
|
|
|
345
|
+
export function useSignalEffect(cb: () => void | (() => void)) {
|
|
346
|
+
const callback = useRef(cb);
|
|
347
|
+
callback.current = cb;
|
|
348
|
+
|
|
349
|
+
useEffect(() => {
|
|
350
|
+
return effect(() => {
|
|
351
|
+
callback.current();
|
|
352
|
+
});
|
|
353
|
+
}, []);
|
|
354
|
+
}
|
|
355
|
+
|
|
319
356
|
/**
|
|
320
357
|
* @todo Determine which Reactive implementation we'll be using.
|
|
321
358
|
* @internal
|
package/src/internal.d.ts
CHANGED
|
@@ -1,9 +1,31 @@
|
|
|
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 */
|
|
@@ -12,17 +34,6 @@ export interface VNode<P = any> extends preact.VNode<P> {
|
|
|
12
34
|
__np?: Record<string, any> | null;
|
|
13
35
|
}
|
|
14
36
|
|
|
15
|
-
export interface ComponentType extends Component {
|
|
16
|
-
/** This component's owner VNode */
|
|
17
|
-
__v: VNode;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export type Updater = Signal<unknown>;
|
|
21
|
-
|
|
22
|
-
export interface ElementUpdater extends Updater {
|
|
23
|
-
_props: Record<string, any>;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
37
|
export const enum OptionsTypes {
|
|
27
38
|
HOOK = "__h",
|
|
28
39
|
DIFF = "__b",
|
package/test/index.test.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { signal, useComputed } from "@preact/signals";
|
|
2
2
|
import { createElement, render } from "preact";
|
|
3
|
-
import { setupRerender } from "preact/test-utils";
|
|
3
|
+
import { setupRerender, act } from "preact/test-utils";
|
|
4
4
|
|
|
5
5
|
const sleep = (ms?: number) => new Promise(r => setTimeout(r, ms));
|
|
6
6
|
|
|
@@ -254,5 +254,86 @@ describe("@preact/signals", () => {
|
|
|
254
254
|
await sleep();
|
|
255
255
|
expect(spy).not.to.have.been.called;
|
|
256
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
|
+
});
|
|
257
338
|
});
|
|
258
339
|
});
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { signal, useSignal, useComputed } from "@preact/signals";
|
|
2
|
+
import { createElement } from "preact";
|
|
3
|
+
import { renderToString } from "preact-render-to-string";
|
|
4
|
+
|
|
5
|
+
const sleep = (ms?: number) => new Promise(r => setTimeout(r, ms));
|
|
6
|
+
|
|
7
|
+
describe("@preact/signals", () => {
|
|
8
|
+
describe("SSR", () => {
|
|
9
|
+
it("should render without erroring", () => {
|
|
10
|
+
const s = signal(0);
|
|
11
|
+
function App() {
|
|
12
|
+
return <p>{s}</p>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
expect(() => renderToString(<App />)).not.to.throw();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe("Text bindings", () => {
|
|
19
|
+
it("should render strings", () => {
|
|
20
|
+
const s = signal("hello");
|
|
21
|
+
expect(renderToString(<p>{s}</p>)).to.equal(`<p>hello</p>`);
|
|
22
|
+
});
|
|
23
|
+
it("should encode HTML entities", () => {
|
|
24
|
+
const s = signal('hello < & " world!');
|
|
25
|
+
expect(renderToString(<p>{s}</p>)).to.equal(
|
|
26
|
+
`<p>hello < & " world!</p>`
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
it("should render numbers as text", () => {
|
|
30
|
+
const s = signal(0);
|
|
31
|
+
expect(renderToString(<p>{s}</p>)).to.equal(`<p>0</p>`);
|
|
32
|
+
});
|
|
33
|
+
it("should not render booleans", () => {
|
|
34
|
+
const a = signal(true);
|
|
35
|
+
expect(renderToString(<p>{a}</p>)).to.equal(`<p></p>`);
|
|
36
|
+
|
|
37
|
+
const b = signal(false);
|
|
38
|
+
expect(renderToString(<p>{b}</p>)).to.equal(`<p></p>`);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe("Property bindings", () => {
|
|
43
|
+
it("should render Signal prop values", () => {
|
|
44
|
+
const a = signal(0);
|
|
45
|
+
// @ts-ignore-next-line
|
|
46
|
+
expect(renderToString(<div count={a} />)).to.equal(
|
|
47
|
+
`<div count="0"></div>`
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const b = signal("hello");
|
|
51
|
+
// @ts-ignore-next-line
|
|
52
|
+
expect(renderToString(<div id={b} />)).to.equal(
|
|
53
|
+
`<div id="hello"></div>`
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("should not subscribe properties to signals", async () => {
|
|
59
|
+
const a = signal(0);
|
|
60
|
+
const b = signal("hi");
|
|
61
|
+
// @ts-ignore-next-line
|
|
62
|
+
expect(renderToString(<p id={b}>{a}</p>)).to.equal(`<p id="hi">0</p>`);
|
|
63
|
+
expect(() => {
|
|
64
|
+
a.value = 1;
|
|
65
|
+
}).not.to.throw();
|
|
66
|
+
expect(() => {
|
|
67
|
+
b.value = "bye";
|
|
68
|
+
}).not.to.throw();
|
|
69
|
+
await sleep(10);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("should not subscribe Components to Signals", () => {
|
|
73
|
+
let s = signal(0);
|
|
74
|
+
function App() {
|
|
75
|
+
return <p>{s.value}</p>;
|
|
76
|
+
}
|
|
77
|
+
expect(renderToString(<App />)).to.equal(`<p>0</p>`);
|
|
78
|
+
expect(() => {
|
|
79
|
+
s.value++;
|
|
80
|
+
}).not.to.throw();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("should allow re-rendering signals multiple times", () => {
|
|
84
|
+
const a = signal(0);
|
|
85
|
+
const b = signal("hi");
|
|
86
|
+
|
|
87
|
+
// @ts-ignore-next-line
|
|
88
|
+
expect(renderToString(<p id={b}>{a}</p>)).to.equal(`<p id="hi">0</p>`);
|
|
89
|
+
|
|
90
|
+
a.value++;
|
|
91
|
+
b.value = "bye";
|
|
92
|
+
|
|
93
|
+
// @ts-ignore-next-line
|
|
94
|
+
expect(renderToString(<p id={b}>{a}</p>)).to.equal(`<p id="bye">1</p>`);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("should render computed signals", () => {
|
|
98
|
+
function App() {
|
|
99
|
+
const name = useSignal("Bob");
|
|
100
|
+
const greeting = useComputed(() => `Hello ${name}!`);
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<div>
|
|
104
|
+
{/* @ts-ignore-next-line */}
|
|
105
|
+
<input value={name} />
|
|
106
|
+
<h1>{greeting}</h1>
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
expect(renderToString(<App />)).to.equal(
|
|
112
|
+
`<div><input value="Bob" /><h1>Hello Bob!</h1></div>`
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("should render updated values for mutated computed signals", () => {
|
|
117
|
+
function App() {
|
|
118
|
+
const name = useSignal("Bob");
|
|
119
|
+
const greeting = useComputed(() => `Hello ${name}!`);
|
|
120
|
+
|
|
121
|
+
name.value = "Alice";
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<div>
|
|
125
|
+
{/* @ts-ignore-next-line */}
|
|
126
|
+
<input value={name} />
|
|
127
|
+
<h1>{greeting}</h1>
|
|
128
|
+
</div>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
expect(renderToString(<App />)).to.equal(
|
|
133
|
+
`<div><input value="Alice" /><h1>Hello Alice!</h1></div>`
|
|
134
|
+
);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("should allow signal mutation during rendering", () => {
|
|
138
|
+
function App() {
|
|
139
|
+
const b = useSignal(0);
|
|
140
|
+
return (
|
|
141
|
+
<div>
|
|
142
|
+
{b.value}
|
|
143
|
+
{++b.value}
|
|
144
|
+
{++b.value}
|
|
145
|
+
</div>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
expect(renderToString(<App />)).to.equal(`<div>012</div>`);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
});
|