@preact/signals 1.2.1 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +6 -2
- 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.module.js.map +1 -1
- package/package.json +12 -2
- package/test/exports.test.tsx +0 -18
- package/test/index.test.tsx +0 -682
- package/test/ssr.test.tsx +0 -151
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @preact/signals
|
|
2
2
|
|
|
3
|
+
## 1.2.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#535](https://github.com/preactjs/signals/pull/535) [`58befba`](https://github.com/preactjs/signals/commit/58befba577d02c5cac5292fda0a599f9708e908b) Thanks [@jviide](https://github.com/jviide)! - Publish packages with provenance statements
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`d846def`](https://github.com/preactjs/signals/commit/d846defaf6e64f0236e2b91247e5f94a35f29cbc), [`cb6bdab`](https://github.com/preactjs/signals/commit/cb6bdabbd31b27f8435c7976089fa276da6bfb7a), [`d846def`](https://github.com/preactjs/signals/commit/d846defaf6e64f0236e2b91247e5f94a35f29cbc), [`d846def`](https://github.com/preactjs/signals/commit/d846defaf6e64f0236e2b91247e5f94a35f29cbc), [`d846def`](https://github.com/preactjs/signals/commit/d846defaf6e64f0236e2b91247e5f94a35f29cbc)]:
|
|
10
|
+
- @preact/signals-core@1.6.0
|
|
11
|
+
|
|
12
|
+
## 1.2.2
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- [#415](https://github.com/preactjs/signals/pull/415) [`79efe32`](https://github.com/preactjs/signals/commit/79efe32437784a2f7583fc727f9f99324289d11d) Thanks [@prinsss](https://github.com/prinsss)! - Fix error when using `useSignal` with UMD builds of `@preact/signals`.
|
|
17
|
+
|
|
3
18
|
## 1.2.1
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -51,15 +51,19 @@ function CounterValue() {
|
|
|
51
51
|
|
|
52
52
|
### Hooks
|
|
53
53
|
|
|
54
|
-
If you need to instantiate new signals inside your components, you can use the `useSignal`
|
|
54
|
+
If you need to instantiate new signals or create new side effects on signal changes inside your components, you can use the `useSignal`, `useComputed` and `useSignalEffect` hooks.
|
|
55
55
|
|
|
56
56
|
```js
|
|
57
|
-
import { useSignal, useComputed } from "@preact/signals";
|
|
57
|
+
import { useSignal, useComputed, useSignalEffect } from "@preact/signals";
|
|
58
58
|
|
|
59
59
|
function Counter() {
|
|
60
60
|
const count = useSignal(0);
|
|
61
61
|
const double = useComputed(() => count.value * 2);
|
|
62
62
|
|
|
63
|
+
useSignalEffect(() => {
|
|
64
|
+
console.log(`Value: ${count.value}, value x 2 = ${double.value}`);
|
|
65
|
+
});
|
|
66
|
+
|
|
63
67
|
return (
|
|
64
68
|
<button onClick={() => count.value++}>
|
|
65
69
|
Value: {count.value}, value x 2 = {double.value}
|
package/dist/signals.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signals.js","sources":["../src/index.ts"],"sourcesContent":["import { options, Component, isValidElement } from \"preact\";\nimport { useRef, useMemo, useEffect } from \"preact/hooks\";\nimport {\n\tsignal,\n\tcomputed,\n\tbatch,\n\teffect,\n\tSignal,\n\ttype ReadonlySignal,\n\tuntracked,\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 {\n\tsignal,\n\tcomputed,\n\tbatch,\n\teffect,\n\tSignal,\n\ttype ReadonlySignal,\n\tuntracked,\n};\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 SignalValue(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\tthis._updater!._callback = () => {\n\t\t\tif (isValidElement(s.peek()) || this.base?.nodeType !== 3) {\n\t\t\t\tthis._updateFlags |= HAS_PENDING_UPDATE;\n\t\t\t\tthis.setState({});\n\t\t\t\treturn;\n\t\t\t}\n\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}\nSignalValue.displayName = \"_st\";\n\nObject.defineProperties(Signal.prototype, {\n\tconstructor: { configurable: true, value: undefined },\n\ttype: { configurable: true, value: SignalValue },\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 || type === 9)\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(() => callback.current());\n\t}, []);\n}\n\n/**\n * @todo Determine which Reactive implementation we'll be using.\n * @internal\n */\n// export function useReactive<T extends object>(value: T): Reactive<T> {\n// \treturn useMemo(() => reactive<T>(value), []);\n// }\n\n/**\n * @internal\n * Update a Reactive's using the properties of an object or other Reactive.\n * Also works for Signals.\n * @example\n * // Update a Reactive with Object.assign()-like syntax:\n * const r = reactive({ name: \"Alice\" });\n * update(r, { name: \"Bob\" });\n * update(r, { age: 42 }); // property 'age' does not exist in type '{ name?: string }'\n * update(r, 2); // '2' has no properties in common with '{ name?: string }'\n * console.log(r.name.value); // \"Bob\"\n *\n * @example\n * // Update a Reactive with the properties of another Reactive:\n * const A = reactive({ name: \"Alice\" });\n * const B = reactive({ name: \"Bob\", age: 42 });\n * update(A, B);\n * console.log(`${A.name} is ${A.age}`); // \"Bob is 42\"\n *\n * @example\n * // Update a signal with assign()-like syntax:\n * const s = signal(42);\n * update(s, \"hi\"); // Argument type 'string' not assignable to type 'number'\n * update(s, {}); // Argument type '{}' not assignable to type 'number'\n * update(s, 43);\n * console.log(s.value); // 43\n *\n * @param obj The Reactive or Signal to be updated\n * @param update The value, Signal, object or Reactive to update `obj` to match\n * @param overwrite If `true`, any properties `obj` missing from `update` are set to `undefined`\n */\n/*\nexport function update<T extends SignalOrReactive>(\n\tobj: T,\n\tupdate: Partial<Unwrap<T>>,\n\toverwrite = false\n) {\n\tif (obj instanceof Signal) {\n\t\tobj.value = peekValue(update);\n\t} else {\n\t\tfor (let i in update) {\n\t\t\tif (i in obj) {\n\t\t\t\tobj[i].value = peekValue(update[i]);\n\t\t\t} else {\n\t\t\t\tlet sig = signal(peekValue(update[i]));\n\t\t\t\tsig[KEY] = i;\n\t\t\t\tobj[i] = sig;\n\t\t\t}\n\t\t}\n\t\tif (overwrite) {\n\t\t\tfor (let i in obj) {\n\t\t\t\tif (!(i in update)) {\n\t\t\t\t\tobj[i].value = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n*/\n"],"names":["currentComponent","finishUpdate","preact","require","hooks","signalsCore","hook","hookName","hookFn","options","bind","setCurrentUpdater","updater","_start","SignalValue","_ref","_this","this","data","currentSignal","useSignal","value","s","useMemo","v","__v","__","__c","_updateFlags","_updater","_callback","_this$base","isValidElement","peek","base","nodeType","setState","computed","displayName","Object","defineProperties","Signal","prototype","constructor","configurable","undefined","type","props","get","__b","old","vnode","signalProps","i","__np","component","update","effect","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","exports","batch","untracked","useComputed","compute","$compute","useRef","current","useSignalEffect","cb","callback","useEffect"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"signals.js","sources":["../src/index.ts"],"sourcesContent":["import { options, Component, isValidElement } from \"preact\";\nimport { useRef, useMemo, useEffect } from \"preact/hooks\";\nimport {\n\tsignal,\n\tcomputed,\n\tbatch,\n\teffect,\n\tSignal,\n\ttype ReadonlySignal,\n\tuntracked,\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 {\n\tsignal,\n\tcomputed,\n\tbatch,\n\teffect,\n\tSignal,\n\ttype ReadonlySignal,\n\tuntracked,\n};\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 SignalValue(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\tthis._updater!._callback = () => {\n\t\t\tif (isValidElement(s.peek()) || this.base?.nodeType !== 3) {\n\t\t\t\tthis._updateFlags |= HAS_PENDING_UPDATE;\n\t\t\t\tthis.setState({});\n\t\t\t\treturn;\n\t\t\t}\n\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}\nSignalValue.displayName = \"_st\";\n\nObject.defineProperties(Signal.prototype, {\n\tconstructor: { configurable: true, value: undefined },\n\ttype: { configurable: true, value: SignalValue },\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 || type === 9)\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(() => callback.current());\n\t}, []);\n}\n\n/**\n * @todo Determine which Reactive implementation we'll be using.\n * @internal\n */\n// export function useReactive<T extends object>(value: T): Reactive<T> {\n// \treturn useMemo(() => reactive<T>(value), []);\n// }\n\n/**\n * @internal\n * Update a Reactive's using the properties of an object or other Reactive.\n * Also works for Signals.\n * @example\n * // Update a Reactive with Object.assign()-like syntax:\n * const r = reactive({ name: \"Alice\" });\n * update(r, { name: \"Bob\" });\n * update(r, { age: 42 }); // property 'age' does not exist in type '{ name?: string }'\n * update(r, 2); // '2' has no properties in common with '{ name?: string }'\n * console.log(r.name.value); // \"Bob\"\n *\n * @example\n * // Update a Reactive with the properties of another Reactive:\n * const A = reactive({ name: \"Alice\" });\n * const B = reactive({ name: \"Bob\", age: 42 });\n * update(A, B);\n * console.log(`${A.name} is ${A.age}`); // \"Bob is 42\"\n *\n * @example\n * // Update a signal with assign()-like syntax:\n * const s = signal(42);\n * update(s, \"hi\"); // Argument type 'string' not assignable to type 'number'\n * update(s, {}); // Argument type '{}' not assignable to type 'number'\n * update(s, 43);\n * console.log(s.value); // 43\n *\n * @param obj The Reactive or Signal to be updated\n * @param update The value, Signal, object or Reactive to update `obj` to match\n * @param overwrite If `true`, any properties `obj` missing from `update` are set to `undefined`\n */\n/*\nexport function update<T extends SignalOrReactive>(\n\tobj: T,\n\tupdate: Partial<Unwrap<T>>,\n\toverwrite = false\n) {\n\tif (obj instanceof Signal) {\n\t\tobj.value = peekValue(update);\n\t} else {\n\t\tfor (let i in update) {\n\t\t\tif (i in obj) {\n\t\t\t\tobj[i].value = peekValue(update[i]);\n\t\t\t} else {\n\t\t\t\tlet sig = signal(peekValue(update[i]));\n\t\t\t\tsig[KEY] = i;\n\t\t\t\tobj[i] = sig;\n\t\t\t}\n\t\t}\n\t\tif (overwrite) {\n\t\t\tfor (let i in obj) {\n\t\t\t\tif (!(i in update)) {\n\t\t\t\t\tobj[i].value = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n*/\n"],"names":["currentComponent","finishUpdate","preact","require","hooks","signalsCore","hook","hookName","hookFn","options","bind","setCurrentUpdater","updater","_start","SignalValue","_ref","_this","this","data","currentSignal","useSignal","value","s","useMemo","v","__v","__","__c","_updateFlags","_updater","_callback","_this$base","isValidElement","peek","base","nodeType","setState","computed","displayName","Object","defineProperties","Signal","prototype","constructor","configurable","undefined","type","props","get","__b","old","vnode","signalProps","i","__np","component","update","effect","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","untracked","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,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.
|
|
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.preactHooks,n.preactSignalsCore)}(this,function(n,i,r,t){var e,f;function o(n,r){i.options[n]=r.bind(null,i.options[n]||function(){})}function u(n){if(f)f();f=n&&n.S()}function a(n){var e=this,f=n.data,o=useSignal(f);o.value=f;var u=r.useMemo(function(){var n=e.__v;while(n=n.__)if(n.__c){n.__c.__$f|=4;break}e.__$u.c=function(){var n;if(!i.isValidElement(u.peek())&&3===(null==(n=e.base)?void 0:n.nodeType))e.base.data=u.peek();else{e.__$f|=1;e.setState({})}};return t.computed(function(){var n=o.value.value;return 0===n?0:!0===n?"":n||""})},[]);return u.value}a.displayName="_st";Object.defineProperties(t.Signal.prototype,{constructor:{configurable:!0,value:void 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,e=i.props;for(var f in e)if("children"!==f){var o=e[f];if(o instanceof t.Signal){if(!r)i.__np=r={};r[f]=o;e[f]=o.peek()}}}n(i)});o("__r",function(n,i){u();var r,f=i.__c;if(f){f.__$f&=-2;if(void 0===(r=f.__$u))f.__$u=r=function(n){var i;t.effect(function(){i=this});i.c=function(){f.__$f|=1;f.setState({})};return i}()}e=f;u(r);n(i)});o("__e",function(n,i,r,t){u();e=void 0;n(i,r,t)});o("diffed",function(n,i){u();e=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 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 l=o[s],d=t[s];if(void 0===l){l=c(r,s,d,f);o[s]=l}else l.o(d,f)}}}n(i)});function c(n,i,r,e){var f=i in n&&void 0===n.ownerSVGElement,o=t.signal(r);return{o:function(n,i){o.value=n;e=i},d:t.effect(function(){var r=o.value.value;if(e[i]!==r){e[i]=r;if(f)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 e in t){var f=t[e];if(f)f.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||9===t)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 e in n)if("__source"!==e&&n[e]!==this.props[e])return!0;for(var f in this.props)if(!(f in n))return!0;return!1};function useSignal(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.untracked=t.untracked;n.useComputed=function(n){var i=r.useRef(n);i.current=n;e.__$f|=4;return r.useMemo(function(){return t.computed(function(){return i.current()})},[])};n.useSignal=useSignal;n.useSignalEffect=function(n){var i=r.useRef(n);i.current=n;r.useEffect(function(){return t.effect(function(){return 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, isValidElement } from \"preact\";\nimport { useRef, useMemo, useEffect } from \"preact/hooks\";\nimport {\n\tsignal,\n\tcomputed,\n\tbatch,\n\teffect,\n\tSignal,\n\ttype ReadonlySignal,\n\tuntracked,\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 {\n\tsignal,\n\tcomputed,\n\tbatch,\n\teffect,\n\tSignal,\n\ttype ReadonlySignal,\n\tuntracked,\n};\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 SignalValue(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\tthis._updater!._callback = () => {\n\t\t\tif (isValidElement(s.peek()) || this.base?.nodeType !== 3) {\n\t\t\t\tthis._updateFlags |= HAS_PENDING_UPDATE;\n\t\t\t\tthis.setState({});\n\t\t\t\treturn;\n\t\t\t}\n\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}\nSignalValue.displayName = \"_st\";\n\nObject.defineProperties(Signal.prototype, {\n\tconstructor: { configurable: true, value: undefined },\n\ttype: { configurable: true, value: SignalValue },\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 || type === 9)\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(() => callback.current());\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","preactSignalsCore","this","signalsCore","currentComponent","finishUpdate","hook","hookName","hookFn","options","bind","setCurrentUpdater","updater","_start","SignalValue","_ref","_this","data","currentSignal","useSignal","value","s","useMemo","v","__v","__","__c","_updateFlags","_updater","_callback","_this$base","isValidElement","peek","base","nodeType","setState","computed","displayName","Object","defineProperties","Signal","prototype","constructor","configurable","undefined","type","props","get","__b","old","vnode","signalProps","i","__np","component","update","effect","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","batch","untracked","useComputed","compute","$compute","useRef","current","useSignalEffect","cb","callback","useEffect"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"signals.min.js","sources":["../src/index.ts"],"sourcesContent":["import { options, Component, isValidElement } from \"preact\";\nimport { useRef, useMemo, useEffect } from \"preact/hooks\";\nimport {\n\tsignal,\n\tcomputed,\n\tbatch,\n\teffect,\n\tSignal,\n\ttype ReadonlySignal,\n\tuntracked,\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 {\n\tsignal,\n\tcomputed,\n\tbatch,\n\teffect,\n\tSignal,\n\ttype ReadonlySignal,\n\tuntracked,\n};\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 SignalValue(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\tthis._updater!._callback = () => {\n\t\t\tif (isValidElement(s.peek()) || this.base?.nodeType !== 3) {\n\t\t\t\tthis._updateFlags |= HAS_PENDING_UPDATE;\n\t\t\t\tthis.setState({});\n\t\t\t\treturn;\n\t\t\t}\n\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}\nSignalValue.displayName = \"_st\";\n\nObject.defineProperties(Signal.prototype, {\n\tconstructor: { configurable: true, value: undefined },\n\ttype: { configurable: true, value: SignalValue },\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 || type === 9)\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(() => callback.current());\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","preactHooks","preactSignalsCore","this","hooks","signalsCore","currentComponent","finishUpdate","hook","hookName","hookFn","options","bind","setCurrentUpdater","updater","_start","SignalValue","_ref","_this","data","currentSignal","useSignal","value","s","useMemo","v","__v","__","__c","_updateFlags","_updater","_callback","_this$base","isValidElement","peek","base","nodeType","setState","computed","displayName","Object","defineProperties","Signal","prototype","constructor","configurable","undefined","type","props","get","__b","old","vnode","signalProps","i","__np","component","update","effect","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","untracked","useComputed","compute","$compute","useRef","current","useSignalEffect","cb","callback","useEffect"],"mappings":"CA+BA,SAAAA,EAAAC,GAAA,iBAAAC,SAAA,oBAAAC,OAAAF,EAAAC,QAAAE,QAAA,UAAAA,QAAA,gBAAAA,QAAA,yBAAA,mBAAAC,QAAAA,OAAAC,IAAAD,OAAA,CAAA,UAAA,SAAA,eAAA,wBAAAJ,GAAAA,GAAAD,EAAA,oBAAAO,WAAAA,WAAAP,GAAAQ,MAAAC,cAAA,CAAA,EAAAT,EAAAU,OAAAV,EAAAW,YAAAX,EAAAY,kBAAA,CAAA,CAAAC,KAAA,SAAAX,EAAAQ,EAAAI,EAAAC,GAAA,IAUIC,EACAC,EANJ,SAASC,EAA6BC,EAAaC,GAElDC,EAAAA,QAAQF,GAAYC,EAAOE,KAAK,KAAMD,UAAQF,IAAc,WAAS,EACtE,CAKA,SAASI,EAAkBC,GAE1B,GAAIP,EAAcA,IAElBA,EAAeO,GAAWA,EAAQC,GACnC,CAwBA,SAASC,EAAWC,GAAqD,IAAAC,EAAxBf,KAAAgB,EAAIF,EAAJE,KAK1CC,EAAgBC,UAAUF,GAChCC,EAAcE,MAAQH,EAEtB,IAAMI,EAAIC,EAAAA,QAAQ,WAEjB,IAAIC,EAAIP,EAAKQ,IACb,MAAQD,EAAIA,EAAEE,GACb,GAAIF,EAAEG,IAAK,CACVH,EAAEG,IAAIC,MArDY,EAsDlB,KACA,CAGFX,EAAKY,KAAUC,EAAY,WAAK,IAAAC,EAC/B,IAAIC,EAAcA,eAACV,EAAEW,SAAmC,KAAf,OAATF,EAAAd,EAAKiB,WAAI,EAATH,EAAWI,UAM1ClB,EAAKiB,KAAchB,KAAOI,EAAEW,WAN7B,CACChB,EAAKW,MA9DkB,EA+DvBX,EAAKmB,SAAS,CAAE,EAEhB,CAGF,EAEA,OAAOC,EAAAA,SAAS,WACf,IACIf,EADOH,EAAcE,MACZA,MACb,OAAa,IAANC,EAAU,GAAU,IAANA,EAAa,GAAKA,GAAK,EAC7C,EACD,EAAG,IAEH,OAAOA,EAAED,KACV,CACAN,EAAYuB,YAAc,MAE1BC,OAAOC,iBAAiBC,SAAOC,UAAW,CACzCC,YAAa,CAAEC,cAAc,EAAMvB,WAAOwB,GAC1CC,KAAM,CAAEF,cAAc,EAAMvB,MAAON,GACnCgC,MAAO,CACNH,cAAc,EACdI,IAAG,WACF,MAAO,CAAE9B,KAAMhB,KAChB,GAKD+C,IAAK,CAAEL,cAAc,EAAMvB,MAAO,KAInCd,QAAwB,SAAC2C,EAAKC,GAC7B,GAA0B,iBAAfA,EAAML,KAAmB,CACnC,IAAIM,EAEAL,EAAQI,EAAMJ,MAClB,IAAK,IAAIM,KAAKN,EACb,GAAU,aAANM,EAAJ,CAEA,IAAIhC,EAAQ0B,EAAMM,GAClB,GAAIhC,aAAiBoB,EAAAA,OAAQ,CAC5B,IAAKW,EAAaD,EAAMG,KAAOF,EAAc,CAAA,EAC7CA,EAAYC,GAAKhC,EACjB0B,EAAMM,GAAKhC,EAAMY,MACjB,CAPqB,CASvB,CAEDiB,EAAIC,EACL,GAGA5C,QAA0B,SAAC2C,EAAKC,GAC/BvC,IAEA,IAAIC,EAEA0C,EAAYJ,EAAMxB,IACtB,GAAI4B,EAAW,CACdA,EAAU3B,OAAgB,EAG1B,QAAgBiB,KADhBhC,EAAU0C,EAAU1B,MAEnB0B,EAAU1B,KAAWhB,EA7GxB,SAAuB2C,GACtB,IAAI3C,EACJ4C,SAAO,WACN5C,EAAUX,IACX,GACAW,EAAQiB,EAwGuC,WAC5CyB,EAAU3B,MAlIa,EAmIvB2B,EAAUnB,SAAS,CAAE,EACtB,EA1GF,OAAOvB,CACR,CAsGkC6C,EAKhC,CAEDrD,EAAmBkD,EACnB3C,EAAkBC,GAClBqC,EAAIC,EACL,GAGA5C,EAAI,MAA2B,SAAC2C,EAAKS,EAAOR,EAAOS,GAClDhD,IACAP,OAAmBwC,EACnBK,EAAIS,EAAOR,EAAOS,EACnB,GAGArD,WAA0B,SAAC2C,EAAKC,GAC/BvC,IACAP,OAAmBwC,EAEnB,IAAIgB,EAIJ,GAA0B,iBAAfV,EAAML,OAAsBe,EAAMV,EAAMW,KAAiB,CACnE,IAAIf,EAAQI,EAAMG,KACdS,EAAgBZ,EAAMJ,MAC1B,GAAIA,EAAO,CACV,IAAIiB,EAAWH,EAAII,EACnB,GAAID,EACH,IAAK,IAAIE,KAAQF,EAAU,CAC1B,IAAInD,EAAUmD,EAASE,GACvB,QAAgBrB,IAAZhC,KAA2BqD,KAAQnB,GAAQ,CAC9ClC,EAAQsD,IAERH,EAASE,QAAQrB,CACjB,CACD,MAGDgB,EAAII,EADJD,EAAW,CAAE,EAGd,IAAK,IAAIE,KAAQnB,EAAO,CACvB,IAAIlC,EAAUmD,EAASE,GACnBE,EAASrB,EAAMmB,GACnB,QAAgBrB,IAAZhC,EAAuB,CAC1BA,EAAUwD,EAAkBR,EAAKK,EAAME,EAAQL,GAC/CC,EAASE,GAAQrD,CACjB,MACAA,EAAQyD,EAAQF,EAAQL,EAEzB,CACD,CACD,CACDb,EAAIC,EACL,GAEA,SAASkB,EACRR,EACAK,EACAK,EACAxB,GAEA,IAAMyB,EACLN,KAAQL,QAIgBhB,IAAxBgB,EAAIY,gBAECC,EAAeN,EAAAA,OAAOG,GAC5B,MAAO,CACND,EAAS,SAACK,EAAmBC,GAC5BF,EAAarD,MAAQsD,EACrB5B,EAAQ6B,CACT,EACAT,EAAUV,EAAAA,OAAO,WAChB,IAAMpC,EAAQqD,EAAarD,MAAMA,MAEjC,GAAI0B,EAAMmB,KAAU7C,EAApB,CACA0B,EAAMmB,GAAQ7C,EACd,GAAImD,EAEHX,EAAIK,GAAQ7C,OACFA,GAAAA,EACVwC,EAAIgB,aAAaX,EAAM7C,QAEvBwC,EAAIiB,gBAAgBZ,EAPrBnB,CASD,GAEF,CAGAxC,YAA2B,SAAC2C,EAAKC,GAChC,GAA0B,iBAAfA,EAAML,KAAmB,CACnC,IAAIe,EAAMV,EAAMW,IAEhB,GAAID,EAAK,CACR,IAAMG,EAAWH,EAAII,EACrB,GAAID,EAAU,CACbH,EAAII,OAAYpB,EAChB,IAAK,IAAIqB,KAAQF,EAAU,CAC1B,IAAInD,EAAUmD,EAASE,GACvB,GAAIrD,EAASA,EAAQsD,GACrB,CACD,CACD,CACD,KAAM,CACN,IAAIZ,EAAYJ,EAAMxB,IACtB,GAAI4B,EAAW,CACd,IAAM1C,EAAU0C,EAAU1B,KAC1B,GAAIhB,EAAS,CACZ0C,EAAU1B,UAAWgB,EACrBhC,EAAQsD,GACR,CACD,CACD,CACDjB,EAAIC,EACL,GAGA5C,EAAI,MAAoB,SAAC2C,EAAKK,EAAWwB,EAAOjC,GAC/C,GAAIA,EAAO,GAAc,IAATA,EACdS,EAAiC3B,MAhQb,EAiQtBsB,EAAIK,EAAWwB,EAAOjC,EACvB,GAMAkC,EAAAA,UAAUtC,UAAUuC,sBAAwB,SAE3ClC,EACAmC,GAGA,IAAMrE,EAAUX,KAAK2B,KA0BrB,KAzBmBhB,QAAgCgC,IAArBhC,EAAQsE,GA9QjB,EAuSAjF,KAAK0B,MAA+B,SAIzD,GAAyBwD,EAArBlF,KAAK0B,KAAsD,OAAO,EAGtE,IAAK,IAAIyB,KAAK6B,EAAO,OAAO,EAG5B,IAAK,IAAI7B,KAAKN,EACb,GAAU,aAANM,GAAoBN,EAAMM,KAAOnD,KAAK6C,MAAMM,GAAI,OAAO,EAE5D,IAAK,IAAIA,UAAUN,MAAO,KAAMM,KAAKN,GAAQ,OAAO,EAGpD,QACD,EAEgB,SAAA3B,UAAaC,GAC5B,OAAOE,UAAQ,WAAM,OAAA6C,SAAU/C,EAAM,EAAE,GACxC,CAgBA9B,EAAAkD,OAAArC,EAAAqC,OAAAlD,EAAA8F,MAAAjF,EAAAiF,MAAA9F,EAAA8C,SAAAjC,EAAAiC,SAAA9C,EAAAkE,OAAArD,EAAAqD,OAAAlE,EAAA6E,OAAAhE,EAAAgE,OAAA7E,EAAA+F,UAAAlF,EAAAkF,UAAA/F,EAAAgG,YAdgB,SAAeC,GAC9B,IAAMC,EAAWC,EAAMA,OAACF,GACxBC,EAASE,QAAUH,EAClBnF,EAAwCuB,MAjUpB,EAkUrB,OAAOL,EAAOA,QAAC,WAAM,OAAAc,EAAQA,SAAI,WAAA,OAAMoD,EAASE,SAAS,EAAC,EAAE,GAC7D,EASApG,EAAA6B,UAAAA,UAAA7B,EAAAqG,gBAPM,SAA0BC,GAC/B,IAAMC,EAAWJ,EAAMA,OAACG,GACxBC,EAASH,QAAUE,EAEnBE,EAAAA,UAAU,WACT,OAAOtC,EAAAA,OAAO,WAAM,OAAAqC,EAASH,SAAS,EACvC,EAAG,GACJ,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signals.module.js","sources":["../src/index.ts"],"sourcesContent":["import { options, Component, isValidElement } from \"preact\";\nimport { useRef, useMemo, useEffect } from \"preact/hooks\";\nimport {\n\tsignal,\n\tcomputed,\n\tbatch,\n\teffect,\n\tSignal,\n\ttype ReadonlySignal,\n\tuntracked,\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 {\n\tsignal,\n\tcomputed,\n\tbatch,\n\teffect,\n\tSignal,\n\ttype ReadonlySignal,\n\tuntracked,\n};\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 SignalValue(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\tthis._updater!._callback = () => {\n\t\t\tif (isValidElement(s.peek()) || this.base?.nodeType !== 3) {\n\t\t\t\tthis._updateFlags |= HAS_PENDING_UPDATE;\n\t\t\t\tthis.setState({});\n\t\t\t\treturn;\n\t\t\t}\n\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}\nSignalValue.displayName = \"_st\";\n\nObject.defineProperties(Signal.prototype, {\n\tconstructor: { configurable: true, value: undefined },\n\ttype: { configurable: true, value: SignalValue },\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 || type === 9)\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(() => callback.current());\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","isValidElement","useMemo","useRef","useEffect","Signal","computed","signal","effect","batch","untracked","currentComponent","finishUpdate","hook","hookName","hookFn","bind","setCurrentUpdater","updater","_start","SignalValue","_ref","_this","this","data","currentSignal","useSignal","value","s","v","__v","__","__c","_updateFlags","_updater","_callback","_this$base","peek","base","nodeType","setState","displayName","Object","defineProperties","prototype","constructor","configurable","undefined","type","props","get","__b","old","vnode","signalProps","i","__np","component","update","createUpdater","error","oldVNode","dom","__e","renderedProps","updaters","_updaters","prop","_dispose","createPropUpdater","_update","propSignal","setAsProperty","ownerSVGElement","changeSignal","newSignal","newProps","setAttribute","removeAttribute","index","shouldComponentUpdate","state","_sources","useComputed","compute","$compute","current","useSignalEffect","cb","callback"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"signals.module.js","sources":["../src/index.ts"],"sourcesContent":["import { options, Component, isValidElement } from \"preact\";\nimport { useRef, useMemo, useEffect } from \"preact/hooks\";\nimport {\n\tsignal,\n\tcomputed,\n\tbatch,\n\teffect,\n\tSignal,\n\ttype ReadonlySignal,\n\tuntracked,\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 {\n\tsignal,\n\tcomputed,\n\tbatch,\n\teffect,\n\tSignal,\n\ttype ReadonlySignal,\n\tuntracked,\n};\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 SignalValue(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\tthis._updater!._callback = () => {\n\t\t\tif (isValidElement(s.peek()) || this.base?.nodeType !== 3) {\n\t\t\t\tthis._updateFlags |= HAS_PENDING_UPDATE;\n\t\t\t\tthis.setState({});\n\t\t\t\treturn;\n\t\t\t}\n\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}\nSignalValue.displayName = \"_st\";\n\nObject.defineProperties(Signal.prototype, {\n\tconstructor: { configurable: true, value: undefined },\n\ttype: { configurable: true, value: SignalValue },\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 || type === 9)\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(() => callback.current());\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","isValidElement","useMemo","useRef","useEffect","Signal","computed","signal","effect","batch","untracked","currentComponent","finishUpdate","hook","hookName","hookFn","bind","setCurrentUpdater","updater","_start","SignalValue","_ref","_this","this","data","currentSignal","useSignal","value","s","v","__v","__","__c","_updateFlags","_updater","_callback","_this$base","peek","base","nodeType","setState","displayName","Object","defineProperties","prototype","constructor","configurable","undefined","type","props","get","__b","old","vnode","signalProps","i","__np","component","update","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.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Manage state with style in Preact",
|
|
6
6
|
"keywords": [],
|
|
@@ -33,8 +33,15 @@
|
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
"mangle": "../../mangle.json",
|
|
36
|
+
"files": [
|
|
37
|
+
"src",
|
|
38
|
+
"dist",
|
|
39
|
+
"CHANGELOG.md",
|
|
40
|
+
"LICENSE",
|
|
41
|
+
"README.md"
|
|
42
|
+
],
|
|
36
43
|
"dependencies": {
|
|
37
|
-
"@preact/signals-core": "^1.
|
|
44
|
+
"@preact/signals-core": "^1.6.0"
|
|
38
45
|
},
|
|
39
46
|
"peerDependencies": {
|
|
40
47
|
"preact": "10.x"
|
|
@@ -43,5 +50,8 @@
|
|
|
43
50
|
"preact": "10.9.0",
|
|
44
51
|
"preact-render-to-string": "^5.2.5"
|
|
45
52
|
},
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"provenance": true
|
|
55
|
+
},
|
|
46
56
|
"scripts": {}
|
|
47
57
|
}
|
package/test/exports.test.tsx
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import * as core from "@preact/signals-core";
|
|
2
|
-
import * as adapter from "@preact/signals";
|
|
3
|
-
|
|
4
|
-
describe("@preact/signals", () => {
|
|
5
|
-
describe("exports", () => {
|
|
6
|
-
it("should re-export core", () => {
|
|
7
|
-
const keys = Object.keys(core);
|
|
8
|
-
|
|
9
|
-
for (let i = 0; i < keys.length; i++) {
|
|
10
|
-
const key = keys[i];
|
|
11
|
-
expect(key in adapter).to.equal(
|
|
12
|
-
true,
|
|
13
|
-
`"${key}" is not exported from preact adapter`
|
|
14
|
-
);
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
});
|
package/test/index.test.tsx
DELETED
|
@@ -1,682 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
computed,
|
|
3
|
-
useComputed,
|
|
4
|
-
useSignalEffect,
|
|
5
|
-
Signal,
|
|
6
|
-
signal,
|
|
7
|
-
} from "@preact/signals";
|
|
8
|
-
import { createElement, createRef, render, createContext } from "preact";
|
|
9
|
-
import { useContext, useState } from "preact/hooks";
|
|
10
|
-
import { setupRerender, act } from "preact/test-utils";
|
|
11
|
-
|
|
12
|
-
const sleep = (ms?: number) => new Promise(r => setTimeout(r, ms));
|
|
13
|
-
|
|
14
|
-
describe("@preact/signals", () => {
|
|
15
|
-
let scratch: HTMLDivElement;
|
|
16
|
-
let rerender: () => void;
|
|
17
|
-
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
scratch = document.createElement("div");
|
|
20
|
-
rerender = setupRerender();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
afterEach(() => {
|
|
24
|
-
render(null, scratch);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
describe("inheritance", () => {
|
|
28
|
-
it("should have signals inherit from Signal", () => {
|
|
29
|
-
expect(signal(0)).to.be.instanceof(Signal);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("should have computed inherit from Signal", () => {
|
|
33
|
-
expect(computed(() => 0)).to.be.instanceof(Signal);
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
describe("SignalValue bindings", () => {
|
|
38
|
-
it("should render text without signals", () => {
|
|
39
|
-
render(<span>test</span>, scratch);
|
|
40
|
-
const span = scratch.firstChild;
|
|
41
|
-
const text = span?.firstChild;
|
|
42
|
-
expect(text).to.have.property("data", "test");
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it("should render Signals as SignalValue", () => {
|
|
46
|
-
const sig = signal("test");
|
|
47
|
-
render(<span>{sig}</span>, scratch);
|
|
48
|
-
const span = scratch.firstChild;
|
|
49
|
-
expect(span).to.have.property("firstChild").that.is.an.instanceOf(Text);
|
|
50
|
-
const text = span?.firstChild;
|
|
51
|
-
expect(text).to.have.property("data", "test");
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it("should update Signal-based SignalValue (no parent component)", () => {
|
|
55
|
-
const sig = signal("test");
|
|
56
|
-
render(<span>{sig}</span>, scratch);
|
|
57
|
-
|
|
58
|
-
const text = scratch.firstChild!.firstChild!;
|
|
59
|
-
expect(text).to.have.property("data", "test");
|
|
60
|
-
|
|
61
|
-
sig.value = "changed";
|
|
62
|
-
|
|
63
|
-
// should not remount/replace SignalValue
|
|
64
|
-
expect(scratch.firstChild!.firstChild!).to.equal(text);
|
|
65
|
-
// should update the text in-place
|
|
66
|
-
expect(text).to.have.property("data", "changed");
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("should update Signal-based SignalValue (in a parent component)", async () => {
|
|
70
|
-
const sig = signal("test");
|
|
71
|
-
const spy = sinon.spy();
|
|
72
|
-
function App({ x }: { x: typeof sig }) {
|
|
73
|
-
spy();
|
|
74
|
-
return <span>{x}</span>;
|
|
75
|
-
}
|
|
76
|
-
render(<App x={sig} />, scratch);
|
|
77
|
-
spy.resetHistory();
|
|
78
|
-
|
|
79
|
-
const text = scratch.firstChild!.firstChild!;
|
|
80
|
-
expect(text).to.have.property("data", "test");
|
|
81
|
-
|
|
82
|
-
sig.value = "changed";
|
|
83
|
-
|
|
84
|
-
// should not remount/replace SignalValue
|
|
85
|
-
expect(scratch.firstChild!.firstChild!).to.equal(text);
|
|
86
|
-
// should update the text in-place
|
|
87
|
-
expect(text).to.have.property("data", "changed");
|
|
88
|
-
|
|
89
|
-
await sleep();
|
|
90
|
-
expect(spy).not.to.have.been.called;
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it("should support swapping Signals in SignalValue positions", async () => {
|
|
94
|
-
const sig = signal("test");
|
|
95
|
-
const spy = sinon.spy();
|
|
96
|
-
function App({ x }: { x: typeof sig }) {
|
|
97
|
-
spy();
|
|
98
|
-
return <span>{x}</span>;
|
|
99
|
-
}
|
|
100
|
-
render(<App x={sig} />, scratch);
|
|
101
|
-
spy.resetHistory();
|
|
102
|
-
|
|
103
|
-
const text = scratch.firstChild!.firstChild!;
|
|
104
|
-
expect(text).to.have.property("data", "test");
|
|
105
|
-
|
|
106
|
-
const sig2 = signal("different");
|
|
107
|
-
render(<App x={sig2} />, scratch);
|
|
108
|
-
expect(spy).to.have.been.called;
|
|
109
|
-
spy.resetHistory();
|
|
110
|
-
|
|
111
|
-
// should not remount/replace SignalValue
|
|
112
|
-
expect(scratch.firstChild!.firstChild!).to.equal(text);
|
|
113
|
-
// should update the text in-place
|
|
114
|
-
expect(text).to.have.property("data", "different");
|
|
115
|
-
|
|
116
|
-
await sleep();
|
|
117
|
-
expect(spy).not.to.have.been.called;
|
|
118
|
-
|
|
119
|
-
sig.value = "changed old signal";
|
|
120
|
-
|
|
121
|
-
await sleep();
|
|
122
|
-
expect(spy).not.to.have.been.called;
|
|
123
|
-
// the text should _not_ have changed:
|
|
124
|
-
expect(text).to.have.property("data", "different");
|
|
125
|
-
|
|
126
|
-
sig2.value = "changed";
|
|
127
|
-
|
|
128
|
-
expect(scratch.firstChild!.firstChild!).to.equal(text);
|
|
129
|
-
expect(text).to.have.property("data", "changed");
|
|
130
|
-
|
|
131
|
-
await sleep();
|
|
132
|
-
expect(spy).not.to.have.been.called;
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it("should support rendering JSX in SignalValue positions", async () => {
|
|
136
|
-
const sig = signal(<span>test</span>);
|
|
137
|
-
function App({ x }: { x: typeof sig }) {
|
|
138
|
-
return <span>{x}</span>;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
render(<App x={sig} />, scratch);
|
|
142
|
-
|
|
143
|
-
const text = scratch.firstChild!.firstChild!;
|
|
144
|
-
|
|
145
|
-
expect(text.textContent).to.equal("test");
|
|
146
|
-
expect(text).to.be.an.instanceOf(HTMLSpanElement);
|
|
147
|
-
expect(text).to.have.property("firstChild").that.is.an.instanceOf(Text);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it("JSX in SignalValue should be reactive", async () => {
|
|
151
|
-
const sig = signal(<span>test</span>);
|
|
152
|
-
const spy = sinon.spy();
|
|
153
|
-
function App({ x }: { x: typeof sig }) {
|
|
154
|
-
spy();
|
|
155
|
-
return <span>{x}</span>;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
render(<App x={sig} />, scratch);
|
|
159
|
-
expect(spy).to.have.been.calledOnce;
|
|
160
|
-
spy.resetHistory();
|
|
161
|
-
|
|
162
|
-
const text = scratch.firstChild!.firstChild!;
|
|
163
|
-
|
|
164
|
-
expect(text.textContent).to.equal("test");
|
|
165
|
-
expect(text).to.be.an.instanceOf(HTMLSpanElement);
|
|
166
|
-
expect(text).to.have.property("firstChild").that.is.an.instanceOf(Text);
|
|
167
|
-
|
|
168
|
-
sig.value = <div>a</div>;
|
|
169
|
-
|
|
170
|
-
expect(spy).not.to.have.been.calledOnce;
|
|
171
|
-
|
|
172
|
-
rerender();
|
|
173
|
-
scratch.firstChild!.firstChild!.textContent!.should.equal("a");
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it("should support swapping between JSX and string in SignalValue positions", async () => {
|
|
177
|
-
const sig = signal<JSX.Element | string>(<span>test</span>);
|
|
178
|
-
function App({ x }: { x: typeof sig }) {
|
|
179
|
-
return <span>{x}</span>;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
render(<App x={sig} />, scratch);
|
|
183
|
-
|
|
184
|
-
let text = scratch.firstChild!.firstChild!;
|
|
185
|
-
|
|
186
|
-
expect(text.textContent).to.equal("test");
|
|
187
|
-
expect(text).to.be.an.instanceOf(HTMLSpanElement);
|
|
188
|
-
expect(text).to.have.property("firstChild").that.is.an.instanceOf(Text);
|
|
189
|
-
sig.value = "a";
|
|
190
|
-
rerender();
|
|
191
|
-
text = scratch.firstChild!.firstChild!;
|
|
192
|
-
expect(text.nodeType).to.equal(Node.TEXT_NODE);
|
|
193
|
-
expect(text.textContent).to.equal("a");
|
|
194
|
-
|
|
195
|
-
sig.value = "b";
|
|
196
|
-
expect(text.textContent).to.equal("b");
|
|
197
|
-
|
|
198
|
-
sig.value = <div>c</div>;
|
|
199
|
-
rerender();
|
|
200
|
-
await sleep();
|
|
201
|
-
text = scratch.firstChild!.firstChild!;
|
|
202
|
-
|
|
203
|
-
expect(text).to.be.an.instanceOf(HTMLDivElement);
|
|
204
|
-
expect(text.textContent).to.equal("c");
|
|
205
|
-
sig.value = <span>d</span>;
|
|
206
|
-
rerender();
|
|
207
|
-
await sleep();
|
|
208
|
-
|
|
209
|
-
text = scratch.firstChild!.firstChild!;
|
|
210
|
-
expect(text).to.be.an.instanceOf(HTMLSpanElement);
|
|
211
|
-
expect(text.textContent).to.equal("d");
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
describe("Component bindings", () => {
|
|
216
|
-
it("should subscribe to signals", () => {
|
|
217
|
-
const sig = signal("foo");
|
|
218
|
-
|
|
219
|
-
function App() {
|
|
220
|
-
const value = sig.value;
|
|
221
|
-
return <p>{value}</p>;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
render(<App />, scratch);
|
|
225
|
-
expect(scratch.textContent).to.equal("foo");
|
|
226
|
-
|
|
227
|
-
sig.value = "bar";
|
|
228
|
-
rerender();
|
|
229
|
-
expect(scratch.textContent).to.equal("bar");
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
it("should activate signal accessed in render", () => {
|
|
233
|
-
const sig = signal(null);
|
|
234
|
-
|
|
235
|
-
function App() {
|
|
236
|
-
const arr = useComputed(() => {
|
|
237
|
-
// trigger read
|
|
238
|
-
sig.value;
|
|
239
|
-
|
|
240
|
-
return [];
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
const str = arr.value.join(", ");
|
|
244
|
-
return <p>{str}</p>;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const fn = () => render(<App />, scratch);
|
|
248
|
-
expect(fn).not.to.throw;
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
it("should not subscribe to child signals", () => {
|
|
252
|
-
const sig = signal("foo");
|
|
253
|
-
|
|
254
|
-
function Child() {
|
|
255
|
-
const value = sig.value;
|
|
256
|
-
return <p>{value}</p>;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
const spy = sinon.spy();
|
|
260
|
-
function App() {
|
|
261
|
-
spy();
|
|
262
|
-
return <Child />;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
render(<App />, scratch);
|
|
266
|
-
expect(scratch.textContent).to.equal("foo");
|
|
267
|
-
|
|
268
|
-
sig.value = "bar";
|
|
269
|
-
rerender();
|
|
270
|
-
expect(spy).to.be.calledOnce;
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
it("should not subscribe to computed signals only created and not used", () => {
|
|
274
|
-
const sig = signal(0);
|
|
275
|
-
const childSpy = sinon.spy();
|
|
276
|
-
const parentSpy = sinon.spy();
|
|
277
|
-
|
|
278
|
-
function Child({ num }: { num: Signal<number> }) {
|
|
279
|
-
childSpy();
|
|
280
|
-
return <p>{num.value}</p>;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function Parent({ num }: { num: Signal<number> }) {
|
|
284
|
-
parentSpy();
|
|
285
|
-
const sig2 = useComputed(() => num.value + 1);
|
|
286
|
-
return <Child num={sig2} />;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
render(<Parent num={sig} />, scratch);
|
|
290
|
-
expect(scratch.innerHTML).to.equal("<p>1</p>");
|
|
291
|
-
expect(parentSpy).to.be.calledOnce;
|
|
292
|
-
expect(childSpy).to.be.calledOnce;
|
|
293
|
-
|
|
294
|
-
sig.value += 1;
|
|
295
|
-
rerender();
|
|
296
|
-
expect(scratch.innerHTML).to.equal("<p>2</p>");
|
|
297
|
-
expect(parentSpy).to.be.calledOnce;
|
|
298
|
-
expect(childSpy).to.be.calledTwice;
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
it("should properly subscribe and unsubscribe to conditionally rendered computed signals ", () => {
|
|
302
|
-
const computedDep = signal(0);
|
|
303
|
-
const renderComputed = signal(true);
|
|
304
|
-
const renderSpy = sinon.spy();
|
|
305
|
-
const computer = sinon.spy(() => computedDep.value + 1);
|
|
306
|
-
|
|
307
|
-
function App() {
|
|
308
|
-
renderSpy();
|
|
309
|
-
const computed = useComputed(computer);
|
|
310
|
-
return renderComputed.value ? <p>{computed.value}</p> : null;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
render(<App />, scratch);
|
|
314
|
-
expect(scratch.innerHTML).to.equal("<p>1</p>");
|
|
315
|
-
expect(renderSpy).to.be.calledOnce;
|
|
316
|
-
expect(computer).to.be.calledOnce;
|
|
317
|
-
|
|
318
|
-
computedDep.value += 1;
|
|
319
|
-
rerender();
|
|
320
|
-
expect(scratch.innerHTML).to.equal("<p>2</p>");
|
|
321
|
-
expect(renderSpy).to.be.calledTwice;
|
|
322
|
-
expect(computer).to.be.calledTwice;
|
|
323
|
-
|
|
324
|
-
renderComputed.value = false;
|
|
325
|
-
rerender();
|
|
326
|
-
expect(scratch.innerHTML).to.equal("");
|
|
327
|
-
expect(renderSpy).to.be.calledThrice;
|
|
328
|
-
expect(computer).to.be.calledTwice;
|
|
329
|
-
|
|
330
|
-
computedDep.value += 1;
|
|
331
|
-
rerender();
|
|
332
|
-
expect(scratch.innerHTML).to.equal("");
|
|
333
|
-
expect(renderSpy).to.be.calledThrice; // Should not be called again
|
|
334
|
-
expect(computer).to.be.calledTwice; // Should not be called again
|
|
335
|
-
});
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
describe("prop bindings", () => {
|
|
339
|
-
it("should set the initial value of the checked property", () => {
|
|
340
|
-
const s = signal(true);
|
|
341
|
-
// @ts-ignore
|
|
342
|
-
render(<input checked={s} />, scratch);
|
|
343
|
-
|
|
344
|
-
expect(scratch.firstChild).to.have.property("checked", true);
|
|
345
|
-
expect(s.value).to.equal(true);
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
it("should update the checked property on change", () => {
|
|
349
|
-
const s = signal(true);
|
|
350
|
-
// @ts-ignore
|
|
351
|
-
render(<input checked={s} />, scratch);
|
|
352
|
-
|
|
353
|
-
expect(scratch.firstChild).to.have.property("checked", true);
|
|
354
|
-
|
|
355
|
-
s.value = false;
|
|
356
|
-
|
|
357
|
-
expect(scratch.firstChild).to.have.property("checked", false);
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
it("should update props without re-rendering", async () => {
|
|
361
|
-
const s = signal("initial");
|
|
362
|
-
const spy = sinon.spy();
|
|
363
|
-
function Wrap() {
|
|
364
|
-
spy();
|
|
365
|
-
// @ts-ignore
|
|
366
|
-
return <input value={s} />;
|
|
367
|
-
}
|
|
368
|
-
render(<Wrap />, scratch);
|
|
369
|
-
spy.resetHistory();
|
|
370
|
-
|
|
371
|
-
expect(scratch.firstChild).to.have.property("value", "initial");
|
|
372
|
-
|
|
373
|
-
s.value = "updated";
|
|
374
|
-
|
|
375
|
-
expect(scratch.firstChild).to.have.property("value", "updated");
|
|
376
|
-
|
|
377
|
-
// ensure the component was never re-rendered: (even after a tick)
|
|
378
|
-
await sleep();
|
|
379
|
-
expect(spy).not.to.have.been.called;
|
|
380
|
-
|
|
381
|
-
s.value = "second update";
|
|
382
|
-
|
|
383
|
-
expect(scratch.firstChild).to.have.property("value", "second update");
|
|
384
|
-
|
|
385
|
-
// ensure the component was never re-rendered: (even after a tick)
|
|
386
|
-
await sleep();
|
|
387
|
-
expect(spy).not.to.have.been.called;
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
it("should set and update string style property", async () => {
|
|
391
|
-
const style = signal("left: 10px");
|
|
392
|
-
const spy = sinon.spy();
|
|
393
|
-
function Wrap() {
|
|
394
|
-
spy();
|
|
395
|
-
// @ts-ignore
|
|
396
|
-
return <div style={style} />;
|
|
397
|
-
}
|
|
398
|
-
render(<Wrap />, scratch);
|
|
399
|
-
spy.resetHistory();
|
|
400
|
-
|
|
401
|
-
const div = scratch.firstChild as HTMLDivElement;
|
|
402
|
-
|
|
403
|
-
expect(div.style).to.have.property("left", "10px");
|
|
404
|
-
|
|
405
|
-
// ensure the component was never re-rendered: (even after a tick)
|
|
406
|
-
await sleep();
|
|
407
|
-
expect(spy).not.to.have.been.called;
|
|
408
|
-
|
|
409
|
-
style.value = "left: 20px;";
|
|
410
|
-
|
|
411
|
-
expect(div.style).to.have.property("left", "20px");
|
|
412
|
-
|
|
413
|
-
// ensure the component was never re-rendered: (even after a tick)
|
|
414
|
-
await sleep();
|
|
415
|
-
expect(spy).not.to.have.been.called;
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
it("should set updated signal prop values at most once", async () => {
|
|
419
|
-
const s = signal("initial");
|
|
420
|
-
const spy = sinon.spy();
|
|
421
|
-
function Wrap() {
|
|
422
|
-
spy();
|
|
423
|
-
// @ts-ignore
|
|
424
|
-
return <span ariaLabel={s} ariaDescription={s.value} />;
|
|
425
|
-
}
|
|
426
|
-
render(<Wrap />, scratch);
|
|
427
|
-
spy.resetHistory();
|
|
428
|
-
|
|
429
|
-
const span = scratch.firstElementChild as HTMLSpanElement;
|
|
430
|
-
const ariaLabel = sinon.spy();
|
|
431
|
-
Object.defineProperty(span, "ariaLabel", {
|
|
432
|
-
set: ariaLabel,
|
|
433
|
-
});
|
|
434
|
-
const ariaDescription = sinon.spy();
|
|
435
|
-
Object.defineProperty(span, "ariaDescription", {
|
|
436
|
-
set: ariaDescription,
|
|
437
|
-
});
|
|
438
|
-
|
|
439
|
-
act(() => {
|
|
440
|
-
s.value = "updated";
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
expect(spy).to.have.been.calledOnce;
|
|
444
|
-
|
|
445
|
-
expect(ariaLabel).to.have.been.calledOnce;
|
|
446
|
-
expect(ariaLabel).to.have.been.calledWith("updated");
|
|
447
|
-
ariaLabel.resetHistory();
|
|
448
|
-
|
|
449
|
-
expect(ariaDescription).to.have.been.calledOnce;
|
|
450
|
-
expect(ariaDescription).to.have.been.calledWith("updated");
|
|
451
|
-
ariaDescription.resetHistory();
|
|
452
|
-
|
|
453
|
-
// ensure the component was never re-rendered: (even after a tick)
|
|
454
|
-
await sleep();
|
|
455
|
-
|
|
456
|
-
expect(ariaLabel).not.to.have.been.called;
|
|
457
|
-
expect(ariaDescription).not.to.have.been.called;
|
|
458
|
-
|
|
459
|
-
act(() => {
|
|
460
|
-
s.value = "second update";
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
expect(ariaLabel).to.have.been.calledOnce;
|
|
464
|
-
expect(ariaLabel).to.have.been.calledWith("second update");
|
|
465
|
-
ariaLabel.resetHistory();
|
|
466
|
-
|
|
467
|
-
expect(ariaDescription).to.have.been.calledOnce;
|
|
468
|
-
expect(ariaDescription).to.have.been.calledWith("second update");
|
|
469
|
-
ariaDescription.resetHistory();
|
|
470
|
-
|
|
471
|
-
// ensure the component was never re-rendered: (even after a tick)
|
|
472
|
-
await sleep();
|
|
473
|
-
|
|
474
|
-
expect(ariaLabel).not.to.have.been.called;
|
|
475
|
-
expect(ariaDescription).not.to.have.been.called;
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
it("should set SVG values", async () => {
|
|
479
|
-
const s = signal("scale(1 1)");
|
|
480
|
-
|
|
481
|
-
function App() {
|
|
482
|
-
return (
|
|
483
|
-
<svg>
|
|
484
|
-
<line
|
|
485
|
-
// @ts-ignore
|
|
486
|
-
transform={s}
|
|
487
|
-
/>
|
|
488
|
-
</svg>
|
|
489
|
-
);
|
|
490
|
-
}
|
|
491
|
-
render(<App />, scratch);
|
|
492
|
-
|
|
493
|
-
act(() => {
|
|
494
|
-
// This should not crash
|
|
495
|
-
s.value = "scale(1, 2)";
|
|
496
|
-
});
|
|
497
|
-
});
|
|
498
|
-
});
|
|
499
|
-
|
|
500
|
-
describe("hooks mixed with signals", () => {
|
|
501
|
-
it("signals should not stop context from propagating", () => {
|
|
502
|
-
const ctx = createContext({ test: "should-not-exist" });
|
|
503
|
-
let update: any;
|
|
504
|
-
|
|
505
|
-
function Provider(props: any) {
|
|
506
|
-
const [test, setTest] = useState("foo");
|
|
507
|
-
update = setTest;
|
|
508
|
-
return <ctx.Provider value={{ test }}>{props.children}</ctx.Provider>;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
const s = signal("baz");
|
|
512
|
-
function Test() {
|
|
513
|
-
const value = useContext(ctx);
|
|
514
|
-
return (
|
|
515
|
-
<p>
|
|
516
|
-
{value.test} {s.value}
|
|
517
|
-
</p>
|
|
518
|
-
);
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
function App() {
|
|
522
|
-
return (
|
|
523
|
-
<Provider>
|
|
524
|
-
<Test />
|
|
525
|
-
</Provider>
|
|
526
|
-
);
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
render(<App />, scratch);
|
|
530
|
-
|
|
531
|
-
expect(scratch.innerHTML).to.equal("<p>foo baz</p>");
|
|
532
|
-
act(() => {
|
|
533
|
-
update("bar");
|
|
534
|
-
});
|
|
535
|
-
expect(scratch.innerHTML).to.equal("<p>bar baz</p>");
|
|
536
|
-
});
|
|
537
|
-
});
|
|
538
|
-
|
|
539
|
-
describe("useSignalEffect()", () => {
|
|
540
|
-
it("should be invoked after commit", async () => {
|
|
541
|
-
const ref = createRef();
|
|
542
|
-
const sig = signal("foo");
|
|
543
|
-
const spy = sinon.spy();
|
|
544
|
-
let count = 0;
|
|
545
|
-
|
|
546
|
-
function App() {
|
|
547
|
-
useSignalEffect(() =>
|
|
548
|
-
spy(
|
|
549
|
-
sig.value,
|
|
550
|
-
ref.current,
|
|
551
|
-
ref.current.getAttribute("data-render-id")
|
|
552
|
-
)
|
|
553
|
-
);
|
|
554
|
-
return (
|
|
555
|
-
<p ref={ref} data-render-id={count++}>
|
|
556
|
-
{sig.value}
|
|
557
|
-
</p>
|
|
558
|
-
);
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
act(() => {
|
|
562
|
-
render(<App />, scratch);
|
|
563
|
-
});
|
|
564
|
-
expect(scratch.textContent).to.equal("foo");
|
|
565
|
-
// expect(spy).not.to.have.been.called;
|
|
566
|
-
await sleep(1);
|
|
567
|
-
expect(spy).to.have.been.calledOnceWith(
|
|
568
|
-
"foo",
|
|
569
|
-
scratch.firstElementChild,
|
|
570
|
-
"0"
|
|
571
|
-
);
|
|
572
|
-
|
|
573
|
-
spy.resetHistory();
|
|
574
|
-
|
|
575
|
-
act(() => {
|
|
576
|
-
sig.value = "bar";
|
|
577
|
-
rerender();
|
|
578
|
-
});
|
|
579
|
-
|
|
580
|
-
expect(scratch.textContent).to.equal("bar");
|
|
581
|
-
await sleep(1);
|
|
582
|
-
|
|
583
|
-
// NOTE: Ideally, call should receive "1" as its third argument!
|
|
584
|
-
// The "0" indicates that Preact's DOM mutations hadn't yet been performed when the callback ran.
|
|
585
|
-
// This happens because we do signal-based effect runs after the first, not VDOM.
|
|
586
|
-
// Perhaps we could find a way to defer the callback when it coincides with a render?
|
|
587
|
-
expect(spy).to.have.been.calledOnceWith(
|
|
588
|
-
"bar",
|
|
589
|
-
scratch.firstElementChild,
|
|
590
|
-
"0" // ideally "1" - update if we find a nice way to do so!
|
|
591
|
-
);
|
|
592
|
-
});
|
|
593
|
-
|
|
594
|
-
it("should invoke any returned cleanup function for updates", async () => {
|
|
595
|
-
const ref = createRef();
|
|
596
|
-
const sig = signal("foo");
|
|
597
|
-
const spy = sinon.spy();
|
|
598
|
-
const cleanup = sinon.spy();
|
|
599
|
-
let count = 0;
|
|
600
|
-
|
|
601
|
-
function App() {
|
|
602
|
-
useSignalEffect(() => {
|
|
603
|
-
const id = ref.current.getAttribute("data-render-id");
|
|
604
|
-
const value = sig.value;
|
|
605
|
-
spy(value, ref.current, id);
|
|
606
|
-
return () => cleanup(value, ref.current, id);
|
|
607
|
-
});
|
|
608
|
-
return (
|
|
609
|
-
<p ref={ref} data-render-id={count++}>
|
|
610
|
-
{sig.value}
|
|
611
|
-
</p>
|
|
612
|
-
);
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
render(<App />, scratch);
|
|
616
|
-
|
|
617
|
-
await sleep(1);
|
|
618
|
-
expect(cleanup).not.to.have.been.called;
|
|
619
|
-
expect(spy).to.have.been.calledOnceWith(
|
|
620
|
-
"foo",
|
|
621
|
-
scratch.firstElementChild,
|
|
622
|
-
"0"
|
|
623
|
-
);
|
|
624
|
-
spy.resetHistory();
|
|
625
|
-
|
|
626
|
-
act(() => {
|
|
627
|
-
sig.value = "bar";
|
|
628
|
-
rerender();
|
|
629
|
-
});
|
|
630
|
-
|
|
631
|
-
expect(scratch.textContent).to.equal("bar");
|
|
632
|
-
await sleep(1);
|
|
633
|
-
|
|
634
|
-
const child = scratch.firstElementChild;
|
|
635
|
-
|
|
636
|
-
expect(cleanup).to.have.been.calledOnceWith("foo", child, "0");
|
|
637
|
-
|
|
638
|
-
expect(spy).to.have.been.calledOnceWith(
|
|
639
|
-
"bar",
|
|
640
|
-
child,
|
|
641
|
-
"0" // ideally "1" - update if we find a nice way to do so!
|
|
642
|
-
);
|
|
643
|
-
});
|
|
644
|
-
|
|
645
|
-
it("should invoke any returned cleanup function for unmounts", async () => {
|
|
646
|
-
const ref = createRef();
|
|
647
|
-
const sig = signal("foo");
|
|
648
|
-
const spy = sinon.spy();
|
|
649
|
-
const cleanup = sinon.spy();
|
|
650
|
-
|
|
651
|
-
function App() {
|
|
652
|
-
useSignalEffect(() => {
|
|
653
|
-
const value = sig.value;
|
|
654
|
-
spy(value, ref.current);
|
|
655
|
-
return () => cleanup(value, ref.current);
|
|
656
|
-
});
|
|
657
|
-
return <p ref={ref}>{sig.value}</p>;
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
act(() => {
|
|
661
|
-
render(<App />, scratch);
|
|
662
|
-
});
|
|
663
|
-
|
|
664
|
-
await sleep(1);
|
|
665
|
-
|
|
666
|
-
const child = scratch.firstElementChild;
|
|
667
|
-
|
|
668
|
-
expect(cleanup).not.to.have.been.called;
|
|
669
|
-
expect(spy).to.have.been.calledOnceWith("foo", child);
|
|
670
|
-
spy.resetHistory();
|
|
671
|
-
|
|
672
|
-
act(() => {
|
|
673
|
-
render(null, scratch);
|
|
674
|
-
});
|
|
675
|
-
|
|
676
|
-
await sleep(1);
|
|
677
|
-
|
|
678
|
-
expect(spy).not.to.have.been.called;
|
|
679
|
-
expect(cleanup).to.have.been.calledOnceWith("foo", child);
|
|
680
|
-
});
|
|
681
|
-
});
|
|
682
|
-
});
|
package/test/ssr.test.tsx
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
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("SignalValue 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
|
-
});
|