@preact/signals-react 3.6.1 → 3.6.2
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 +9 -0
- package/package.json +2 -2
- package/runtime/dist/index.d.ts +7 -0
- package/runtime/dist/runtime.js +1 -1
- package/runtime/dist/runtime.js.map +1 -1
- package/runtime/dist/runtime.min.js +1 -1
- package/runtime/dist/runtime.min.js.map +1 -1
- package/runtime/dist/runtime.mjs +1 -1
- package/runtime/dist/runtime.mjs.map +1 -1
- package/runtime/dist/runtime.module.js +1 -1
- package/runtime/dist/runtime.module.js.map +1 -1
- package/runtime/package.json +4 -1
- package/runtime/src/index.ts +14 -0
- package/utils/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# @preact/signals-react
|
|
2
2
|
|
|
3
|
+
## 3.6.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#827](https://github.com/preactjs/signals/pull/827) [`f17889b`](https://github.com/preactjs/signals/commit/f17889b6d46448205d9485b8d5e691fbe05cd404) Thanks [@JoviDeCroock](https://github.com/JoviDeCroock)! - Add mangle entry for \_debugCallback
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`f17889b`](https://github.com/preactjs/signals/commit/f17889b6d46448205d9485b8d5e691fbe05cd404)]:
|
|
10
|
+
- @preact/signals-core@1.12.2
|
|
11
|
+
|
|
3
12
|
## 3.6.1
|
|
4
13
|
|
|
5
14
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@preact/signals-react",
|
|
3
|
-
"version": "3.6.
|
|
3
|
+
"version": "3.6.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Manage state with style in React",
|
|
6
6
|
"keywords": [],
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"README.md"
|
|
62
62
|
],
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@preact/signals-core": "^1.12.
|
|
64
|
+
"@preact/signals-core": "^1.12.2",
|
|
65
65
|
"use-sync-external-store": "^1.2.0"
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
package/runtime/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Signal, ReadonlySignal, SignalOptions, EffectOptions } from "@preact/signals-core";
|
|
2
|
+
import type { SignalsDevToolsAPI } from "../../../debug/src/devtools";
|
|
2
3
|
export declare function wrapJsx<T>(jsx: T): T;
|
|
3
4
|
declare const symDispose: unique symbol;
|
|
4
5
|
interface Effect {
|
|
5
6
|
_sources: object | undefined;
|
|
7
|
+
_debugCallback?: () => void;
|
|
6
8
|
_start(): () => void;
|
|
7
9
|
_callback(): void;
|
|
8
10
|
_dispose(): void;
|
|
@@ -60,4 +62,9 @@ export declare function useSignal<T>(value: T, options?: SignalOptions<T>): Sign
|
|
|
60
62
|
export declare function useSignal<T = undefined>(): Signal<T | undefined>;
|
|
61
63
|
export declare function useComputed<T>(compute: () => T, options?: SignalOptions<T>): ReadonlySignal<T>;
|
|
62
64
|
export declare function useSignalEffect(cb: () => void | (() => void), options?: EffectOptions): void;
|
|
65
|
+
declare global {
|
|
66
|
+
interface Window {
|
|
67
|
+
__PREACT_SIGNALS_DEVTOOLS__: SignalsDevToolsAPI;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
63
70
|
export {};
|
package/runtime/dist/runtime.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var n,t=require("@preact/signals-core"),e=require("react"),r=require("use-sync-external-store/shim/index.js"),i=e.version.split(".").map(Number)[0],u=[],o=Symbol.for(i>=19?"react.transitional.element":"react.element"),f=Symbol.dispose||Symbol.for("Symbol.dispose");function
|
|
1
|
+
var n,t=require("@preact/signals-core"),e=require("react"),r=require("use-sync-external-store/shim/index.js"),i=e.version.split(".").map(Number)[0],u=[],o=Symbol.for(i>=19?"react.transitional.element":"react.element"),f="undefined"!=typeof window&&!!window.__PREACT_SIGNALS_DEVTOOLS__,c=Symbol.dispose||Symbol.for("Symbol.dispose");function s(t,e){var r=e.effect.S();n=e;return a.bind(e,t,r)}function a(t,e){e();n=t}var l,v,d=function(){},p=((l={u:0,effect:{s:void 0,c:function(){},S:function(){return d},d:function(){}},subscribe:function(){return d},getSnapshot:function(){return 0},S:function(){},f:function(){}})[c]=function(){},l),b=Promise.prototype.then.bind(Promise.resolve());function y(){if(!v)v=b(g)}function g(){var t;v=void 0;null==(t=n)||t.f()}var h="undefined"!=typeof window?e.useLayoutEffect:e.useEffect;function m(i){if(void 0===i)i=0;y();var u=e.useRef();if(null==u.current)if("undefined"==typeof window)u.current=p;else u.current=function(e){var r,i,u,o,a=0,l=t.effect(function(){i=this});i.c=function(){a=a+1|0;if(f){var n;null==(n=i.y)||n.call(i)}if(o)o()};return(r={u:e,effect:i,subscribe:function(n){o=n;return function(){a=a+1|0;o=void 0;l()}},getSnapshot:function(){return a},S:function(){if(null!=n){var t=n.u,e=this.u;if(0==t&&0==e||0==t&&1==e){n.f();u=s(void 0,this)}else if(1==t&&0==e||2==t&&0==e);else u=s(n,this)}else u=s(void 0,this)},f:function(){var n=u;u=void 0;null==n||n()}})[c]=function(){this.f()},r}(i);var o=u.current;r.useSyncExternalStore(o.subscribe,o.getSnapshot,o.getSnapshot);o.S();if(0===i)h(g);return o}Object.defineProperties(t.Signal.prototype,{$$typeof:{configurable:!0,value:o},type:{configurable:!0,value:function(n){var t=n.data,e=m(1);try{return t.value}finally{e.f()}}},props:{configurable:!0,get:function(){return{data:this}}},ref:{configurable:!0,value:null}});exports._useSignalsImplementation=m;exports.ensureFinalCleanup=y;exports.useComputed=function(n,r){var i=e.useRef(n);i.current=n;return e.useMemo(function(){return t.computed(function(){return i.current()},r)},u)};exports.useSignal=function(n,r){return e.useMemo(function(){return t.signal(n,r)},u)};exports.useSignalEffect=function(n,r){var i=e.useRef(n);i.current=n;e.useEffect(function(){return t.effect(function(){return i.current()},r)},u)};exports.useSignals=function(n){return m(n)};exports.wrapJsx=function(n){if("function"!=typeof n)return n;else return function(e,r){if("string"==typeof e&&r)for(var i in r){var u=r[i];if("children"!==i&&u instanceof t.Signal)r[i]=u.value}return n.call.apply(n,[n,e,r].concat([].slice.call(arguments,2)))}};//# sourceMappingURL=runtime.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.js","sources":["../src/index.ts"],"sourcesContent":["import {\n\tsignal,\n\tcomputed,\n\teffect,\n\tSignal,\n\tReadonlySignal,\n\tSignalOptions,\n\tEffectOptions,\n} from \"@preact/signals-core\";\nimport {\n\tuseRef,\n\tuseMemo,\n\tuseEffect,\n\tuseLayoutEffect,\n\tversion as reactVersion,\n} from \"react\";\nimport { useSyncExternalStore } from \"use-sync-external-store/shim/index.js\";\n\nconst [major] = reactVersion.split(\".\").map(Number);\nconst Empty = [] as const;\n// V19 https://github.com/facebook/react/blob/346c7d4c43a0717302d446da9e7423a8e28d8996/packages/shared/ReactSymbols.js#L15\n// V18 https://github.com/facebook/react/blob/346c7d4c43a0717302d446da9e7423a8e28d8996/packages/shared/ReactSymbols.js#L15\nconst ReactElemType = Symbol.for(\n\tmajor >= 19 ? \"react.transitional.element\" : \"react.element\"\n);\n\nexport function wrapJsx<T>(jsx: T): T {\n\tif (typeof jsx !== \"function\") return jsx;\n\n\treturn function (type: any, props: any, ...rest: any[]) {\n\t\tif (typeof type === \"string\" && props) {\n\t\t\tfor (let i in props) {\n\t\t\t\tlet v = props[i];\n\t\t\t\tif (i !== \"children\" && v instanceof Signal) {\n\t\t\t\t\tprops[i] = v.value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn jsx.call(jsx, type, props, ...rest);\n\t} as any as T;\n}\n\nconst symDispose: unique symbol =\n\t(Symbol as any).dispose || Symbol.for(\"Symbol.dispose\");\n\ninterface Effect {\n\t_sources: object | undefined;\n\t_start(): () => void;\n\t_callback(): void;\n\t_dispose(): void;\n}\n\n/**\n * Use this flag to represent a bare `useSignals` call that doesn't manually\n * close its effect store and relies on auto-closing when the next useSignals is\n * called or after a microtask\n */\nconst UNMANAGED = 0;\n/**\n * Use this flag to represent a `useSignals` call that is manually closed by a\n * try/finally block in a component's render method. This is the default usage\n * that the react-transform plugin uses.\n */\nconst MANAGED_COMPONENT = 1;\n/**\n * Use this flag to represent a `useSignals` call that is manually closed by a\n * try/finally block in a hook body. This is the default usage that the\n * react-transform plugin uses.\n */\nconst MANAGED_HOOK = 2;\n\n/**\n * An enum defining how this store is used. See the documentation for each enum\n * member for more details.\n * @see {@link UNMANAGED}\n * @see {@link MANAGED_COMPONENT}\n * @see {@link MANAGED_HOOK}\n */\ntype EffectStoreUsage =\n\t| typeof UNMANAGED\n\t| typeof MANAGED_COMPONENT\n\t| typeof MANAGED_HOOK;\n\nexport interface EffectStore {\n\t/**\n\t * An enum defining how this hook is used and whether it is invoked in a\n\t * component's body or hook body. See the comment on `EffectStoreUsage` for\n\t * more details.\n\t */\n\treadonly _usage: EffectStoreUsage;\n\treadonly effect: Effect;\n\tsubscribe(onStoreChange: () => void): () => void;\n\tgetSnapshot(): number;\n\t/** startEffect - begin tracking signals used in this component */\n\t_start(): void;\n\t/** finishEffect - stop tracking the signals used in this component */\n\tf(): void;\n\t[symDispose](): void;\n}\n\nlet currentStore: EffectStore | undefined;\n\nfunction startComponentEffect(\n\tprevStore: EffectStore | undefined,\n\tnextStore: EffectStore\n) {\n\tconst endEffect = nextStore.effect._start();\n\tcurrentStore = nextStore;\n\n\treturn finishComponentEffect.bind(nextStore, prevStore, endEffect);\n}\n\nfunction finishComponentEffect(\n\tthis: EffectStore,\n\tprevStore: EffectStore | undefined,\n\tendEffect: () => void\n) {\n\tendEffect();\n\tcurrentStore = prevStore;\n}\n\n/**\n * A redux-like store whose store value is a positive 32bit integer (a\n * 'version').\n *\n * React subscribes to this store and gets a snapshot of the current 'version',\n * whenever the 'version' changes, we tell React it's time to update the\n * component (call 'onStoreChange').\n *\n * How we achieve this is by creating a binding with an 'effect', when the\n * `effect._callback' is called, we update our store version and tell React to\n * re-render the component ([1] We don't really care when/how React does it).\n *\n * [1]\n * @see https://react.dev/reference/react/useSyncExternalStore\n * @see\n * https://github.com/reactjs/rfcs/blob/main/text/0214-use-sync-external-store.md\n *\n * @param _usage An enum defining how this hook is used and whether it is\n * invoked in a component's body or hook body. See the comment on\n * `EffectStoreUsage` for more details.\n */\nfunction createEffectStore(_usage: EffectStoreUsage): EffectStore {\n\tlet effectInstance!: Effect;\n\tlet endEffect: (() => void) | undefined;\n\tlet version = 0;\n\tlet onChangeNotifyReact: (() => void) | undefined;\n\n\tlet unsubscribe = effect(function (this: Effect) {\n\t\teffectInstance = this;\n\t});\n\teffectInstance._callback = function () {\n\t\tversion = (version + 1) | 0;\n\t\tif (onChangeNotifyReact) onChangeNotifyReact();\n\t};\n\n\treturn {\n\t\t_usage,\n\t\teffect: effectInstance,\n\t\tsubscribe(onStoreChange) {\n\t\t\tonChangeNotifyReact = onStoreChange;\n\n\t\t\treturn function () {\n\t\t\t\t/**\n\t\t\t\t * Rotate to next version when unsubscribing to ensure that components are re-run\n\t\t\t\t * when subscribing again.\n\t\t\t\t *\n\t\t\t\t * In StrictMode, 'memo'-ed components seem to keep a stale snapshot version, so\n\t\t\t\t * don't re-run after subscribing again if the version is the same as last time.\n\t\t\t\t *\n\t\t\t\t * Because we unsubscribe from the effect, the version may not change. We simply\n\t\t\t\t * set a new initial version in case of stale snapshots here.\n\t\t\t\t */\n\t\t\t\tversion = (version + 1) | 0;\n\t\t\t\tonChangeNotifyReact = undefined;\n\t\t\t\tunsubscribe();\n\t\t\t};\n\t\t},\n\t\tgetSnapshot() {\n\t\t\treturn version;\n\t\t},\n\t\t_start() {\n\t\t\t// In general, we want to support two kinds of usages of useSignals:\n\t\t\t//\n\t\t\t// A) Managed: calling useSignals in a component or hook body wrapped in a\n\t\t\t// try/finally (like what the react-transform plugin does)\n\t\t\t//\n\t\t\t// B) Unmanaged: Calling useSignals directly without wrapping in a\n\t\t\t// try/finally\n\t\t\t//\n\t\t\t// For managed, we finish the effect in the finally block of the component\n\t\t\t// or hook body. For unmanaged, we finish the effect in the next\n\t\t\t// useSignals call or after a microtask.\n\t\t\t//\n\t\t\t// There are different tradeoffs which each approach. With managed, using\n\t\t\t// a try/finally ensures that only signals used in the component or hook\n\t\t\t// body are tracked. However, signals accessed in render props are missed\n\t\t\t// because the render prop is invoked in another component that may or may\n\t\t\t// not realize it is rendering signals accessed in the render prop it is\n\t\t\t// given.\n\t\t\t//\n\t\t\t// The other approach is \"unmanaged\": to call useSignals directly without\n\t\t\t// wrapping in a try/finally. This approach is easier to manually write in\n\t\t\t// situations where a build step isn't available but does open up the\n\t\t\t// possibility of catching signals accessed in other code before the\n\t\t\t// effect is closed (e.g. in a layout effect). Most situations where this\n\t\t\t// could happen are generally consider bad patterns or bugs. For example,\n\t\t\t// using a signal in a component and not having a call to `useSignals`\n\t\t\t// would be an bug. Or using a signal in `useLayoutEffect` is generally\n\t\t\t// not recommended since that layout effect won't update when the signals'\n\t\t\t// value change.\n\t\t\t//\n\t\t\t// To support both approaches, we need to track how each invocation of\n\t\t\t// useSignals is used, so we can properly transition between different\n\t\t\t// kinds of usages.\n\t\t\t//\n\t\t\t// The following table shows the different scenarios and how we should\n\t\t\t// handle them.\n\t\t\t//\n\t\t\t// Key:\n\t\t\t// 0 = UNMANAGED\n\t\t\t// 1 = MANAGED_COMPONENT\n\t\t\t// 2 = MANAGED_HOOK\n\t\t\t//\n\t\t\t// Pattern:\n\t\t\t// prev store usage -> this store usage: action to take\n\t\t\t//\n\t\t\t// - 0 -> 0: finish previous effect (unknown to unknown)\n\t\t\t//\n\t\t\t// We don't know how the previous effect was used, so we need to finish\n\t\t\t// it before starting the next effect.\n\t\t\t//\n\t\t\t// - 0 -> 1: finish previous effect\n\t\t\t//\n\t\t\t// Assume previous invocation was another component or hook from another\n\t\t\t// component. Nested component renders (renderToStaticMarkup within a\n\t\t\t// component's render) won't be supported with bare useSignals calls.\n\t\t\t//\n\t\t\t// - 0 -> 2: capture & restore\n\t\t\t//\n\t\t\t// Previous invocation could be a component or a hook. Either way,\n\t\t\t// restore it after our invocation so that it can continue to capture\n\t\t\t// any signals after we exit.\n\t\t\t//\n\t\t\t// - 1 -> 0: Do nothing. Signals already captured by current effect store\n\t\t\t// - 1 -> 1: capture & restore (e.g. component calls renderToStaticMarkup)\n\t\t\t// - 1 -> 2: capture & restore (e.g. hook)\n\t\t\t//\n\t\t\t// - 2 -> 0: Do nothing. Signals already captured by current effect store\n\t\t\t// - 2 -> 1: capture & restore (e.g. hook calls renderToStaticMarkup)\n\t\t\t// - 2 -> 2: capture & restore (e.g. nested hook calls)\n\n\t\t\tif (currentStore == undefined) {\n\t\t\t\tendEffect = startComponentEffect(undefined, this);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst prevUsage = currentStore._usage;\n\t\t\tconst thisUsage = this._usage;\n\n\t\t\tif (\n\t\t\t\t(prevUsage == UNMANAGED && thisUsage == UNMANAGED) || // 0 -> 0\n\t\t\t\t(prevUsage == UNMANAGED && thisUsage == MANAGED_COMPONENT) // 0 -> 1\n\t\t\t) {\n\t\t\t\t// finish previous effect\n\t\t\t\tcurrentStore.f();\n\t\t\t\tendEffect = startComponentEffect(undefined, this);\n\t\t\t} else if (\n\t\t\t\t(prevUsage == MANAGED_COMPONENT && thisUsage == UNMANAGED) || // 1 -> 0\n\t\t\t\t(prevUsage == MANAGED_HOOK && thisUsage == UNMANAGED) // 2 -> 0\n\t\t\t) {\n\t\t\t\t// Do nothing since it'll be captured by current effect store\n\t\t\t} else {\n\t\t\t\t// nested scenarios, so capture and restore the previous effect store\n\t\t\t\tendEffect = startComponentEffect(currentStore, this);\n\t\t\t}\n\t\t},\n\t\tf() {\n\t\t\tconst end = endEffect;\n\t\t\tendEffect = undefined;\n\t\t\tend?.();\n\t\t},\n\t\t[symDispose]() {\n\t\t\tthis.f();\n\t\t},\n\t};\n}\n\nconst noop = () => {};\n\nfunction createEmptyEffectStore(): EffectStore {\n\treturn {\n\t\t_usage: UNMANAGED,\n\t\teffect: {\n\t\t\t_sources: undefined,\n\t\t\t_callback() {},\n\t\t\t_start() {\n\t\t\t\treturn /* endEffect */ noop;\n\t\t\t},\n\t\t\t_dispose() {},\n\t\t},\n\t\tsubscribe() {\n\t\t\treturn /* unsubscribe */ noop;\n\t\t},\n\t\tgetSnapshot() {\n\t\t\treturn 0;\n\t\t},\n\t\t_start() {},\n\t\tf() {},\n\t\t[symDispose]() {},\n\t};\n}\n\nconst emptyEffectStore = createEmptyEffectStore();\n\nconst _queueMicroTask = Promise.prototype.then.bind(Promise.resolve());\n\nlet finalCleanup: Promise<void> | undefined;\nexport function ensureFinalCleanup() {\n\tif (!finalCleanup) {\n\t\tfinalCleanup = _queueMicroTask(cleanupTrailingStore);\n\t}\n}\nfunction cleanupTrailingStore() {\n\tfinalCleanup = undefined;\n\tcurrentStore?.f();\n}\n\nconst useIsomorphicLayoutEffect =\n\ttypeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n\n/**\n * Custom hook to create the effect to track signals used during render and\n * subscribe to changes to rerender the component when the signals change.\n */\nexport function _useSignalsImplementation(\n\t_usage: EffectStoreUsage = UNMANAGED\n): EffectStore {\n\tensureFinalCleanup();\n\n\tconst storeRef = useRef<EffectStore>();\n\tif (storeRef.current == null) {\n\t\tif (typeof window === \"undefined\") {\n\t\t\tstoreRef.current = emptyEffectStore;\n\t\t} else {\n\t\t\tstoreRef.current = createEffectStore(_usage);\n\t\t}\n\t}\n\n\tconst store = storeRef.current;\n\tuseSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);\n\tstore._start();\n\t// note: _usage is a constant here, so conditional is okay\n\tif (_usage === UNMANAGED) useIsomorphicLayoutEffect(cleanupTrailingStore);\n\n\treturn store;\n}\n\n/**\n * A wrapper component that renders a Signal's value directly as a Text node or JSX.\n */\nfunction SignalValue({ data }: { data: Signal }) {\n\tconst store = _useSignalsImplementation(1);\n\ttry {\n\t\treturn data.value;\n\t} finally {\n\t\tstore.f();\n\t}\n}\n\n// Decorate Signals so React renders them as <SignalValue> components.\nObject.defineProperties(Signal.prototype, {\n\t$$typeof: { configurable: true, value: ReactElemType },\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\tref: { configurable: true, value: null },\n});\n\nexport function useSignals(usage?: EffectStoreUsage): EffectStore {\n\treturn _useSignalsImplementation(usage);\n}\n\nexport function useSignal<T>(value: T, options?: SignalOptions<T>): Signal<T>;\nexport function useSignal<T = undefined>(): Signal<T | undefined>;\nexport function useSignal<T>(value?: T, options?: SignalOptions<T>) {\n\treturn useMemo(\n\t\t() => signal<T | undefined>(value, options as SignalOptions),\n\t\tEmpty\n\t);\n}\n\nexport function useComputed<T>(\n\tcompute: () => T,\n\toptions?: SignalOptions<T>\n): ReadonlySignal<T> {\n\tconst $compute = useRef(compute);\n\t$compute.current = compute;\n\treturn useMemo(() => computed<T>(() => $compute.current(), options), Empty);\n}\n\nexport function useSignalEffect(\n\tcb: () => void | (() => void),\n\toptions?: EffectOptions\n) {\n\tconst callback = useRef(cb);\n\tcallback.current = cb;\n\n\tuseEffect(() => {\n\t\treturn effect(function (this: Effect) {\n\t\t\treturn callback.current();\n\t\t}, options);\n\t}, Empty);\n}\n"],"names":["currentStore","major","reactVersion","split","map","Number","Empty","ReactElemType","Symbol","symDispose","dispose","startComponentEffect","prevStore","nextStore","endEffect","effect","_start","finishComponentEffect","bind","_ref2","finalCleanup","noop","emptyEffectStore","_usage","_sources","undefined","_callback","_dispose","subscribe","getSnapshot","f","_queueMicroTask","Promise","prototype","then","resolve","ensureFinalCleanup","cleanupTrailingStore","_currentStore","useIsomorphicLayoutEffect","window","useLayoutEffect","useEffect","_useSignalsImplementation","storeRef","useRef","current","_ref","effectInstance","onChangeNotifyReact","version","unsubscribe","this","onStoreChange","prevUsage","thisUsage","end","createEffectStore","store","useSyncExternalStore","Object","defineProperties","Signal","$$typeof","configurable","value","type","_ref3","data","props","get","ref","exports","useComputed","compute","options","$compute","useMemo","computed","useSignal","signal","useSignalEffect","cb","callback","useSignals","usage","wrapJsx","jsx","i","v","call","apply","concat","slice","arguments"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runtime.js","sources":["../src/index.ts"],"sourcesContent":["import {\n\tsignal,\n\tcomputed,\n\teffect,\n\tSignal,\n\tReadonlySignal,\n\tSignalOptions,\n\tEffectOptions,\n} from \"@preact/signals-core\";\nimport {\n\tuseRef,\n\tuseMemo,\n\tuseEffect,\n\tuseLayoutEffect,\n\tversion as reactVersion,\n} from \"react\";\nimport { useSyncExternalStore } from \"use-sync-external-store/shim/index.js\";\nimport type { SignalsDevToolsAPI } from \"../../../debug/src/devtools\";\n\nconst [major] = reactVersion.split(\".\").map(Number);\nconst Empty = [] as const;\n// V19 https://github.com/facebook/react/blob/346c7d4c43a0717302d446da9e7423a8e28d8996/packages/shared/ReactSymbols.js#L15\n// V18 https://github.com/facebook/react/blob/346c7d4c43a0717302d446da9e7423a8e28d8996/packages/shared/ReactSymbols.js#L15\nconst ReactElemType = Symbol.for(\n\tmajor >= 19 ? \"react.transitional.element\" : \"react.element\"\n);\n\nconst DEVTOOLS_ENABLED =\n\ttypeof window !== \"undefined\" && !!window.__PREACT_SIGNALS_DEVTOOLS__;\n\nexport function wrapJsx<T>(jsx: T): T {\n\tif (typeof jsx !== \"function\") return jsx;\n\n\treturn function (type: any, props: any, ...rest: any[]) {\n\t\tif (typeof type === \"string\" && props) {\n\t\t\tfor (let i in props) {\n\t\t\t\tlet v = props[i];\n\t\t\t\tif (i !== \"children\" && v instanceof Signal) {\n\t\t\t\t\tprops[i] = v.value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn jsx.call(jsx, type, props, ...rest);\n\t} as any as T;\n}\n\nconst symDispose: unique symbol =\n\t(Symbol as any).dispose || Symbol.for(\"Symbol.dispose\");\n\ninterface Effect {\n\t_sources: object | undefined;\n\t_debugCallback?: () => void;\n\t_start(): () => void;\n\t_callback(): void;\n\t_dispose(): void;\n}\n\n/**\n * Use this flag to represent a bare `useSignals` call that doesn't manually\n * close its effect store and relies on auto-closing when the next useSignals is\n * called or after a microtask\n */\nconst UNMANAGED = 0;\n/**\n * Use this flag to represent a `useSignals` call that is manually closed by a\n * try/finally block in a component's render method. This is the default usage\n * that the react-transform plugin uses.\n */\nconst MANAGED_COMPONENT = 1;\n/**\n * Use this flag to represent a `useSignals` call that is manually closed by a\n * try/finally block in a hook body. This is the default usage that the\n * react-transform plugin uses.\n */\nconst MANAGED_HOOK = 2;\n\n/**\n * An enum defining how this store is used. See the documentation for each enum\n * member for more details.\n * @see {@link UNMANAGED}\n * @see {@link MANAGED_COMPONENT}\n * @see {@link MANAGED_HOOK}\n */\ntype EffectStoreUsage =\n\t| typeof UNMANAGED\n\t| typeof MANAGED_COMPONENT\n\t| typeof MANAGED_HOOK;\n\nexport interface EffectStore {\n\t/**\n\t * An enum defining how this hook is used and whether it is invoked in a\n\t * component's body or hook body. See the comment on `EffectStoreUsage` for\n\t * more details.\n\t */\n\treadonly _usage: EffectStoreUsage;\n\treadonly effect: Effect;\n\tsubscribe(onStoreChange: () => void): () => void;\n\tgetSnapshot(): number;\n\t/** startEffect - begin tracking signals used in this component */\n\t_start(): void;\n\t/** finishEffect - stop tracking the signals used in this component */\n\tf(): void;\n\t[symDispose](): void;\n}\n\nlet currentStore: EffectStore | undefined;\n\nfunction startComponentEffect(\n\tprevStore: EffectStore | undefined,\n\tnextStore: EffectStore\n) {\n\tconst endEffect = nextStore.effect._start();\n\tcurrentStore = nextStore;\n\n\treturn finishComponentEffect.bind(nextStore, prevStore, endEffect);\n}\n\nfunction finishComponentEffect(\n\tthis: EffectStore,\n\tprevStore: EffectStore | undefined,\n\tendEffect: () => void\n) {\n\tendEffect();\n\tcurrentStore = prevStore;\n}\n\n/**\n * A redux-like store whose store value is a positive 32bit integer (a\n * 'version').\n *\n * React subscribes to this store and gets a snapshot of the current 'version',\n * whenever the 'version' changes, we tell React it's time to update the\n * component (call 'onStoreChange').\n *\n * How we achieve this is by creating a binding with an 'effect', when the\n * `effect._callback' is called, we update our store version and tell React to\n * re-render the component ([1] We don't really care when/how React does it).\n *\n * [1]\n * @see https://react.dev/reference/react/useSyncExternalStore\n * @see\n * https://github.com/reactjs/rfcs/blob/main/text/0214-use-sync-external-store.md\n *\n * @param _usage An enum defining how this hook is used and whether it is\n * invoked in a component's body or hook body. See the comment on\n * `EffectStoreUsage` for more details.\n */\nfunction createEffectStore(_usage: EffectStoreUsage): EffectStore {\n\tlet effectInstance!: Effect;\n\tlet endEffect: (() => void) | undefined;\n\tlet version = 0;\n\tlet onChangeNotifyReact: (() => void) | undefined;\n\n\tlet unsubscribe = effect(function (this: Effect) {\n\t\teffectInstance = this;\n\t});\n\teffectInstance._callback = function () {\n\t\tversion = (version + 1) | 0;\n\t\tif (DEVTOOLS_ENABLED) {\n\t\t\teffectInstance._debugCallback?.call(effectInstance);\n\t\t}\n\t\tif (onChangeNotifyReact) onChangeNotifyReact();\n\t};\n\n\treturn {\n\t\t_usage,\n\t\teffect: effectInstance,\n\t\tsubscribe(onStoreChange) {\n\t\t\tonChangeNotifyReact = onStoreChange;\n\n\t\t\treturn function () {\n\t\t\t\t/**\n\t\t\t\t * Rotate to next version when unsubscribing to ensure that components are re-run\n\t\t\t\t * when subscribing again.\n\t\t\t\t *\n\t\t\t\t * In StrictMode, 'memo'-ed components seem to keep a stale snapshot version, so\n\t\t\t\t * don't re-run after subscribing again if the version is the same as last time.\n\t\t\t\t *\n\t\t\t\t * Because we unsubscribe from the effect, the version may not change. We simply\n\t\t\t\t * set a new initial version in case of stale snapshots here.\n\t\t\t\t */\n\t\t\t\tversion = (version + 1) | 0;\n\t\t\t\tonChangeNotifyReact = undefined;\n\t\t\t\tunsubscribe();\n\t\t\t};\n\t\t},\n\t\tgetSnapshot() {\n\t\t\treturn version;\n\t\t},\n\t\t_start() {\n\t\t\t// In general, we want to support two kinds of usages of useSignals:\n\t\t\t//\n\t\t\t// A) Managed: calling useSignals in a component or hook body wrapped in a\n\t\t\t// try/finally (like what the react-transform plugin does)\n\t\t\t//\n\t\t\t// B) Unmanaged: Calling useSignals directly without wrapping in a\n\t\t\t// try/finally\n\t\t\t//\n\t\t\t// For managed, we finish the effect in the finally block of the component\n\t\t\t// or hook body. For unmanaged, we finish the effect in the next\n\t\t\t// useSignals call or after a microtask.\n\t\t\t//\n\t\t\t// There are different tradeoffs which each approach. With managed, using\n\t\t\t// a try/finally ensures that only signals used in the component or hook\n\t\t\t// body are tracked. However, signals accessed in render props are missed\n\t\t\t// because the render prop is invoked in another component that may or may\n\t\t\t// not realize it is rendering signals accessed in the render prop it is\n\t\t\t// given.\n\t\t\t//\n\t\t\t// The other approach is \"unmanaged\": to call useSignals directly without\n\t\t\t// wrapping in a try/finally. This approach is easier to manually write in\n\t\t\t// situations where a build step isn't available but does open up the\n\t\t\t// possibility of catching signals accessed in other code before the\n\t\t\t// effect is closed (e.g. in a layout effect). Most situations where this\n\t\t\t// could happen are generally consider bad patterns or bugs. For example,\n\t\t\t// using a signal in a component and not having a call to `useSignals`\n\t\t\t// would be an bug. Or using a signal in `useLayoutEffect` is generally\n\t\t\t// not recommended since that layout effect won't update when the signals'\n\t\t\t// value change.\n\t\t\t//\n\t\t\t// To support both approaches, we need to track how each invocation of\n\t\t\t// useSignals is used, so we can properly transition between different\n\t\t\t// kinds of usages.\n\t\t\t//\n\t\t\t// The following table shows the different scenarios and how we should\n\t\t\t// handle them.\n\t\t\t//\n\t\t\t// Key:\n\t\t\t// 0 = UNMANAGED\n\t\t\t// 1 = MANAGED_COMPONENT\n\t\t\t// 2 = MANAGED_HOOK\n\t\t\t//\n\t\t\t// Pattern:\n\t\t\t// prev store usage -> this store usage: action to take\n\t\t\t//\n\t\t\t// - 0 -> 0: finish previous effect (unknown to unknown)\n\t\t\t//\n\t\t\t// We don't know how the previous effect was used, so we need to finish\n\t\t\t// it before starting the next effect.\n\t\t\t//\n\t\t\t// - 0 -> 1: finish previous effect\n\t\t\t//\n\t\t\t// Assume previous invocation was another component or hook from another\n\t\t\t// component. Nested component renders (renderToStaticMarkup within a\n\t\t\t// component's render) won't be supported with bare useSignals calls.\n\t\t\t//\n\t\t\t// - 0 -> 2: capture & restore\n\t\t\t//\n\t\t\t// Previous invocation could be a component or a hook. Either way,\n\t\t\t// restore it after our invocation so that it can continue to capture\n\t\t\t// any signals after we exit.\n\t\t\t//\n\t\t\t// - 1 -> 0: Do nothing. Signals already captured by current effect store\n\t\t\t// - 1 -> 1: capture & restore (e.g. component calls renderToStaticMarkup)\n\t\t\t// - 1 -> 2: capture & restore (e.g. hook)\n\t\t\t//\n\t\t\t// - 2 -> 0: Do nothing. Signals already captured by current effect store\n\t\t\t// - 2 -> 1: capture & restore (e.g. hook calls renderToStaticMarkup)\n\t\t\t// - 2 -> 2: capture & restore (e.g. nested hook calls)\n\n\t\t\tif (currentStore == undefined) {\n\t\t\t\tendEffect = startComponentEffect(undefined, this);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst prevUsage = currentStore._usage;\n\t\t\tconst thisUsage = this._usage;\n\n\t\t\tif (\n\t\t\t\t(prevUsage == UNMANAGED && thisUsage == UNMANAGED) || // 0 -> 0\n\t\t\t\t(prevUsage == UNMANAGED && thisUsage == MANAGED_COMPONENT) // 0 -> 1\n\t\t\t) {\n\t\t\t\t// finish previous effect\n\t\t\t\tcurrentStore.f();\n\t\t\t\tendEffect = startComponentEffect(undefined, this);\n\t\t\t} else if (\n\t\t\t\t(prevUsage == MANAGED_COMPONENT && thisUsage == UNMANAGED) || // 1 -> 0\n\t\t\t\t(prevUsage == MANAGED_HOOK && thisUsage == UNMANAGED) // 2 -> 0\n\t\t\t) {\n\t\t\t\t// Do nothing since it'll be captured by current effect store\n\t\t\t} else {\n\t\t\t\t// nested scenarios, so capture and restore the previous effect store\n\t\t\t\tendEffect = startComponentEffect(currentStore, this);\n\t\t\t}\n\t\t},\n\t\tf() {\n\t\t\tconst end = endEffect;\n\t\t\tendEffect = undefined;\n\t\t\tend?.();\n\t\t},\n\t\t[symDispose]() {\n\t\t\tthis.f();\n\t\t},\n\t};\n}\n\nconst noop = () => {};\n\nfunction createEmptyEffectStore(): EffectStore {\n\treturn {\n\t\t_usage: UNMANAGED,\n\t\teffect: {\n\t\t\t_sources: undefined,\n\t\t\t_callback() {},\n\t\t\t_start() {\n\t\t\t\treturn /* endEffect */ noop;\n\t\t\t},\n\t\t\t_dispose() {},\n\t\t},\n\t\tsubscribe() {\n\t\t\treturn /* unsubscribe */ noop;\n\t\t},\n\t\tgetSnapshot() {\n\t\t\treturn 0;\n\t\t},\n\t\t_start() {},\n\t\tf() {},\n\t\t[symDispose]() {},\n\t};\n}\n\nconst emptyEffectStore = createEmptyEffectStore();\n\nconst _queueMicroTask = Promise.prototype.then.bind(Promise.resolve());\n\nlet finalCleanup: Promise<void> | undefined;\nexport function ensureFinalCleanup() {\n\tif (!finalCleanup) {\n\t\tfinalCleanup = _queueMicroTask(cleanupTrailingStore);\n\t}\n}\nfunction cleanupTrailingStore() {\n\tfinalCleanup = undefined;\n\tcurrentStore?.f();\n}\n\nconst useIsomorphicLayoutEffect =\n\ttypeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n\n/**\n * Custom hook to create the effect to track signals used during render and\n * subscribe to changes to rerender the component when the signals change.\n */\nexport function _useSignalsImplementation(\n\t_usage: EffectStoreUsage = UNMANAGED\n): EffectStore {\n\tensureFinalCleanup();\n\n\tconst storeRef = useRef<EffectStore>();\n\tif (storeRef.current == null) {\n\t\tif (typeof window === \"undefined\") {\n\t\t\tstoreRef.current = emptyEffectStore;\n\t\t} else {\n\t\t\tstoreRef.current = createEffectStore(_usage);\n\t\t}\n\t}\n\n\tconst store = storeRef.current;\n\tuseSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);\n\tstore._start();\n\t// note: _usage is a constant here, so conditional is okay\n\tif (_usage === UNMANAGED) useIsomorphicLayoutEffect(cleanupTrailingStore);\n\n\treturn store;\n}\n\n/**\n * A wrapper component that renders a Signal's value directly as a Text node or JSX.\n */\nfunction SignalValue({ data }: { data: Signal }) {\n\tconst store = _useSignalsImplementation(1);\n\ttry {\n\t\treturn data.value;\n\t} finally {\n\t\tstore.f();\n\t}\n}\n\n// Decorate Signals so React renders them as <SignalValue> components.\nObject.defineProperties(Signal.prototype, {\n\t$$typeof: { configurable: true, value: ReactElemType },\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\tref: { configurable: true, value: null },\n});\n\nexport function useSignals(usage?: EffectStoreUsage): EffectStore {\n\treturn _useSignalsImplementation(usage);\n}\n\nexport function useSignal<T>(value: T, options?: SignalOptions<T>): Signal<T>;\nexport function useSignal<T = undefined>(): Signal<T | undefined>;\nexport function useSignal<T>(value?: T, options?: SignalOptions<T>) {\n\treturn useMemo(\n\t\t() => signal<T | undefined>(value, options as SignalOptions),\n\t\tEmpty\n\t);\n}\n\nexport function useComputed<T>(\n\tcompute: () => T,\n\toptions?: SignalOptions<T>\n): ReadonlySignal<T> {\n\tconst $compute = useRef(compute);\n\t$compute.current = compute;\n\treturn useMemo(() => computed<T>(() => $compute.current(), options), Empty);\n}\n\nexport function useSignalEffect(\n\tcb: () => void | (() => void),\n\toptions?: EffectOptions\n) {\n\tconst callback = useRef(cb);\n\tcallback.current = cb;\n\n\tuseEffect(() => {\n\t\treturn effect(function (this: Effect) {\n\t\t\treturn callback.current();\n\t\t}, options);\n\t}, Empty);\n}\n\ndeclare global {\n\tinterface Window {\n\t\t__PREACT_SIGNALS_DEVTOOLS__: SignalsDevToolsAPI;\n\t}\n}\n"],"names":["currentStore","signalsCore","require","react","index_js","major","reactVersion","version","split","map","Number","Empty","ReactElemType","Symbol","DEVTOOLS_ENABLED","window","__PREACT_SIGNALS_DEVTOOLS__","symDispose","dispose","startComponentEffect","prevStore","nextStore","endEffect","effect","_start","finishComponentEffect","bind","_ref2","finalCleanup","noop","emptyEffectStore","_usage","_sources","undefined","_callback","_dispose","subscribe","getSnapshot","f","_queueMicroTask","Promise","prototype","then","resolve","ensureFinalCleanup","cleanupTrailingStore","_currentStore","useIsomorphicLayoutEffect","useLayoutEffect","useEffect","_useSignalsImplementation","storeRef","useRef","current","_ref","effectInstance","onChangeNotifyReact","unsubscribe","this","_effectInstance$_debu","_debugCallback","call","onStoreChange","prevUsage","thisUsage","end","createEffectStore","store","useSyncExternalStore","Object","defineProperties","Signal","$$typeof","configurable","value","type","_ref3","data","props","get","ref","exports","useComputed","compute","options","$compute","useMemo","computed","useSignal","signal","useSignalEffect","cb","callback","useSignals","usage","wrapJsx","jsx","i","v","apply","concat","slice","arguments"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(n,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("@preact/signals-core"),require("react"),require("use-sync-external-store/shim/index.js")):"function"==typeof define&&define.amd?define(["exports","@preact/signals-core","react","use-sync-external-store/shim/index.js"],e):e((n||self).reactSignalsRuntime={},n.preactSignalsCore,n.react,n.index_js)}(this,function(n,e,t,i){var r,
|
|
1
|
+
!function(n,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("@preact/signals-core"),require("react"),require("use-sync-external-store/shim/index.js")):"function"==typeof define&&define.amd?define(["exports","@preact/signals-core","react","use-sync-external-store/shim/index.js"],e):e((n||self).reactSignalsRuntime={},n.preactSignalsCore,n.react,n.index_js)}(this,function(n,e,t,i){var r,o=t.version.split(".").map(Number)[0],u=[],f=Symbol.for(o>=19?"react.transitional.element":"react.element"),c="undefined"!=typeof window&&!!window.__PREACT_SIGNALS_DEVTOOLS__,s=Symbol.dispose||Symbol.for("Symbol.dispose");function a(n,e){var t=e.effect.S();r=e;return l.bind(e,n,t)}function l(n,e){e();r=n}var d,v,p=function(){},y=((d={o:0,effect:{s:void 0,c:function(){},S:function(){return p},d:function(){}},subscribe:function(){return p},getSnapshot:function(){return 0},S:function(){},f:function(){}})[s]=function(){},d),b=Promise.prototype.then.bind(Promise.resolve());function h(){if(!v)v=b(g)}function g(){var n;v=void 0;null==(n=r)||n.f()}var m="undefined"!=typeof window?t.useLayoutEffect:t.useEffect;function w(n){if(void 0===n)n=0;h();var o=t.useRef();if(null==o.current)if("undefined"==typeof window)o.current=y;else o.current=function(n){var t,i,o,u,f=0,l=e.effect(function(){i=this});i.c=function(){f=f+1|0;if(c){var n;null==(n=i.y)||n.call(i)}if(u)u()};return(t={o:n,effect:i,subscribe:function(n){u=n;return function(){f=f+1|0;u=void 0;l()}},getSnapshot:function(){return f},S:function(){if(null!=r){var n=r.o,e=this.o;if(0==n&&0==e||0==n&&1==e){r.f();o=a(void 0,this)}else if(1==n&&0==e||2==n&&0==e);else o=a(r,this)}else o=a(void 0,this)},f:function(){var n=o;o=void 0;null==n||n()}})[s]=function(){this.f()},t}(n);var u=o.current;i.useSyncExternalStore(u.subscribe,u.getSnapshot,u.getSnapshot);u.S();if(0===n)m(g);return u}Object.defineProperties(e.Signal.prototype,{$$typeof:{configurable:!0,value:f},type:{configurable:!0,value:function(n){var e=n.data,t=w(1);try{return e.value}finally{t.f()}}},props:{configurable:!0,get:function(){return{data:this}}},ref:{configurable:!0,value:null}});n.u=w;n.ensureFinalCleanup=h;n.useComputed=function(n,i){var r=t.useRef(n);r.current=n;return t.useMemo(function(){return e.computed(function(){return r.current()},i)},u)};n.useSignal=function(n,i){return t.useMemo(function(){return e.signal(n,i)},u)};n.useSignalEffect=function(n,i){var r=t.useRef(n);r.current=n;t.useEffect(function(){return e.effect(function(){return r.current()},i)},u)};n.useSignals=function(n){return w(n)};n.wrapJsx=function(n){if("function"!=typeof n)return n;else return function(t,i){if("string"==typeof t&&i)for(var r in i){var o=i[r];if("children"!==r&&o instanceof e.Signal)i[r]=o.value}return n.call.apply(n,[n,t,i].concat([].slice.call(arguments,2)))}}});//# sourceMappingURL=runtime.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.min.js","sources":["../src/index.ts"],"sourcesContent":["import {\n\tsignal,\n\tcomputed,\n\teffect,\n\tSignal,\n\tReadonlySignal,\n\tSignalOptions,\n\tEffectOptions,\n} from \"@preact/signals-core\";\nimport {\n\tuseRef,\n\tuseMemo,\n\tuseEffect,\n\tuseLayoutEffect,\n\tversion as reactVersion,\n} from \"react\";\nimport { useSyncExternalStore } from \"use-sync-external-store/shim/index.js\";\n\nconst [major] = reactVersion.split(\".\").map(Number);\nconst Empty = [] as const;\n// V19 https://github.com/facebook/react/blob/346c7d4c43a0717302d446da9e7423a8e28d8996/packages/shared/ReactSymbols.js#L15\n// V18 https://github.com/facebook/react/blob/346c7d4c43a0717302d446da9e7423a8e28d8996/packages/shared/ReactSymbols.js#L15\nconst ReactElemType = Symbol.for(\n\tmajor >= 19 ? \"react.transitional.element\" : \"react.element\"\n);\n\nexport function wrapJsx<T>(jsx: T): T {\n\tif (typeof jsx !== \"function\") return jsx;\n\n\treturn function (type: any, props: any, ...rest: any[]) {\n\t\tif (typeof type === \"string\" && props) {\n\t\t\tfor (let i in props) {\n\t\t\t\tlet v = props[i];\n\t\t\t\tif (i !== \"children\" && v instanceof Signal) {\n\t\t\t\t\tprops[i] = v.value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn jsx.call(jsx, type, props, ...rest);\n\t} as any as T;\n}\n\nconst symDispose: unique symbol =\n\t(Symbol as any).dispose || Symbol.for(\"Symbol.dispose\");\n\ninterface Effect {\n\t_sources: object | undefined;\n\t_start(): () => void;\n\t_callback(): void;\n\t_dispose(): void;\n}\n\n/**\n * Use this flag to represent a bare `useSignals` call that doesn't manually\n * close its effect store and relies on auto-closing when the next useSignals is\n * called or after a microtask\n */\nconst UNMANAGED = 0;\n/**\n * Use this flag to represent a `useSignals` call that is manually closed by a\n * try/finally block in a component's render method. This is the default usage\n * that the react-transform plugin uses.\n */\nconst MANAGED_COMPONENT = 1;\n/**\n * Use this flag to represent a `useSignals` call that is manually closed by a\n * try/finally block in a hook body. This is the default usage that the\n * react-transform plugin uses.\n */\nconst MANAGED_HOOK = 2;\n\n/**\n * An enum defining how this store is used. See the documentation for each enum\n * member for more details.\n * @see {@link UNMANAGED}\n * @see {@link MANAGED_COMPONENT}\n * @see {@link MANAGED_HOOK}\n */\ntype EffectStoreUsage =\n\t| typeof UNMANAGED\n\t| typeof MANAGED_COMPONENT\n\t| typeof MANAGED_HOOK;\n\nexport interface EffectStore {\n\t/**\n\t * An enum defining how this hook is used and whether it is invoked in a\n\t * component's body or hook body. See the comment on `EffectStoreUsage` for\n\t * more details.\n\t */\n\treadonly _usage: EffectStoreUsage;\n\treadonly effect: Effect;\n\tsubscribe(onStoreChange: () => void): () => void;\n\tgetSnapshot(): number;\n\t/** startEffect - begin tracking signals used in this component */\n\t_start(): void;\n\t/** finishEffect - stop tracking the signals used in this component */\n\tf(): void;\n\t[symDispose](): void;\n}\n\nlet currentStore: EffectStore | undefined;\n\nfunction startComponentEffect(\n\tprevStore: EffectStore | undefined,\n\tnextStore: EffectStore\n) {\n\tconst endEffect = nextStore.effect._start();\n\tcurrentStore = nextStore;\n\n\treturn finishComponentEffect.bind(nextStore, prevStore, endEffect);\n}\n\nfunction finishComponentEffect(\n\tthis: EffectStore,\n\tprevStore: EffectStore | undefined,\n\tendEffect: () => void\n) {\n\tendEffect();\n\tcurrentStore = prevStore;\n}\n\n/**\n * A redux-like store whose store value is a positive 32bit integer (a\n * 'version').\n *\n * React subscribes to this store and gets a snapshot of the current 'version',\n * whenever the 'version' changes, we tell React it's time to update the\n * component (call 'onStoreChange').\n *\n * How we achieve this is by creating a binding with an 'effect', when the\n * `effect._callback' is called, we update our store version and tell React to\n * re-render the component ([1] We don't really care when/how React does it).\n *\n * [1]\n * @see https://react.dev/reference/react/useSyncExternalStore\n * @see\n * https://github.com/reactjs/rfcs/blob/main/text/0214-use-sync-external-store.md\n *\n * @param _usage An enum defining how this hook is used and whether it is\n * invoked in a component's body or hook body. See the comment on\n * `EffectStoreUsage` for more details.\n */\nfunction createEffectStore(_usage: EffectStoreUsage): EffectStore {\n\tlet effectInstance!: Effect;\n\tlet endEffect: (() => void) | undefined;\n\tlet version = 0;\n\tlet onChangeNotifyReact: (() => void) | undefined;\n\n\tlet unsubscribe = effect(function (this: Effect) {\n\t\teffectInstance = this;\n\t});\n\teffectInstance._callback = function () {\n\t\tversion = (version + 1) | 0;\n\t\tif (onChangeNotifyReact) onChangeNotifyReact();\n\t};\n\n\treturn {\n\t\t_usage,\n\t\teffect: effectInstance,\n\t\tsubscribe(onStoreChange) {\n\t\t\tonChangeNotifyReact = onStoreChange;\n\n\t\t\treturn function () {\n\t\t\t\t/**\n\t\t\t\t * Rotate to next version when unsubscribing to ensure that components are re-run\n\t\t\t\t * when subscribing again.\n\t\t\t\t *\n\t\t\t\t * In StrictMode, 'memo'-ed components seem to keep a stale snapshot version, so\n\t\t\t\t * don't re-run after subscribing again if the version is the same as last time.\n\t\t\t\t *\n\t\t\t\t * Because we unsubscribe from the effect, the version may not change. We simply\n\t\t\t\t * set a new initial version in case of stale snapshots here.\n\t\t\t\t */\n\t\t\t\tversion = (version + 1) | 0;\n\t\t\t\tonChangeNotifyReact = undefined;\n\t\t\t\tunsubscribe();\n\t\t\t};\n\t\t},\n\t\tgetSnapshot() {\n\t\t\treturn version;\n\t\t},\n\t\t_start() {\n\t\t\t// In general, we want to support two kinds of usages of useSignals:\n\t\t\t//\n\t\t\t// A) Managed: calling useSignals in a component or hook body wrapped in a\n\t\t\t// try/finally (like what the react-transform plugin does)\n\t\t\t//\n\t\t\t// B) Unmanaged: Calling useSignals directly without wrapping in a\n\t\t\t// try/finally\n\t\t\t//\n\t\t\t// For managed, we finish the effect in the finally block of the component\n\t\t\t// or hook body. For unmanaged, we finish the effect in the next\n\t\t\t// useSignals call or after a microtask.\n\t\t\t//\n\t\t\t// There are different tradeoffs which each approach. With managed, using\n\t\t\t// a try/finally ensures that only signals used in the component or hook\n\t\t\t// body are tracked. However, signals accessed in render props are missed\n\t\t\t// because the render prop is invoked in another component that may or may\n\t\t\t// not realize it is rendering signals accessed in the render prop it is\n\t\t\t// given.\n\t\t\t//\n\t\t\t// The other approach is \"unmanaged\": to call useSignals directly without\n\t\t\t// wrapping in a try/finally. This approach is easier to manually write in\n\t\t\t// situations where a build step isn't available but does open up the\n\t\t\t// possibility of catching signals accessed in other code before the\n\t\t\t// effect is closed (e.g. in a layout effect). Most situations where this\n\t\t\t// could happen are generally consider bad patterns or bugs. For example,\n\t\t\t// using a signal in a component and not having a call to `useSignals`\n\t\t\t// would be an bug. Or using a signal in `useLayoutEffect` is generally\n\t\t\t// not recommended since that layout effect won't update when the signals'\n\t\t\t// value change.\n\t\t\t//\n\t\t\t// To support both approaches, we need to track how each invocation of\n\t\t\t// useSignals is used, so we can properly transition between different\n\t\t\t// kinds of usages.\n\t\t\t//\n\t\t\t// The following table shows the different scenarios and how we should\n\t\t\t// handle them.\n\t\t\t//\n\t\t\t// Key:\n\t\t\t// 0 = UNMANAGED\n\t\t\t// 1 = MANAGED_COMPONENT\n\t\t\t// 2 = MANAGED_HOOK\n\t\t\t//\n\t\t\t// Pattern:\n\t\t\t// prev store usage -> this store usage: action to take\n\t\t\t//\n\t\t\t// - 0 -> 0: finish previous effect (unknown to unknown)\n\t\t\t//\n\t\t\t// We don't know how the previous effect was used, so we need to finish\n\t\t\t// it before starting the next effect.\n\t\t\t//\n\t\t\t// - 0 -> 1: finish previous effect\n\t\t\t//\n\t\t\t// Assume previous invocation was another component or hook from another\n\t\t\t// component. Nested component renders (renderToStaticMarkup within a\n\t\t\t// component's render) won't be supported with bare useSignals calls.\n\t\t\t//\n\t\t\t// - 0 -> 2: capture & restore\n\t\t\t//\n\t\t\t// Previous invocation could be a component or a hook. Either way,\n\t\t\t// restore it after our invocation so that it can continue to capture\n\t\t\t// any signals after we exit.\n\t\t\t//\n\t\t\t// - 1 -> 0: Do nothing. Signals already captured by current effect store\n\t\t\t// - 1 -> 1: capture & restore (e.g. component calls renderToStaticMarkup)\n\t\t\t// - 1 -> 2: capture & restore (e.g. hook)\n\t\t\t//\n\t\t\t// - 2 -> 0: Do nothing. Signals already captured by current effect store\n\t\t\t// - 2 -> 1: capture & restore (e.g. hook calls renderToStaticMarkup)\n\t\t\t// - 2 -> 2: capture & restore (e.g. nested hook calls)\n\n\t\t\tif (currentStore == undefined) {\n\t\t\t\tendEffect = startComponentEffect(undefined, this);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst prevUsage = currentStore._usage;\n\t\t\tconst thisUsage = this._usage;\n\n\t\t\tif (\n\t\t\t\t(prevUsage == UNMANAGED && thisUsage == UNMANAGED) || // 0 -> 0\n\t\t\t\t(prevUsage == UNMANAGED && thisUsage == MANAGED_COMPONENT) // 0 -> 1\n\t\t\t) {\n\t\t\t\t// finish previous effect\n\t\t\t\tcurrentStore.f();\n\t\t\t\tendEffect = startComponentEffect(undefined, this);\n\t\t\t} else if (\n\t\t\t\t(prevUsage == MANAGED_COMPONENT && thisUsage == UNMANAGED) || // 1 -> 0\n\t\t\t\t(prevUsage == MANAGED_HOOK && thisUsage == UNMANAGED) // 2 -> 0\n\t\t\t) {\n\t\t\t\t// Do nothing since it'll be captured by current effect store\n\t\t\t} else {\n\t\t\t\t// nested scenarios, so capture and restore the previous effect store\n\t\t\t\tendEffect = startComponentEffect(currentStore, this);\n\t\t\t}\n\t\t},\n\t\tf() {\n\t\t\tconst end = endEffect;\n\t\t\tendEffect = undefined;\n\t\t\tend?.();\n\t\t},\n\t\t[symDispose]() {\n\t\t\tthis.f();\n\t\t},\n\t};\n}\n\nconst noop = () => {};\n\nfunction createEmptyEffectStore(): EffectStore {\n\treturn {\n\t\t_usage: UNMANAGED,\n\t\teffect: {\n\t\t\t_sources: undefined,\n\t\t\t_callback() {},\n\t\t\t_start() {\n\t\t\t\treturn /* endEffect */ noop;\n\t\t\t},\n\t\t\t_dispose() {},\n\t\t},\n\t\tsubscribe() {\n\t\t\treturn /* unsubscribe */ noop;\n\t\t},\n\t\tgetSnapshot() {\n\t\t\treturn 0;\n\t\t},\n\t\t_start() {},\n\t\tf() {},\n\t\t[symDispose]() {},\n\t};\n}\n\nconst emptyEffectStore = createEmptyEffectStore();\n\nconst _queueMicroTask = Promise.prototype.then.bind(Promise.resolve());\n\nlet finalCleanup: Promise<void> | undefined;\nexport function ensureFinalCleanup() {\n\tif (!finalCleanup) {\n\t\tfinalCleanup = _queueMicroTask(cleanupTrailingStore);\n\t}\n}\nfunction cleanupTrailingStore() {\n\tfinalCleanup = undefined;\n\tcurrentStore?.f();\n}\n\nconst useIsomorphicLayoutEffect =\n\ttypeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n\n/**\n * Custom hook to create the effect to track signals used during render and\n * subscribe to changes to rerender the component when the signals change.\n */\nexport function _useSignalsImplementation(\n\t_usage: EffectStoreUsage = UNMANAGED\n): EffectStore {\n\tensureFinalCleanup();\n\n\tconst storeRef = useRef<EffectStore>();\n\tif (storeRef.current == null) {\n\t\tif (typeof window === \"undefined\") {\n\t\t\tstoreRef.current = emptyEffectStore;\n\t\t} else {\n\t\t\tstoreRef.current = createEffectStore(_usage);\n\t\t}\n\t}\n\n\tconst store = storeRef.current;\n\tuseSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);\n\tstore._start();\n\t// note: _usage is a constant here, so conditional is okay\n\tif (_usage === UNMANAGED) useIsomorphicLayoutEffect(cleanupTrailingStore);\n\n\treturn store;\n}\n\n/**\n * A wrapper component that renders a Signal's value directly as a Text node or JSX.\n */\nfunction SignalValue({ data }: { data: Signal }) {\n\tconst store = _useSignalsImplementation(1);\n\ttry {\n\t\treturn data.value;\n\t} finally {\n\t\tstore.f();\n\t}\n}\n\n// Decorate Signals so React renders them as <SignalValue> components.\nObject.defineProperties(Signal.prototype, {\n\t$$typeof: { configurable: true, value: ReactElemType },\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\tref: { configurable: true, value: null },\n});\n\nexport function useSignals(usage?: EffectStoreUsage): EffectStore {\n\treturn _useSignalsImplementation(usage);\n}\n\nexport function useSignal<T>(value: T, options?: SignalOptions<T>): Signal<T>;\nexport function useSignal<T = undefined>(): Signal<T | undefined>;\nexport function useSignal<T>(value?: T, options?: SignalOptions<T>) {\n\treturn useMemo(\n\t\t() => signal<T | undefined>(value, options as SignalOptions),\n\t\tEmpty\n\t);\n}\n\nexport function useComputed<T>(\n\tcompute: () => T,\n\toptions?: SignalOptions<T>\n): ReadonlySignal<T> {\n\tconst $compute = useRef(compute);\n\t$compute.current = compute;\n\treturn useMemo(() => computed<T>(() => $compute.current(), options), Empty);\n}\n\nexport function useSignalEffect(\n\tcb: () => void | (() => void),\n\toptions?: EffectOptions\n) {\n\tconst callback = useRef(cb);\n\tcallback.current = cb;\n\n\tuseEffect(() => {\n\t\treturn effect(function (this: Effect) {\n\t\t\treturn callback.current();\n\t\t}, options);\n\t}, Empty);\n}\n"],"names":["currentStore","major","reactVersion","split","map","Number","Empty","ReactElemType","Symbol","symDispose","dispose","startComponentEffect","prevStore","nextStore","endEffect","effect","_start","finishComponentEffect","bind","_ref2","finalCleanup","noop","emptyEffectStore","_usage","_sources","undefined","_callback","_dispose","subscribe","getSnapshot","f","_queueMicroTask","Promise","prototype","then","resolve","ensureFinalCleanup","cleanupTrailingStore","_currentStore","useIsomorphicLayoutEffect","window","useLayoutEffect","useEffect","_useSignalsImplementation","storeRef","useRef","current","_ref","effectInstance","onChangeNotifyReact","version","unsubscribe","this","onStoreChange","prevUsage","thisUsage","end","createEffectStore","store","useSyncExternalStore","Object","defineProperties","Signal","$$typeof","configurable","value","type","_ref3","data","props","get","ref","exports","useComputed","compute","options","$compute","useMemo","computed","useSignal","signal","useSignalEffect","cb","callback","useSignals","usage","wrapJsx","jsx","i","v","call","apply","concat","slice","arguments"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runtime.min.js","sources":["../src/index.ts"],"sourcesContent":["import {\n\tsignal,\n\tcomputed,\n\teffect,\n\tSignal,\n\tReadonlySignal,\n\tSignalOptions,\n\tEffectOptions,\n} from \"@preact/signals-core\";\nimport {\n\tuseRef,\n\tuseMemo,\n\tuseEffect,\n\tuseLayoutEffect,\n\tversion as reactVersion,\n} from \"react\";\nimport { useSyncExternalStore } from \"use-sync-external-store/shim/index.js\";\nimport type { SignalsDevToolsAPI } from \"../../../debug/src/devtools\";\n\nconst [major] = reactVersion.split(\".\").map(Number);\nconst Empty = [] as const;\n// V19 https://github.com/facebook/react/blob/346c7d4c43a0717302d446da9e7423a8e28d8996/packages/shared/ReactSymbols.js#L15\n// V18 https://github.com/facebook/react/blob/346c7d4c43a0717302d446da9e7423a8e28d8996/packages/shared/ReactSymbols.js#L15\nconst ReactElemType = Symbol.for(\n\tmajor >= 19 ? \"react.transitional.element\" : \"react.element\"\n);\n\nconst DEVTOOLS_ENABLED =\n\ttypeof window !== \"undefined\" && !!window.__PREACT_SIGNALS_DEVTOOLS__;\n\nexport function wrapJsx<T>(jsx: T): T {\n\tif (typeof jsx !== \"function\") return jsx;\n\n\treturn function (type: any, props: any, ...rest: any[]) {\n\t\tif (typeof type === \"string\" && props) {\n\t\t\tfor (let i in props) {\n\t\t\t\tlet v = props[i];\n\t\t\t\tif (i !== \"children\" && v instanceof Signal) {\n\t\t\t\t\tprops[i] = v.value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn jsx.call(jsx, type, props, ...rest);\n\t} as any as T;\n}\n\nconst symDispose: unique symbol =\n\t(Symbol as any).dispose || Symbol.for(\"Symbol.dispose\");\n\ninterface Effect {\n\t_sources: object | undefined;\n\t_debugCallback?: () => void;\n\t_start(): () => void;\n\t_callback(): void;\n\t_dispose(): void;\n}\n\n/**\n * Use this flag to represent a bare `useSignals` call that doesn't manually\n * close its effect store and relies on auto-closing when the next useSignals is\n * called or after a microtask\n */\nconst UNMANAGED = 0;\n/**\n * Use this flag to represent a `useSignals` call that is manually closed by a\n * try/finally block in a component's render method. This is the default usage\n * that the react-transform plugin uses.\n */\nconst MANAGED_COMPONENT = 1;\n/**\n * Use this flag to represent a `useSignals` call that is manually closed by a\n * try/finally block in a hook body. This is the default usage that the\n * react-transform plugin uses.\n */\nconst MANAGED_HOOK = 2;\n\n/**\n * An enum defining how this store is used. See the documentation for each enum\n * member for more details.\n * @see {@link UNMANAGED}\n * @see {@link MANAGED_COMPONENT}\n * @see {@link MANAGED_HOOK}\n */\ntype EffectStoreUsage =\n\t| typeof UNMANAGED\n\t| typeof MANAGED_COMPONENT\n\t| typeof MANAGED_HOOK;\n\nexport interface EffectStore {\n\t/**\n\t * An enum defining how this hook is used and whether it is invoked in a\n\t * component's body or hook body. See the comment on `EffectStoreUsage` for\n\t * more details.\n\t */\n\treadonly _usage: EffectStoreUsage;\n\treadonly effect: Effect;\n\tsubscribe(onStoreChange: () => void): () => void;\n\tgetSnapshot(): number;\n\t/** startEffect - begin tracking signals used in this component */\n\t_start(): void;\n\t/** finishEffect - stop tracking the signals used in this component */\n\tf(): void;\n\t[symDispose](): void;\n}\n\nlet currentStore: EffectStore | undefined;\n\nfunction startComponentEffect(\n\tprevStore: EffectStore | undefined,\n\tnextStore: EffectStore\n) {\n\tconst endEffect = nextStore.effect._start();\n\tcurrentStore = nextStore;\n\n\treturn finishComponentEffect.bind(nextStore, prevStore, endEffect);\n}\n\nfunction finishComponentEffect(\n\tthis: EffectStore,\n\tprevStore: EffectStore | undefined,\n\tendEffect: () => void\n) {\n\tendEffect();\n\tcurrentStore = prevStore;\n}\n\n/**\n * A redux-like store whose store value is a positive 32bit integer (a\n * 'version').\n *\n * React subscribes to this store and gets a snapshot of the current 'version',\n * whenever the 'version' changes, we tell React it's time to update the\n * component (call 'onStoreChange').\n *\n * How we achieve this is by creating a binding with an 'effect', when the\n * `effect._callback' is called, we update our store version and tell React to\n * re-render the component ([1] We don't really care when/how React does it).\n *\n * [1]\n * @see https://react.dev/reference/react/useSyncExternalStore\n * @see\n * https://github.com/reactjs/rfcs/blob/main/text/0214-use-sync-external-store.md\n *\n * @param _usage An enum defining how this hook is used and whether it is\n * invoked in a component's body or hook body. See the comment on\n * `EffectStoreUsage` for more details.\n */\nfunction createEffectStore(_usage: EffectStoreUsage): EffectStore {\n\tlet effectInstance!: Effect;\n\tlet endEffect: (() => void) | undefined;\n\tlet version = 0;\n\tlet onChangeNotifyReact: (() => void) | undefined;\n\n\tlet unsubscribe = effect(function (this: Effect) {\n\t\teffectInstance = this;\n\t});\n\teffectInstance._callback = function () {\n\t\tversion = (version + 1) | 0;\n\t\tif (DEVTOOLS_ENABLED) {\n\t\t\teffectInstance._debugCallback?.call(effectInstance);\n\t\t}\n\t\tif (onChangeNotifyReact) onChangeNotifyReact();\n\t};\n\n\treturn {\n\t\t_usage,\n\t\teffect: effectInstance,\n\t\tsubscribe(onStoreChange) {\n\t\t\tonChangeNotifyReact = onStoreChange;\n\n\t\t\treturn function () {\n\t\t\t\t/**\n\t\t\t\t * Rotate to next version when unsubscribing to ensure that components are re-run\n\t\t\t\t * when subscribing again.\n\t\t\t\t *\n\t\t\t\t * In StrictMode, 'memo'-ed components seem to keep a stale snapshot version, so\n\t\t\t\t * don't re-run after subscribing again if the version is the same as last time.\n\t\t\t\t *\n\t\t\t\t * Because we unsubscribe from the effect, the version may not change. We simply\n\t\t\t\t * set a new initial version in case of stale snapshots here.\n\t\t\t\t */\n\t\t\t\tversion = (version + 1) | 0;\n\t\t\t\tonChangeNotifyReact = undefined;\n\t\t\t\tunsubscribe();\n\t\t\t};\n\t\t},\n\t\tgetSnapshot() {\n\t\t\treturn version;\n\t\t},\n\t\t_start() {\n\t\t\t// In general, we want to support two kinds of usages of useSignals:\n\t\t\t//\n\t\t\t// A) Managed: calling useSignals in a component or hook body wrapped in a\n\t\t\t// try/finally (like what the react-transform plugin does)\n\t\t\t//\n\t\t\t// B) Unmanaged: Calling useSignals directly without wrapping in a\n\t\t\t// try/finally\n\t\t\t//\n\t\t\t// For managed, we finish the effect in the finally block of the component\n\t\t\t// or hook body. For unmanaged, we finish the effect in the next\n\t\t\t// useSignals call or after a microtask.\n\t\t\t//\n\t\t\t// There are different tradeoffs which each approach. With managed, using\n\t\t\t// a try/finally ensures that only signals used in the component or hook\n\t\t\t// body are tracked. However, signals accessed in render props are missed\n\t\t\t// because the render prop is invoked in another component that may or may\n\t\t\t// not realize it is rendering signals accessed in the render prop it is\n\t\t\t// given.\n\t\t\t//\n\t\t\t// The other approach is \"unmanaged\": to call useSignals directly without\n\t\t\t// wrapping in a try/finally. This approach is easier to manually write in\n\t\t\t// situations where a build step isn't available but does open up the\n\t\t\t// possibility of catching signals accessed in other code before the\n\t\t\t// effect is closed (e.g. in a layout effect). Most situations where this\n\t\t\t// could happen are generally consider bad patterns or bugs. For example,\n\t\t\t// using a signal in a component and not having a call to `useSignals`\n\t\t\t// would be an bug. Or using a signal in `useLayoutEffect` is generally\n\t\t\t// not recommended since that layout effect won't update when the signals'\n\t\t\t// value change.\n\t\t\t//\n\t\t\t// To support both approaches, we need to track how each invocation of\n\t\t\t// useSignals is used, so we can properly transition between different\n\t\t\t// kinds of usages.\n\t\t\t//\n\t\t\t// The following table shows the different scenarios and how we should\n\t\t\t// handle them.\n\t\t\t//\n\t\t\t// Key:\n\t\t\t// 0 = UNMANAGED\n\t\t\t// 1 = MANAGED_COMPONENT\n\t\t\t// 2 = MANAGED_HOOK\n\t\t\t//\n\t\t\t// Pattern:\n\t\t\t// prev store usage -> this store usage: action to take\n\t\t\t//\n\t\t\t// - 0 -> 0: finish previous effect (unknown to unknown)\n\t\t\t//\n\t\t\t// We don't know how the previous effect was used, so we need to finish\n\t\t\t// it before starting the next effect.\n\t\t\t//\n\t\t\t// - 0 -> 1: finish previous effect\n\t\t\t//\n\t\t\t// Assume previous invocation was another component or hook from another\n\t\t\t// component. Nested component renders (renderToStaticMarkup within a\n\t\t\t// component's render) won't be supported with bare useSignals calls.\n\t\t\t//\n\t\t\t// - 0 -> 2: capture & restore\n\t\t\t//\n\t\t\t// Previous invocation could be a component or a hook. Either way,\n\t\t\t// restore it after our invocation so that it can continue to capture\n\t\t\t// any signals after we exit.\n\t\t\t//\n\t\t\t// - 1 -> 0: Do nothing. Signals already captured by current effect store\n\t\t\t// - 1 -> 1: capture & restore (e.g. component calls renderToStaticMarkup)\n\t\t\t// - 1 -> 2: capture & restore (e.g. hook)\n\t\t\t//\n\t\t\t// - 2 -> 0: Do nothing. Signals already captured by current effect store\n\t\t\t// - 2 -> 1: capture & restore (e.g. hook calls renderToStaticMarkup)\n\t\t\t// - 2 -> 2: capture & restore (e.g. nested hook calls)\n\n\t\t\tif (currentStore == undefined) {\n\t\t\t\tendEffect = startComponentEffect(undefined, this);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst prevUsage = currentStore._usage;\n\t\t\tconst thisUsage = this._usage;\n\n\t\t\tif (\n\t\t\t\t(prevUsage == UNMANAGED && thisUsage == UNMANAGED) || // 0 -> 0\n\t\t\t\t(prevUsage == UNMANAGED && thisUsage == MANAGED_COMPONENT) // 0 -> 1\n\t\t\t) {\n\t\t\t\t// finish previous effect\n\t\t\t\tcurrentStore.f();\n\t\t\t\tendEffect = startComponentEffect(undefined, this);\n\t\t\t} else if (\n\t\t\t\t(prevUsage == MANAGED_COMPONENT && thisUsage == UNMANAGED) || // 1 -> 0\n\t\t\t\t(prevUsage == MANAGED_HOOK && thisUsage == UNMANAGED) // 2 -> 0\n\t\t\t) {\n\t\t\t\t// Do nothing since it'll be captured by current effect store\n\t\t\t} else {\n\t\t\t\t// nested scenarios, so capture and restore the previous effect store\n\t\t\t\tendEffect = startComponentEffect(currentStore, this);\n\t\t\t}\n\t\t},\n\t\tf() {\n\t\t\tconst end = endEffect;\n\t\t\tendEffect = undefined;\n\t\t\tend?.();\n\t\t},\n\t\t[symDispose]() {\n\t\t\tthis.f();\n\t\t},\n\t};\n}\n\nconst noop = () => {};\n\nfunction createEmptyEffectStore(): EffectStore {\n\treturn {\n\t\t_usage: UNMANAGED,\n\t\teffect: {\n\t\t\t_sources: undefined,\n\t\t\t_callback() {},\n\t\t\t_start() {\n\t\t\t\treturn /* endEffect */ noop;\n\t\t\t},\n\t\t\t_dispose() {},\n\t\t},\n\t\tsubscribe() {\n\t\t\treturn /* unsubscribe */ noop;\n\t\t},\n\t\tgetSnapshot() {\n\t\t\treturn 0;\n\t\t},\n\t\t_start() {},\n\t\tf() {},\n\t\t[symDispose]() {},\n\t};\n}\n\nconst emptyEffectStore = createEmptyEffectStore();\n\nconst _queueMicroTask = Promise.prototype.then.bind(Promise.resolve());\n\nlet finalCleanup: Promise<void> | undefined;\nexport function ensureFinalCleanup() {\n\tif (!finalCleanup) {\n\t\tfinalCleanup = _queueMicroTask(cleanupTrailingStore);\n\t}\n}\nfunction cleanupTrailingStore() {\n\tfinalCleanup = undefined;\n\tcurrentStore?.f();\n}\n\nconst useIsomorphicLayoutEffect =\n\ttypeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n\n/**\n * Custom hook to create the effect to track signals used during render and\n * subscribe to changes to rerender the component when the signals change.\n */\nexport function _useSignalsImplementation(\n\t_usage: EffectStoreUsage = UNMANAGED\n): EffectStore {\n\tensureFinalCleanup();\n\n\tconst storeRef = useRef<EffectStore>();\n\tif (storeRef.current == null) {\n\t\tif (typeof window === \"undefined\") {\n\t\t\tstoreRef.current = emptyEffectStore;\n\t\t} else {\n\t\t\tstoreRef.current = createEffectStore(_usage);\n\t\t}\n\t}\n\n\tconst store = storeRef.current;\n\tuseSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);\n\tstore._start();\n\t// note: _usage is a constant here, so conditional is okay\n\tif (_usage === UNMANAGED) useIsomorphicLayoutEffect(cleanupTrailingStore);\n\n\treturn store;\n}\n\n/**\n * A wrapper component that renders a Signal's value directly as a Text node or JSX.\n */\nfunction SignalValue({ data }: { data: Signal }) {\n\tconst store = _useSignalsImplementation(1);\n\ttry {\n\t\treturn data.value;\n\t} finally {\n\t\tstore.f();\n\t}\n}\n\n// Decorate Signals so React renders them as <SignalValue> components.\nObject.defineProperties(Signal.prototype, {\n\t$$typeof: { configurable: true, value: ReactElemType },\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\tref: { configurable: true, value: null },\n});\n\nexport function useSignals(usage?: EffectStoreUsage): EffectStore {\n\treturn _useSignalsImplementation(usage);\n}\n\nexport function useSignal<T>(value: T, options?: SignalOptions<T>): Signal<T>;\nexport function useSignal<T = undefined>(): Signal<T | undefined>;\nexport function useSignal<T>(value?: T, options?: SignalOptions<T>) {\n\treturn useMemo(\n\t\t() => signal<T | undefined>(value, options as SignalOptions),\n\t\tEmpty\n\t);\n}\n\nexport function useComputed<T>(\n\tcompute: () => T,\n\toptions?: SignalOptions<T>\n): ReadonlySignal<T> {\n\tconst $compute = useRef(compute);\n\t$compute.current = compute;\n\treturn useMemo(() => computed<T>(() => $compute.current(), options), Empty);\n}\n\nexport function useSignalEffect(\n\tcb: () => void | (() => void),\n\toptions?: EffectOptions\n) {\n\tconst callback = useRef(cb);\n\tcallback.current = cb;\n\n\tuseEffect(() => {\n\t\treturn effect(function (this: Effect) {\n\t\t\treturn callback.current();\n\t\t}, options);\n\t}, Empty);\n}\n\ndeclare global {\n\tinterface Window {\n\t\t__PREACT_SIGNALS_DEVTOOLS__: SignalsDevToolsAPI;\n\t}\n}\n"],"names":["g","f","exports","module","require","define","amd","globalThis","self","reactSignalsRuntime","preactSignalsCore","react","index_js","this","signalsCore","currentStore","major","reactVersion","version","split","map","Number","Empty","ReactElemType","Symbol","DEVTOOLS_ENABLED","window","__PREACT_SIGNALS_DEVTOOLS__","symDispose","dispose","startComponentEffect","prevStore","nextStore","endEffect","effect","_start","finishComponentEffect","bind","_ref2","finalCleanup","noop","emptyEffectStore","_usage","_sources","undefined","_callback","_dispose","subscribe","getSnapshot","_queueMicroTask","Promise","prototype","then","resolve","ensureFinalCleanup","cleanupTrailingStore","_currentStore","useIsomorphicLayoutEffect","useLayoutEffect","useEffect","_useSignalsImplementation","storeRef","useRef","current","_ref","effectInstance","onChangeNotifyReact","unsubscribe","_effectInstance$_debu","_debugCallback","call","onStoreChange","prevUsage","thisUsage","end","createEffectStore","store","useSyncExternalStore","Object","defineProperties","Signal","$$typeof","configurable","value","type","_ref3","data","props","get","ref","useComputed","compute","options","$compute","useMemo","computed","useSignal","signal","useSignalEffect","cb","callback","useSignals","usage","wrapJsx","jsx","i","v","apply","concat","slice","arguments"],"mappings":"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"}
|
package/runtime/dist/runtime.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Signal as
|
|
1
|
+
import{Signal as n,signal as t,computed as e,effect as o}from"@preact/signals-core";import{version as i,useRef as r,useMemo as f,useEffect as u,useLayoutEffect as s}from"react";import{useSyncExternalStore as c}from"use-sync-external-store/shim/index.js";const[l]=i.split(".").map(Number),a=[],d=Symbol.for(l>=19?"react.transitional.element":"react.element"),p="undefined"!=typeof window&&!!window.__PREACT_SIGNALS_DEVTOOLS__;function m(t){if("function"!=typeof t)return t;else return function(e,o,...i){if("string"==typeof e&&o)for(let t in o){let e=o[t];if("children"!==t&&e instanceof n)o[t]=e.value}return t.call(t,e,o,...i)}}const b=Symbol.dispose||Symbol.for("Symbol.dispose");let y;function g(n,t){const e=t.effect.S();y=t;return h.bind(t,n,e)}function h(n,t){t();y=n}const v=()=>{},w={o:0,effect:{s:void 0,c(){},S:()=>v,d(){}},subscribe:()=>v,getSnapshot:()=>0,S(){},f(){},[b](){}},_=Promise.prototype.then.bind(Promise.resolve());let S;function x(){if(!S)S=_(j)}function j(){var n;S=void 0;null==(n=y)||n.f()}const P="undefined"!=typeof window?s:u;function $(n=0){x();const t=r();if(null==t.current)if("undefined"==typeof window)t.current=w;else t.current=function(n){let t,e,i,r=0,f=o(function(){t=this});t.c=function(){r=r+1|0;if(p){var n;null==(n=t.y)||n.call(t)}if(i)i()};return{o:n,effect:t,subscribe(n){i=n;return function(){r=r+1|0;i=void 0;f()}},getSnapshot:()=>r,S(){if(null==y){e=g(void 0,this);return}const n=y.o,t=this.o;if(0==n&&0==t||0==n&&1==t){y.f();e=g(void 0,this)}else if(1==n&&0==t||2==n&&0==t);else e=g(y,this)},f(){const n=e;e=void 0;null==n||n()},[b](){this.f()}}}(n);const e=t.current;c(e.subscribe,e.getSnapshot,e.getSnapshot);e.S();if(0===n)P(j);return e}Object.defineProperties(n.prototype,{$$typeof:{configurable:!0,value:d},type:{configurable:!0,value:function({data:n}){const t=$(1);try{return n.value}finally{t.f()}}},props:{configurable:!0,get(){return{data:this}}},ref:{configurable:!0,value:null}});function k(n){return $(n)}function useSignal(n,e){return f(()=>t(n,e),a)}function useComputed(n,t){const o=r(n);o.current=n;return f(()=>e(()=>o.current(),t),a)}function useSignalEffect(n,t){const e=r(n);e.current=n;u(()=>o(function(){return e.current()},t),a)}export{$ as _useSignalsImplementation,x as ensureFinalCleanup,useComputed,useSignal,useSignalEffect,k as useSignals,m as wrapJsx};//# sourceMappingURL=runtime.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.mjs","sources":["../src/index.ts"],"sourcesContent":["import {\n\tsignal,\n\tcomputed,\n\teffect,\n\tSignal,\n\tReadonlySignal,\n\tSignalOptions,\n\tEffectOptions,\n} from \"@preact/signals-core\";\nimport {\n\tuseRef,\n\tuseMemo,\n\tuseEffect,\n\tuseLayoutEffect,\n\tversion as reactVersion,\n} from \"react\";\nimport { useSyncExternalStore } from \"use-sync-external-store/shim/index.js\";\n\nconst [major] = reactVersion.split(\".\").map(Number);\nconst Empty = [] as const;\n// V19 https://github.com/facebook/react/blob/346c7d4c43a0717302d446da9e7423a8e28d8996/packages/shared/ReactSymbols.js#L15\n// V18 https://github.com/facebook/react/blob/346c7d4c43a0717302d446da9e7423a8e28d8996/packages/shared/ReactSymbols.js#L15\nconst ReactElemType = Symbol.for(\n\tmajor >= 19 ? \"react.transitional.element\" : \"react.element\"\n);\n\nexport function wrapJsx<T>(jsx: T): T {\n\tif (typeof jsx !== \"function\") return jsx;\n\n\treturn function (type: any, props: any, ...rest: any[]) {\n\t\tif (typeof type === \"string\" && props) {\n\t\t\tfor (let i in props) {\n\t\t\t\tlet v = props[i];\n\t\t\t\tif (i !== \"children\" && v instanceof Signal) {\n\t\t\t\t\tprops[i] = v.value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn jsx.call(jsx, type, props, ...rest);\n\t} as any as T;\n}\n\nconst symDispose: unique symbol =\n\t(Symbol as any).dispose || Symbol.for(\"Symbol.dispose\");\n\ninterface Effect {\n\t_sources: object | undefined;\n\t_start(): () => void;\n\t_callback(): void;\n\t_dispose(): void;\n}\n\n/**\n * Use this flag to represent a bare `useSignals` call that doesn't manually\n * close its effect store and relies on auto-closing when the next useSignals is\n * called or after a microtask\n */\nconst UNMANAGED = 0;\n/**\n * Use this flag to represent a `useSignals` call that is manually closed by a\n * try/finally block in a component's render method. This is the default usage\n * that the react-transform plugin uses.\n */\nconst MANAGED_COMPONENT = 1;\n/**\n * Use this flag to represent a `useSignals` call that is manually closed by a\n * try/finally block in a hook body. This is the default usage that the\n * react-transform plugin uses.\n */\nconst MANAGED_HOOK = 2;\n\n/**\n * An enum defining how this store is used. See the documentation for each enum\n * member for more details.\n * @see {@link UNMANAGED}\n * @see {@link MANAGED_COMPONENT}\n * @see {@link MANAGED_HOOK}\n */\ntype EffectStoreUsage =\n\t| typeof UNMANAGED\n\t| typeof MANAGED_COMPONENT\n\t| typeof MANAGED_HOOK;\n\nexport interface EffectStore {\n\t/**\n\t * An enum defining how this hook is used and whether it is invoked in a\n\t * component's body or hook body. See the comment on `EffectStoreUsage` for\n\t * more details.\n\t */\n\treadonly _usage: EffectStoreUsage;\n\treadonly effect: Effect;\n\tsubscribe(onStoreChange: () => void): () => void;\n\tgetSnapshot(): number;\n\t/** startEffect - begin tracking signals used in this component */\n\t_start(): void;\n\t/** finishEffect - stop tracking the signals used in this component */\n\tf(): void;\n\t[symDispose](): void;\n}\n\nlet currentStore: EffectStore | undefined;\n\nfunction startComponentEffect(\n\tprevStore: EffectStore | undefined,\n\tnextStore: EffectStore\n) {\n\tconst endEffect = nextStore.effect._start();\n\tcurrentStore = nextStore;\n\n\treturn finishComponentEffect.bind(nextStore, prevStore, endEffect);\n}\n\nfunction finishComponentEffect(\n\tthis: EffectStore,\n\tprevStore: EffectStore | undefined,\n\tendEffect: () => void\n) {\n\tendEffect();\n\tcurrentStore = prevStore;\n}\n\n/**\n * A redux-like store whose store value is a positive 32bit integer (a\n * 'version').\n *\n * React subscribes to this store and gets a snapshot of the current 'version',\n * whenever the 'version' changes, we tell React it's time to update the\n * component (call 'onStoreChange').\n *\n * How we achieve this is by creating a binding with an 'effect', when the\n * `effect._callback' is called, we update our store version and tell React to\n * re-render the component ([1] We don't really care when/how React does it).\n *\n * [1]\n * @see https://react.dev/reference/react/useSyncExternalStore\n * @see\n * https://github.com/reactjs/rfcs/blob/main/text/0214-use-sync-external-store.md\n *\n * @param _usage An enum defining how this hook is used and whether it is\n * invoked in a component's body or hook body. See the comment on\n * `EffectStoreUsage` for more details.\n */\nfunction createEffectStore(_usage: EffectStoreUsage): EffectStore {\n\tlet effectInstance!: Effect;\n\tlet endEffect: (() => void) | undefined;\n\tlet version = 0;\n\tlet onChangeNotifyReact: (() => void) | undefined;\n\n\tlet unsubscribe = effect(function (this: Effect) {\n\t\teffectInstance = this;\n\t});\n\teffectInstance._callback = function () {\n\t\tversion = (version + 1) | 0;\n\t\tif (onChangeNotifyReact) onChangeNotifyReact();\n\t};\n\n\treturn {\n\t\t_usage,\n\t\teffect: effectInstance,\n\t\tsubscribe(onStoreChange) {\n\t\t\tonChangeNotifyReact = onStoreChange;\n\n\t\t\treturn function () {\n\t\t\t\t/**\n\t\t\t\t * Rotate to next version when unsubscribing to ensure that components are re-run\n\t\t\t\t * when subscribing again.\n\t\t\t\t *\n\t\t\t\t * In StrictMode, 'memo'-ed components seem to keep a stale snapshot version, so\n\t\t\t\t * don't re-run after subscribing again if the version is the same as last time.\n\t\t\t\t *\n\t\t\t\t * Because we unsubscribe from the effect, the version may not change. We simply\n\t\t\t\t * set a new initial version in case of stale snapshots here.\n\t\t\t\t */\n\t\t\t\tversion = (version + 1) | 0;\n\t\t\t\tonChangeNotifyReact = undefined;\n\t\t\t\tunsubscribe();\n\t\t\t};\n\t\t},\n\t\tgetSnapshot() {\n\t\t\treturn version;\n\t\t},\n\t\t_start() {\n\t\t\t// In general, we want to support two kinds of usages of useSignals:\n\t\t\t//\n\t\t\t// A) Managed: calling useSignals in a component or hook body wrapped in a\n\t\t\t// try/finally (like what the react-transform plugin does)\n\t\t\t//\n\t\t\t// B) Unmanaged: Calling useSignals directly without wrapping in a\n\t\t\t// try/finally\n\t\t\t//\n\t\t\t// For managed, we finish the effect in the finally block of the component\n\t\t\t// or hook body. For unmanaged, we finish the effect in the next\n\t\t\t// useSignals call or after a microtask.\n\t\t\t//\n\t\t\t// There are different tradeoffs which each approach. With managed, using\n\t\t\t// a try/finally ensures that only signals used in the component or hook\n\t\t\t// body are tracked. However, signals accessed in render props are missed\n\t\t\t// because the render prop is invoked in another component that may or may\n\t\t\t// not realize it is rendering signals accessed in the render prop it is\n\t\t\t// given.\n\t\t\t//\n\t\t\t// The other approach is \"unmanaged\": to call useSignals directly without\n\t\t\t// wrapping in a try/finally. This approach is easier to manually write in\n\t\t\t// situations where a build step isn't available but does open up the\n\t\t\t// possibility of catching signals accessed in other code before the\n\t\t\t// effect is closed (e.g. in a layout effect). Most situations where this\n\t\t\t// could happen are generally consider bad patterns or bugs. For example,\n\t\t\t// using a signal in a component and not having a call to `useSignals`\n\t\t\t// would be an bug. Or using a signal in `useLayoutEffect` is generally\n\t\t\t// not recommended since that layout effect won't update when the signals'\n\t\t\t// value change.\n\t\t\t//\n\t\t\t// To support both approaches, we need to track how each invocation of\n\t\t\t// useSignals is used, so we can properly transition between different\n\t\t\t// kinds of usages.\n\t\t\t//\n\t\t\t// The following table shows the different scenarios and how we should\n\t\t\t// handle them.\n\t\t\t//\n\t\t\t// Key:\n\t\t\t// 0 = UNMANAGED\n\t\t\t// 1 = MANAGED_COMPONENT\n\t\t\t// 2 = MANAGED_HOOK\n\t\t\t//\n\t\t\t// Pattern:\n\t\t\t// prev store usage -> this store usage: action to take\n\t\t\t//\n\t\t\t// - 0 -> 0: finish previous effect (unknown to unknown)\n\t\t\t//\n\t\t\t// We don't know how the previous effect was used, so we need to finish\n\t\t\t// it before starting the next effect.\n\t\t\t//\n\t\t\t// - 0 -> 1: finish previous effect\n\t\t\t//\n\t\t\t// Assume previous invocation was another component or hook from another\n\t\t\t// component. Nested component renders (renderToStaticMarkup within a\n\t\t\t// component's render) won't be supported with bare useSignals calls.\n\t\t\t//\n\t\t\t// - 0 -> 2: capture & restore\n\t\t\t//\n\t\t\t// Previous invocation could be a component or a hook. Either way,\n\t\t\t// restore it after our invocation so that it can continue to capture\n\t\t\t// any signals after we exit.\n\t\t\t//\n\t\t\t// - 1 -> 0: Do nothing. Signals already captured by current effect store\n\t\t\t// - 1 -> 1: capture & restore (e.g. component calls renderToStaticMarkup)\n\t\t\t// - 1 -> 2: capture & restore (e.g. hook)\n\t\t\t//\n\t\t\t// - 2 -> 0: Do nothing. Signals already captured by current effect store\n\t\t\t// - 2 -> 1: capture & restore (e.g. hook calls renderToStaticMarkup)\n\t\t\t// - 2 -> 2: capture & restore (e.g. nested hook calls)\n\n\t\t\tif (currentStore == undefined) {\n\t\t\t\tendEffect = startComponentEffect(undefined, this);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst prevUsage = currentStore._usage;\n\t\t\tconst thisUsage = this._usage;\n\n\t\t\tif (\n\t\t\t\t(prevUsage == UNMANAGED && thisUsage == UNMANAGED) || // 0 -> 0\n\t\t\t\t(prevUsage == UNMANAGED && thisUsage == MANAGED_COMPONENT) // 0 -> 1\n\t\t\t) {\n\t\t\t\t// finish previous effect\n\t\t\t\tcurrentStore.f();\n\t\t\t\tendEffect = startComponentEffect(undefined, this);\n\t\t\t} else if (\n\t\t\t\t(prevUsage == MANAGED_COMPONENT && thisUsage == UNMANAGED) || // 1 -> 0\n\t\t\t\t(prevUsage == MANAGED_HOOK && thisUsage == UNMANAGED) // 2 -> 0\n\t\t\t) {\n\t\t\t\t// Do nothing since it'll be captured by current effect store\n\t\t\t} else {\n\t\t\t\t// nested scenarios, so capture and restore the previous effect store\n\t\t\t\tendEffect = startComponentEffect(currentStore, this);\n\t\t\t}\n\t\t},\n\t\tf() {\n\t\t\tconst end = endEffect;\n\t\t\tendEffect = undefined;\n\t\t\tend?.();\n\t\t},\n\t\t[symDispose]() {\n\t\t\tthis.f();\n\t\t},\n\t};\n}\n\nconst noop = () => {};\n\nfunction createEmptyEffectStore(): EffectStore {\n\treturn {\n\t\t_usage: UNMANAGED,\n\t\teffect: {\n\t\t\t_sources: undefined,\n\t\t\t_callback() {},\n\t\t\t_start() {\n\t\t\t\treturn /* endEffect */ noop;\n\t\t\t},\n\t\t\t_dispose() {},\n\t\t},\n\t\tsubscribe() {\n\t\t\treturn /* unsubscribe */ noop;\n\t\t},\n\t\tgetSnapshot() {\n\t\t\treturn 0;\n\t\t},\n\t\t_start() {},\n\t\tf() {},\n\t\t[symDispose]() {},\n\t};\n}\n\nconst emptyEffectStore = createEmptyEffectStore();\n\nconst _queueMicroTask = Promise.prototype.then.bind(Promise.resolve());\n\nlet finalCleanup: Promise<void> | undefined;\nexport function ensureFinalCleanup() {\n\tif (!finalCleanup) {\n\t\tfinalCleanup = _queueMicroTask(cleanupTrailingStore);\n\t}\n}\nfunction cleanupTrailingStore() {\n\tfinalCleanup = undefined;\n\tcurrentStore?.f();\n}\n\nconst useIsomorphicLayoutEffect =\n\ttypeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n\n/**\n * Custom hook to create the effect to track signals used during render and\n * subscribe to changes to rerender the component when the signals change.\n */\nexport function _useSignalsImplementation(\n\t_usage: EffectStoreUsage = UNMANAGED\n): EffectStore {\n\tensureFinalCleanup();\n\n\tconst storeRef = useRef<EffectStore>();\n\tif (storeRef.current == null) {\n\t\tif (typeof window === \"undefined\") {\n\t\t\tstoreRef.current = emptyEffectStore;\n\t\t} else {\n\t\t\tstoreRef.current = createEffectStore(_usage);\n\t\t}\n\t}\n\n\tconst store = storeRef.current;\n\tuseSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);\n\tstore._start();\n\t// note: _usage is a constant here, so conditional is okay\n\tif (_usage === UNMANAGED) useIsomorphicLayoutEffect(cleanupTrailingStore);\n\n\treturn store;\n}\n\n/**\n * A wrapper component that renders a Signal's value directly as a Text node or JSX.\n */\nfunction SignalValue({ data }: { data: Signal }) {\n\tconst store = _useSignalsImplementation(1);\n\ttry {\n\t\treturn data.value;\n\t} finally {\n\t\tstore.f();\n\t}\n}\n\n// Decorate Signals so React renders them as <SignalValue> components.\nObject.defineProperties(Signal.prototype, {\n\t$$typeof: { configurable: true, value: ReactElemType },\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\tref: { configurable: true, value: null },\n});\n\nexport function useSignals(usage?: EffectStoreUsage): EffectStore {\n\treturn _useSignalsImplementation(usage);\n}\n\nexport function useSignal<T>(value: T, options?: SignalOptions<T>): Signal<T>;\nexport function useSignal<T = undefined>(): Signal<T | undefined>;\nexport function useSignal<T>(value?: T, options?: SignalOptions<T>) {\n\treturn useMemo(\n\t\t() => signal<T | undefined>(value, options as SignalOptions),\n\t\tEmpty\n\t);\n}\n\nexport function useComputed<T>(\n\tcompute: () => T,\n\toptions?: SignalOptions<T>\n): ReadonlySignal<T> {\n\tconst $compute = useRef(compute);\n\t$compute.current = compute;\n\treturn useMemo(() => computed<T>(() => $compute.current(), options), Empty);\n}\n\nexport function useSignalEffect(\n\tcb: () => void | (() => void),\n\toptions?: EffectOptions\n) {\n\tconst callback = useRef(cb);\n\tcallback.current = cb;\n\n\tuseEffect(() => {\n\t\treturn effect(function (this: Effect) {\n\t\t\treturn callback.current();\n\t\t}, options);\n\t}, Empty);\n}\n"],"names":["major","reactVersion","split","map","Number","Empty","ReactElemType","Symbol","for","wrapJsx","jsx","type","props","rest","i","v","Signal","value","call","symDispose","dispose","currentStore","startComponentEffect","prevStore","nextStore","endEffect","effect","_start","finishComponentEffect","bind","noop","emptyEffectStore","_usage","_sources","undefined","_callback","_dispose","subscribe","getSnapshot","f","_queueMicroTask","Promise","prototype","then","resolve","finalCleanup","ensureFinalCleanup","cleanupTrailingStore","_currentStore","useIsomorphicLayoutEffect","window","useLayoutEffect","useEffect","_useSignalsImplementation","storeRef","useRef","current","effectInstance","onChangeNotifyReact","version","unsubscribe","this","onStoreChange","prevUsage","thisUsage","end","createEffectStore","store","useSyncExternalStore","Object","defineProperties","$$typeof","configurable","data","get","ref","useSignals","usage","useSignal","options","useMemo","signal","useComputed","compute","$compute","computed","useSignalEffect","cb","callback"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runtime.mjs","sources":["../src/index.ts"],"sourcesContent":["import {\n\tsignal,\n\tcomputed,\n\teffect,\n\tSignal,\n\tReadonlySignal,\n\tSignalOptions,\n\tEffectOptions,\n} from \"@preact/signals-core\";\nimport {\n\tuseRef,\n\tuseMemo,\n\tuseEffect,\n\tuseLayoutEffect,\n\tversion as reactVersion,\n} from \"react\";\nimport { useSyncExternalStore } from \"use-sync-external-store/shim/index.js\";\nimport type { SignalsDevToolsAPI } from \"../../../debug/src/devtools\";\n\nconst [major] = reactVersion.split(\".\").map(Number);\nconst Empty = [] as const;\n// V19 https://github.com/facebook/react/blob/346c7d4c43a0717302d446da9e7423a8e28d8996/packages/shared/ReactSymbols.js#L15\n// V18 https://github.com/facebook/react/blob/346c7d4c43a0717302d446da9e7423a8e28d8996/packages/shared/ReactSymbols.js#L15\nconst ReactElemType = Symbol.for(\n\tmajor >= 19 ? \"react.transitional.element\" : \"react.element\"\n);\n\nconst DEVTOOLS_ENABLED =\n\ttypeof window !== \"undefined\" && !!window.__PREACT_SIGNALS_DEVTOOLS__;\n\nexport function wrapJsx<T>(jsx: T): T {\n\tif (typeof jsx !== \"function\") return jsx;\n\n\treturn function (type: any, props: any, ...rest: any[]) {\n\t\tif (typeof type === \"string\" && props) {\n\t\t\tfor (let i in props) {\n\t\t\t\tlet v = props[i];\n\t\t\t\tif (i !== \"children\" && v instanceof Signal) {\n\t\t\t\t\tprops[i] = v.value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn jsx.call(jsx, type, props, ...rest);\n\t} as any as T;\n}\n\nconst symDispose: unique symbol =\n\t(Symbol as any).dispose || Symbol.for(\"Symbol.dispose\");\n\ninterface Effect {\n\t_sources: object | undefined;\n\t_debugCallback?: () => void;\n\t_start(): () => void;\n\t_callback(): void;\n\t_dispose(): void;\n}\n\n/**\n * Use this flag to represent a bare `useSignals` call that doesn't manually\n * close its effect store and relies on auto-closing when the next useSignals is\n * called or after a microtask\n */\nconst UNMANAGED = 0;\n/**\n * Use this flag to represent a `useSignals` call that is manually closed by a\n * try/finally block in a component's render method. This is the default usage\n * that the react-transform plugin uses.\n */\nconst MANAGED_COMPONENT = 1;\n/**\n * Use this flag to represent a `useSignals` call that is manually closed by a\n * try/finally block in a hook body. This is the default usage that the\n * react-transform plugin uses.\n */\nconst MANAGED_HOOK = 2;\n\n/**\n * An enum defining how this store is used. See the documentation for each enum\n * member for more details.\n * @see {@link UNMANAGED}\n * @see {@link MANAGED_COMPONENT}\n * @see {@link MANAGED_HOOK}\n */\ntype EffectStoreUsage =\n\t| typeof UNMANAGED\n\t| typeof MANAGED_COMPONENT\n\t| typeof MANAGED_HOOK;\n\nexport interface EffectStore {\n\t/**\n\t * An enum defining how this hook is used and whether it is invoked in a\n\t * component's body or hook body. See the comment on `EffectStoreUsage` for\n\t * more details.\n\t */\n\treadonly _usage: EffectStoreUsage;\n\treadonly effect: Effect;\n\tsubscribe(onStoreChange: () => void): () => void;\n\tgetSnapshot(): number;\n\t/** startEffect - begin tracking signals used in this component */\n\t_start(): void;\n\t/** finishEffect - stop tracking the signals used in this component */\n\tf(): void;\n\t[symDispose](): void;\n}\n\nlet currentStore: EffectStore | undefined;\n\nfunction startComponentEffect(\n\tprevStore: EffectStore | undefined,\n\tnextStore: EffectStore\n) {\n\tconst endEffect = nextStore.effect._start();\n\tcurrentStore = nextStore;\n\n\treturn finishComponentEffect.bind(nextStore, prevStore, endEffect);\n}\n\nfunction finishComponentEffect(\n\tthis: EffectStore,\n\tprevStore: EffectStore | undefined,\n\tendEffect: () => void\n) {\n\tendEffect();\n\tcurrentStore = prevStore;\n}\n\n/**\n * A redux-like store whose store value is a positive 32bit integer (a\n * 'version').\n *\n * React subscribes to this store and gets a snapshot of the current 'version',\n * whenever the 'version' changes, we tell React it's time to update the\n * component (call 'onStoreChange').\n *\n * How we achieve this is by creating a binding with an 'effect', when the\n * `effect._callback' is called, we update our store version and tell React to\n * re-render the component ([1] We don't really care when/how React does it).\n *\n * [1]\n * @see https://react.dev/reference/react/useSyncExternalStore\n * @see\n * https://github.com/reactjs/rfcs/blob/main/text/0214-use-sync-external-store.md\n *\n * @param _usage An enum defining how this hook is used and whether it is\n * invoked in a component's body or hook body. See the comment on\n * `EffectStoreUsage` for more details.\n */\nfunction createEffectStore(_usage: EffectStoreUsage): EffectStore {\n\tlet effectInstance!: Effect;\n\tlet endEffect: (() => void) | undefined;\n\tlet version = 0;\n\tlet onChangeNotifyReact: (() => void) | undefined;\n\n\tlet unsubscribe = effect(function (this: Effect) {\n\t\teffectInstance = this;\n\t});\n\teffectInstance._callback = function () {\n\t\tversion = (version + 1) | 0;\n\t\tif (DEVTOOLS_ENABLED) {\n\t\t\teffectInstance._debugCallback?.call(effectInstance);\n\t\t}\n\t\tif (onChangeNotifyReact) onChangeNotifyReact();\n\t};\n\n\treturn {\n\t\t_usage,\n\t\teffect: effectInstance,\n\t\tsubscribe(onStoreChange) {\n\t\t\tonChangeNotifyReact = onStoreChange;\n\n\t\t\treturn function () {\n\t\t\t\t/**\n\t\t\t\t * Rotate to next version when unsubscribing to ensure that components are re-run\n\t\t\t\t * when subscribing again.\n\t\t\t\t *\n\t\t\t\t * In StrictMode, 'memo'-ed components seem to keep a stale snapshot version, so\n\t\t\t\t * don't re-run after subscribing again if the version is the same as last time.\n\t\t\t\t *\n\t\t\t\t * Because we unsubscribe from the effect, the version may not change. We simply\n\t\t\t\t * set a new initial version in case of stale snapshots here.\n\t\t\t\t */\n\t\t\t\tversion = (version + 1) | 0;\n\t\t\t\tonChangeNotifyReact = undefined;\n\t\t\t\tunsubscribe();\n\t\t\t};\n\t\t},\n\t\tgetSnapshot() {\n\t\t\treturn version;\n\t\t},\n\t\t_start() {\n\t\t\t// In general, we want to support two kinds of usages of useSignals:\n\t\t\t//\n\t\t\t// A) Managed: calling useSignals in a component or hook body wrapped in a\n\t\t\t// try/finally (like what the react-transform plugin does)\n\t\t\t//\n\t\t\t// B) Unmanaged: Calling useSignals directly without wrapping in a\n\t\t\t// try/finally\n\t\t\t//\n\t\t\t// For managed, we finish the effect in the finally block of the component\n\t\t\t// or hook body. For unmanaged, we finish the effect in the next\n\t\t\t// useSignals call or after a microtask.\n\t\t\t//\n\t\t\t// There are different tradeoffs which each approach. With managed, using\n\t\t\t// a try/finally ensures that only signals used in the component or hook\n\t\t\t// body are tracked. However, signals accessed in render props are missed\n\t\t\t// because the render prop is invoked in another component that may or may\n\t\t\t// not realize it is rendering signals accessed in the render prop it is\n\t\t\t// given.\n\t\t\t//\n\t\t\t// The other approach is \"unmanaged\": to call useSignals directly without\n\t\t\t// wrapping in a try/finally. This approach is easier to manually write in\n\t\t\t// situations where a build step isn't available but does open up the\n\t\t\t// possibility of catching signals accessed in other code before the\n\t\t\t// effect is closed (e.g. in a layout effect). Most situations where this\n\t\t\t// could happen are generally consider bad patterns or bugs. For example,\n\t\t\t// using a signal in a component and not having a call to `useSignals`\n\t\t\t// would be an bug. Or using a signal in `useLayoutEffect` is generally\n\t\t\t// not recommended since that layout effect won't update when the signals'\n\t\t\t// value change.\n\t\t\t//\n\t\t\t// To support both approaches, we need to track how each invocation of\n\t\t\t// useSignals is used, so we can properly transition between different\n\t\t\t// kinds of usages.\n\t\t\t//\n\t\t\t// The following table shows the different scenarios and how we should\n\t\t\t// handle them.\n\t\t\t//\n\t\t\t// Key:\n\t\t\t// 0 = UNMANAGED\n\t\t\t// 1 = MANAGED_COMPONENT\n\t\t\t// 2 = MANAGED_HOOK\n\t\t\t//\n\t\t\t// Pattern:\n\t\t\t// prev store usage -> this store usage: action to take\n\t\t\t//\n\t\t\t// - 0 -> 0: finish previous effect (unknown to unknown)\n\t\t\t//\n\t\t\t// We don't know how the previous effect was used, so we need to finish\n\t\t\t// it before starting the next effect.\n\t\t\t//\n\t\t\t// - 0 -> 1: finish previous effect\n\t\t\t//\n\t\t\t// Assume previous invocation was another component or hook from another\n\t\t\t// component. Nested component renders (renderToStaticMarkup within a\n\t\t\t// component's render) won't be supported with bare useSignals calls.\n\t\t\t//\n\t\t\t// - 0 -> 2: capture & restore\n\t\t\t//\n\t\t\t// Previous invocation could be a component or a hook. Either way,\n\t\t\t// restore it after our invocation so that it can continue to capture\n\t\t\t// any signals after we exit.\n\t\t\t//\n\t\t\t// - 1 -> 0: Do nothing. Signals already captured by current effect store\n\t\t\t// - 1 -> 1: capture & restore (e.g. component calls renderToStaticMarkup)\n\t\t\t// - 1 -> 2: capture & restore (e.g. hook)\n\t\t\t//\n\t\t\t// - 2 -> 0: Do nothing. Signals already captured by current effect store\n\t\t\t// - 2 -> 1: capture & restore (e.g. hook calls renderToStaticMarkup)\n\t\t\t// - 2 -> 2: capture & restore (e.g. nested hook calls)\n\n\t\t\tif (currentStore == undefined) {\n\t\t\t\tendEffect = startComponentEffect(undefined, this);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst prevUsage = currentStore._usage;\n\t\t\tconst thisUsage = this._usage;\n\n\t\t\tif (\n\t\t\t\t(prevUsage == UNMANAGED && thisUsage == UNMANAGED) || // 0 -> 0\n\t\t\t\t(prevUsage == UNMANAGED && thisUsage == MANAGED_COMPONENT) // 0 -> 1\n\t\t\t) {\n\t\t\t\t// finish previous effect\n\t\t\t\tcurrentStore.f();\n\t\t\t\tendEffect = startComponentEffect(undefined, this);\n\t\t\t} else if (\n\t\t\t\t(prevUsage == MANAGED_COMPONENT && thisUsage == UNMANAGED) || // 1 -> 0\n\t\t\t\t(prevUsage == MANAGED_HOOK && thisUsage == UNMANAGED) // 2 -> 0\n\t\t\t) {\n\t\t\t\t// Do nothing since it'll be captured by current effect store\n\t\t\t} else {\n\t\t\t\t// nested scenarios, so capture and restore the previous effect store\n\t\t\t\tendEffect = startComponentEffect(currentStore, this);\n\t\t\t}\n\t\t},\n\t\tf() {\n\t\t\tconst end = endEffect;\n\t\t\tendEffect = undefined;\n\t\t\tend?.();\n\t\t},\n\t\t[symDispose]() {\n\t\t\tthis.f();\n\t\t},\n\t};\n}\n\nconst noop = () => {};\n\nfunction createEmptyEffectStore(): EffectStore {\n\treturn {\n\t\t_usage: UNMANAGED,\n\t\teffect: {\n\t\t\t_sources: undefined,\n\t\t\t_callback() {},\n\t\t\t_start() {\n\t\t\t\treturn /* endEffect */ noop;\n\t\t\t},\n\t\t\t_dispose() {},\n\t\t},\n\t\tsubscribe() {\n\t\t\treturn /* unsubscribe */ noop;\n\t\t},\n\t\tgetSnapshot() {\n\t\t\treturn 0;\n\t\t},\n\t\t_start() {},\n\t\tf() {},\n\t\t[symDispose]() {},\n\t};\n}\n\nconst emptyEffectStore = createEmptyEffectStore();\n\nconst _queueMicroTask = Promise.prototype.then.bind(Promise.resolve());\n\nlet finalCleanup: Promise<void> | undefined;\nexport function ensureFinalCleanup() {\n\tif (!finalCleanup) {\n\t\tfinalCleanup = _queueMicroTask(cleanupTrailingStore);\n\t}\n}\nfunction cleanupTrailingStore() {\n\tfinalCleanup = undefined;\n\tcurrentStore?.f();\n}\n\nconst useIsomorphicLayoutEffect =\n\ttypeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n\n/**\n * Custom hook to create the effect to track signals used during render and\n * subscribe to changes to rerender the component when the signals change.\n */\nexport function _useSignalsImplementation(\n\t_usage: EffectStoreUsage = UNMANAGED\n): EffectStore {\n\tensureFinalCleanup();\n\n\tconst storeRef = useRef<EffectStore>();\n\tif (storeRef.current == null) {\n\t\tif (typeof window === \"undefined\") {\n\t\t\tstoreRef.current = emptyEffectStore;\n\t\t} else {\n\t\t\tstoreRef.current = createEffectStore(_usage);\n\t\t}\n\t}\n\n\tconst store = storeRef.current;\n\tuseSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);\n\tstore._start();\n\t// note: _usage is a constant here, so conditional is okay\n\tif (_usage === UNMANAGED) useIsomorphicLayoutEffect(cleanupTrailingStore);\n\n\treturn store;\n}\n\n/**\n * A wrapper component that renders a Signal's value directly as a Text node or JSX.\n */\nfunction SignalValue({ data }: { data: Signal }) {\n\tconst store = _useSignalsImplementation(1);\n\ttry {\n\t\treturn data.value;\n\t} finally {\n\t\tstore.f();\n\t}\n}\n\n// Decorate Signals so React renders them as <SignalValue> components.\nObject.defineProperties(Signal.prototype, {\n\t$$typeof: { configurable: true, value: ReactElemType },\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\tref: { configurable: true, value: null },\n});\n\nexport function useSignals(usage?: EffectStoreUsage): EffectStore {\n\treturn _useSignalsImplementation(usage);\n}\n\nexport function useSignal<T>(value: T, options?: SignalOptions<T>): Signal<T>;\nexport function useSignal<T = undefined>(): Signal<T | undefined>;\nexport function useSignal<T>(value?: T, options?: SignalOptions<T>) {\n\treturn useMemo(\n\t\t() => signal<T | undefined>(value, options as SignalOptions),\n\t\tEmpty\n\t);\n}\n\nexport function useComputed<T>(\n\tcompute: () => T,\n\toptions?: SignalOptions<T>\n): ReadonlySignal<T> {\n\tconst $compute = useRef(compute);\n\t$compute.current = compute;\n\treturn useMemo(() => computed<T>(() => $compute.current(), options), Empty);\n}\n\nexport function useSignalEffect(\n\tcb: () => void | (() => void),\n\toptions?: EffectOptions\n) {\n\tconst callback = useRef(cb);\n\tcallback.current = cb;\n\n\tuseEffect(() => {\n\t\treturn effect(function (this: Effect) {\n\t\t\treturn callback.current();\n\t\t}, options);\n\t}, Empty);\n}\n\ndeclare global {\n\tinterface Window {\n\t\t__PREACT_SIGNALS_DEVTOOLS__: SignalsDevToolsAPI;\n\t}\n}\n"],"names":["major","reactVersion","split","map","Number","Empty","ReactElemType","Symbol","for","DEVTOOLS_ENABLED","window","__PREACT_SIGNALS_DEVTOOLS__","wrapJsx","jsx","type","props","rest","i","v","Signal","value","call","symDispose","dispose","currentStore","startComponentEffect","prevStore","nextStore","endEffect","effect","_start","finishComponentEffect","bind","noop","emptyEffectStore","_usage","_sources","undefined","_callback","_dispose","subscribe","getSnapshot","f","_queueMicroTask","Promise","prototype","then","resolve","finalCleanup","ensureFinalCleanup","cleanupTrailingStore","_currentStore","useIsomorphicLayoutEffect","useLayoutEffect","useEffect","_useSignalsImplementation","storeRef","useRef","current","effectInstance","onChangeNotifyReact","version","unsubscribe","this","_effectInstance$_debu","_debugCallback","onStoreChange","prevUsage","thisUsage","end","createEffectStore","store","useSyncExternalStore","Object","defineProperties","$$typeof","configurable","data","get","ref","useSignals","usage","useSignal","options","useMemo","signal","useComputed","compute","$compute","computed","useSignalEffect","cb","callback"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Signal as n,signal as t,computed as
|
|
1
|
+
import{Signal as n,signal as t,computed as i,effect as r}from"@preact/signals-core";import{version as e,useRef as o,useMemo as u,useEffect as f,useLayoutEffect as c}from"react";import{useSyncExternalStore as a}from"use-sync-external-store/shim/index.js";var s=e.split(".").map(Number)[0],l=[],v=Symbol.for(s>=19?"react.transitional.element":"react.element"),d="undefined"!=typeof window&&!!window.__PREACT_SIGNALS_DEVTOOLS__;function p(t){if("function"!=typeof t)return t;else return function(i,r){if("string"==typeof i&&r)for(var e in r){var o=r[e];if("children"!==e&&o instanceof n)r[e]=o.value}return t.call.apply(t,[t,i,r].concat([].slice.call(arguments,2)))}}var m,b=Symbol.dispose||Symbol.for("Symbol.dispose");function y(n,t){var i=t.effect.S();m=t;return g.bind(t,n,i)}function g(n,t){t();m=n}var h,w,_=function(){},S=((h={o:0,effect:{s:void 0,c:function(){},S:function(){return _},d:function(){}},subscribe:function(){return _},getSnapshot:function(){return 0},S:function(){},f:function(){}})[b]=function(){},h),x=Promise.prototype.then.bind(Promise.resolve());function j(){if(!w)w=x(P)}function P(){var n;w=void 0;null==(n=m)||n.f()}var $="undefined"!=typeof window?c:f;function k(n){if(void 0===n)n=0;j();var t=o();if(null==t.current)if("undefined"==typeof window)t.current=S;else t.current=function(n){var t,i,e,o,u=0,f=r(function(){i=this});i.c=function(){u=u+1|0;if(d){var n;null==(n=i.y)||n.call(i)}if(o)o()};return(t={o:n,effect:i,subscribe:function(n){o=n;return function(){u=u+1|0;o=void 0;f()}},getSnapshot:function(){return u},S:function(){if(null!=m){var n=m.o,t=this.o;if(0==n&&0==t||0==n&&1==t){m.f();e=y(void 0,this)}else if(1==n&&0==t||2==n&&0==t);else e=y(m,this)}else e=y(void 0,this)},f:function(){var n=e;e=void 0;null==n||n()}})[b]=function(){this.f()},t}(n);var i=t.current;a(i.subscribe,i.getSnapshot,i.getSnapshot);i.S();if(0===n)$(P);return i}Object.defineProperties(n.prototype,{$$typeof:{configurable:!0,value:v},type:{configurable:!0,value:function(n){var t=n.data,i=k(1);try{return t.value}finally{i.f()}}},props:{configurable:!0,get:function(){return{data:this}}},ref:{configurable:!0,value:null}});function N(n){return k(n)}function useSignal(n,i){return u(function(){return t(n,i)},l)}function useComputed(n,t){var r=o(n);r.current=n;return u(function(){return i(function(){return r.current()},t)},l)}function useSignalEffect(n,t){var i=o(n);i.current=n;f(function(){return r(function(){return i.current()},t)},l)}export{k as _useSignalsImplementation,j as ensureFinalCleanup,useComputed,useSignal,useSignalEffect,N as useSignals,p as wrapJsx};//# sourceMappingURL=runtime.module.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.module.js","sources":["../src/index.ts"],"sourcesContent":["import {\n\tsignal,\n\tcomputed,\n\teffect,\n\tSignal,\n\tReadonlySignal,\n\tSignalOptions,\n\tEffectOptions,\n} from \"@preact/signals-core\";\nimport {\n\tuseRef,\n\tuseMemo,\n\tuseEffect,\n\tuseLayoutEffect,\n\tversion as reactVersion,\n} from \"react\";\nimport { useSyncExternalStore } from \"use-sync-external-store/shim/index.js\";\n\nconst [major] = reactVersion.split(\".\").map(Number);\nconst Empty = [] as const;\n// V19 https://github.com/facebook/react/blob/346c7d4c43a0717302d446da9e7423a8e28d8996/packages/shared/ReactSymbols.js#L15\n// V18 https://github.com/facebook/react/blob/346c7d4c43a0717302d446da9e7423a8e28d8996/packages/shared/ReactSymbols.js#L15\nconst ReactElemType = Symbol.for(\n\tmajor >= 19 ? \"react.transitional.element\" : \"react.element\"\n);\n\nexport function wrapJsx<T>(jsx: T): T {\n\tif (typeof jsx !== \"function\") return jsx;\n\n\treturn function (type: any, props: any, ...rest: any[]) {\n\t\tif (typeof type === \"string\" && props) {\n\t\t\tfor (let i in props) {\n\t\t\t\tlet v = props[i];\n\t\t\t\tif (i !== \"children\" && v instanceof Signal) {\n\t\t\t\t\tprops[i] = v.value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn jsx.call(jsx, type, props, ...rest);\n\t} as any as T;\n}\n\nconst symDispose: unique symbol =\n\t(Symbol as any).dispose || Symbol.for(\"Symbol.dispose\");\n\ninterface Effect {\n\t_sources: object | undefined;\n\t_start(): () => void;\n\t_callback(): void;\n\t_dispose(): void;\n}\n\n/**\n * Use this flag to represent a bare `useSignals` call that doesn't manually\n * close its effect store and relies on auto-closing when the next useSignals is\n * called or after a microtask\n */\nconst UNMANAGED = 0;\n/**\n * Use this flag to represent a `useSignals` call that is manually closed by a\n * try/finally block in a component's render method. This is the default usage\n * that the react-transform plugin uses.\n */\nconst MANAGED_COMPONENT = 1;\n/**\n * Use this flag to represent a `useSignals` call that is manually closed by a\n * try/finally block in a hook body. This is the default usage that the\n * react-transform plugin uses.\n */\nconst MANAGED_HOOK = 2;\n\n/**\n * An enum defining how this store is used. See the documentation for each enum\n * member for more details.\n * @see {@link UNMANAGED}\n * @see {@link MANAGED_COMPONENT}\n * @see {@link MANAGED_HOOK}\n */\ntype EffectStoreUsage =\n\t| typeof UNMANAGED\n\t| typeof MANAGED_COMPONENT\n\t| typeof MANAGED_HOOK;\n\nexport interface EffectStore {\n\t/**\n\t * An enum defining how this hook is used and whether it is invoked in a\n\t * component's body or hook body. See the comment on `EffectStoreUsage` for\n\t * more details.\n\t */\n\treadonly _usage: EffectStoreUsage;\n\treadonly effect: Effect;\n\tsubscribe(onStoreChange: () => void): () => void;\n\tgetSnapshot(): number;\n\t/** startEffect - begin tracking signals used in this component */\n\t_start(): void;\n\t/** finishEffect - stop tracking the signals used in this component */\n\tf(): void;\n\t[symDispose](): void;\n}\n\nlet currentStore: EffectStore | undefined;\n\nfunction startComponentEffect(\n\tprevStore: EffectStore | undefined,\n\tnextStore: EffectStore\n) {\n\tconst endEffect = nextStore.effect._start();\n\tcurrentStore = nextStore;\n\n\treturn finishComponentEffect.bind(nextStore, prevStore, endEffect);\n}\n\nfunction finishComponentEffect(\n\tthis: EffectStore,\n\tprevStore: EffectStore | undefined,\n\tendEffect: () => void\n) {\n\tendEffect();\n\tcurrentStore = prevStore;\n}\n\n/**\n * A redux-like store whose store value is a positive 32bit integer (a\n * 'version').\n *\n * React subscribes to this store and gets a snapshot of the current 'version',\n * whenever the 'version' changes, we tell React it's time to update the\n * component (call 'onStoreChange').\n *\n * How we achieve this is by creating a binding with an 'effect', when the\n * `effect._callback' is called, we update our store version and tell React to\n * re-render the component ([1] We don't really care when/how React does it).\n *\n * [1]\n * @see https://react.dev/reference/react/useSyncExternalStore\n * @see\n * https://github.com/reactjs/rfcs/blob/main/text/0214-use-sync-external-store.md\n *\n * @param _usage An enum defining how this hook is used and whether it is\n * invoked in a component's body or hook body. See the comment on\n * `EffectStoreUsage` for more details.\n */\nfunction createEffectStore(_usage: EffectStoreUsage): EffectStore {\n\tlet effectInstance!: Effect;\n\tlet endEffect: (() => void) | undefined;\n\tlet version = 0;\n\tlet onChangeNotifyReact: (() => void) | undefined;\n\n\tlet unsubscribe = effect(function (this: Effect) {\n\t\teffectInstance = this;\n\t});\n\teffectInstance._callback = function () {\n\t\tversion = (version + 1) | 0;\n\t\tif (onChangeNotifyReact) onChangeNotifyReact();\n\t};\n\n\treturn {\n\t\t_usage,\n\t\teffect: effectInstance,\n\t\tsubscribe(onStoreChange) {\n\t\t\tonChangeNotifyReact = onStoreChange;\n\n\t\t\treturn function () {\n\t\t\t\t/**\n\t\t\t\t * Rotate to next version when unsubscribing to ensure that components are re-run\n\t\t\t\t * when subscribing again.\n\t\t\t\t *\n\t\t\t\t * In StrictMode, 'memo'-ed components seem to keep a stale snapshot version, so\n\t\t\t\t * don't re-run after subscribing again if the version is the same as last time.\n\t\t\t\t *\n\t\t\t\t * Because we unsubscribe from the effect, the version may not change. We simply\n\t\t\t\t * set a new initial version in case of stale snapshots here.\n\t\t\t\t */\n\t\t\t\tversion = (version + 1) | 0;\n\t\t\t\tonChangeNotifyReact = undefined;\n\t\t\t\tunsubscribe();\n\t\t\t};\n\t\t},\n\t\tgetSnapshot() {\n\t\t\treturn version;\n\t\t},\n\t\t_start() {\n\t\t\t// In general, we want to support two kinds of usages of useSignals:\n\t\t\t//\n\t\t\t// A) Managed: calling useSignals in a component or hook body wrapped in a\n\t\t\t// try/finally (like what the react-transform plugin does)\n\t\t\t//\n\t\t\t// B) Unmanaged: Calling useSignals directly without wrapping in a\n\t\t\t// try/finally\n\t\t\t//\n\t\t\t// For managed, we finish the effect in the finally block of the component\n\t\t\t// or hook body. For unmanaged, we finish the effect in the next\n\t\t\t// useSignals call or after a microtask.\n\t\t\t//\n\t\t\t// There are different tradeoffs which each approach. With managed, using\n\t\t\t// a try/finally ensures that only signals used in the component or hook\n\t\t\t// body are tracked. However, signals accessed in render props are missed\n\t\t\t// because the render prop is invoked in another component that may or may\n\t\t\t// not realize it is rendering signals accessed in the render prop it is\n\t\t\t// given.\n\t\t\t//\n\t\t\t// The other approach is \"unmanaged\": to call useSignals directly without\n\t\t\t// wrapping in a try/finally. This approach is easier to manually write in\n\t\t\t// situations where a build step isn't available but does open up the\n\t\t\t// possibility of catching signals accessed in other code before the\n\t\t\t// effect is closed (e.g. in a layout effect). Most situations where this\n\t\t\t// could happen are generally consider bad patterns or bugs. For example,\n\t\t\t// using a signal in a component and not having a call to `useSignals`\n\t\t\t// would be an bug. Or using a signal in `useLayoutEffect` is generally\n\t\t\t// not recommended since that layout effect won't update when the signals'\n\t\t\t// value change.\n\t\t\t//\n\t\t\t// To support both approaches, we need to track how each invocation of\n\t\t\t// useSignals is used, so we can properly transition between different\n\t\t\t// kinds of usages.\n\t\t\t//\n\t\t\t// The following table shows the different scenarios and how we should\n\t\t\t// handle them.\n\t\t\t//\n\t\t\t// Key:\n\t\t\t// 0 = UNMANAGED\n\t\t\t// 1 = MANAGED_COMPONENT\n\t\t\t// 2 = MANAGED_HOOK\n\t\t\t//\n\t\t\t// Pattern:\n\t\t\t// prev store usage -> this store usage: action to take\n\t\t\t//\n\t\t\t// - 0 -> 0: finish previous effect (unknown to unknown)\n\t\t\t//\n\t\t\t// We don't know how the previous effect was used, so we need to finish\n\t\t\t// it before starting the next effect.\n\t\t\t//\n\t\t\t// - 0 -> 1: finish previous effect\n\t\t\t//\n\t\t\t// Assume previous invocation was another component or hook from another\n\t\t\t// component. Nested component renders (renderToStaticMarkup within a\n\t\t\t// component's render) won't be supported with bare useSignals calls.\n\t\t\t//\n\t\t\t// - 0 -> 2: capture & restore\n\t\t\t//\n\t\t\t// Previous invocation could be a component or a hook. Either way,\n\t\t\t// restore it after our invocation so that it can continue to capture\n\t\t\t// any signals after we exit.\n\t\t\t//\n\t\t\t// - 1 -> 0: Do nothing. Signals already captured by current effect store\n\t\t\t// - 1 -> 1: capture & restore (e.g. component calls renderToStaticMarkup)\n\t\t\t// - 1 -> 2: capture & restore (e.g. hook)\n\t\t\t//\n\t\t\t// - 2 -> 0: Do nothing. Signals already captured by current effect store\n\t\t\t// - 2 -> 1: capture & restore (e.g. hook calls renderToStaticMarkup)\n\t\t\t// - 2 -> 2: capture & restore (e.g. nested hook calls)\n\n\t\t\tif (currentStore == undefined) {\n\t\t\t\tendEffect = startComponentEffect(undefined, this);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst prevUsage = currentStore._usage;\n\t\t\tconst thisUsage = this._usage;\n\n\t\t\tif (\n\t\t\t\t(prevUsage == UNMANAGED && thisUsage == UNMANAGED) || // 0 -> 0\n\t\t\t\t(prevUsage == UNMANAGED && thisUsage == MANAGED_COMPONENT) // 0 -> 1\n\t\t\t) {\n\t\t\t\t// finish previous effect\n\t\t\t\tcurrentStore.f();\n\t\t\t\tendEffect = startComponentEffect(undefined, this);\n\t\t\t} else if (\n\t\t\t\t(prevUsage == MANAGED_COMPONENT && thisUsage == UNMANAGED) || // 1 -> 0\n\t\t\t\t(prevUsage == MANAGED_HOOK && thisUsage == UNMANAGED) // 2 -> 0\n\t\t\t) {\n\t\t\t\t// Do nothing since it'll be captured by current effect store\n\t\t\t} else {\n\t\t\t\t// nested scenarios, so capture and restore the previous effect store\n\t\t\t\tendEffect = startComponentEffect(currentStore, this);\n\t\t\t}\n\t\t},\n\t\tf() {\n\t\t\tconst end = endEffect;\n\t\t\tendEffect = undefined;\n\t\t\tend?.();\n\t\t},\n\t\t[symDispose]() {\n\t\t\tthis.f();\n\t\t},\n\t};\n}\n\nconst noop = () => {};\n\nfunction createEmptyEffectStore(): EffectStore {\n\treturn {\n\t\t_usage: UNMANAGED,\n\t\teffect: {\n\t\t\t_sources: undefined,\n\t\t\t_callback() {},\n\t\t\t_start() {\n\t\t\t\treturn /* endEffect */ noop;\n\t\t\t},\n\t\t\t_dispose() {},\n\t\t},\n\t\tsubscribe() {\n\t\t\treturn /* unsubscribe */ noop;\n\t\t},\n\t\tgetSnapshot() {\n\t\t\treturn 0;\n\t\t},\n\t\t_start() {},\n\t\tf() {},\n\t\t[symDispose]() {},\n\t};\n}\n\nconst emptyEffectStore = createEmptyEffectStore();\n\nconst _queueMicroTask = Promise.prototype.then.bind(Promise.resolve());\n\nlet finalCleanup: Promise<void> | undefined;\nexport function ensureFinalCleanup() {\n\tif (!finalCleanup) {\n\t\tfinalCleanup = _queueMicroTask(cleanupTrailingStore);\n\t}\n}\nfunction cleanupTrailingStore() {\n\tfinalCleanup = undefined;\n\tcurrentStore?.f();\n}\n\nconst useIsomorphicLayoutEffect =\n\ttypeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n\n/**\n * Custom hook to create the effect to track signals used during render and\n * subscribe to changes to rerender the component when the signals change.\n */\nexport function _useSignalsImplementation(\n\t_usage: EffectStoreUsage = UNMANAGED\n): EffectStore {\n\tensureFinalCleanup();\n\n\tconst storeRef = useRef<EffectStore>();\n\tif (storeRef.current == null) {\n\t\tif (typeof window === \"undefined\") {\n\t\t\tstoreRef.current = emptyEffectStore;\n\t\t} else {\n\t\t\tstoreRef.current = createEffectStore(_usage);\n\t\t}\n\t}\n\n\tconst store = storeRef.current;\n\tuseSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);\n\tstore._start();\n\t// note: _usage is a constant here, so conditional is okay\n\tif (_usage === UNMANAGED) useIsomorphicLayoutEffect(cleanupTrailingStore);\n\n\treturn store;\n}\n\n/**\n * A wrapper component that renders a Signal's value directly as a Text node or JSX.\n */\nfunction SignalValue({ data }: { data: Signal }) {\n\tconst store = _useSignalsImplementation(1);\n\ttry {\n\t\treturn data.value;\n\t} finally {\n\t\tstore.f();\n\t}\n}\n\n// Decorate Signals so React renders them as <SignalValue> components.\nObject.defineProperties(Signal.prototype, {\n\t$$typeof: { configurable: true, value: ReactElemType },\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\tref: { configurable: true, value: null },\n});\n\nexport function useSignals(usage?: EffectStoreUsage): EffectStore {\n\treturn _useSignalsImplementation(usage);\n}\n\nexport function useSignal<T>(value: T, options?: SignalOptions<T>): Signal<T>;\nexport function useSignal<T = undefined>(): Signal<T | undefined>;\nexport function useSignal<T>(value?: T, options?: SignalOptions<T>) {\n\treturn useMemo(\n\t\t() => signal<T | undefined>(value, options as SignalOptions),\n\t\tEmpty\n\t);\n}\n\nexport function useComputed<T>(\n\tcompute: () => T,\n\toptions?: SignalOptions<T>\n): ReadonlySignal<T> {\n\tconst $compute = useRef(compute);\n\t$compute.current = compute;\n\treturn useMemo(() => computed<T>(() => $compute.current(), options), Empty);\n}\n\nexport function useSignalEffect(\n\tcb: () => void | (() => void),\n\toptions?: EffectOptions\n) {\n\tconst callback = useRef(cb);\n\tcallback.current = cb;\n\n\tuseEffect(() => {\n\t\treturn effect(function (this: Effect) {\n\t\t\treturn callback.current();\n\t\t}, options);\n\t}, Empty);\n}\n"],"names":["major","reactVersion","split","map","Number","Empty","ReactElemType","Symbol","wrapJsx","jsx","type","props","i","v","Signal","value","call","apply","concat","slice","arguments","currentStore","symDispose","dispose","startComponentEffect","prevStore","nextStore","endEffect","effect","_start","finishComponentEffect","bind","_ref2","finalCleanup","noop","emptyEffectStore","_usage","_sources","undefined","_callback","_dispose","subscribe","getSnapshot","f","_queueMicroTask","Promise","prototype","then","resolve","ensureFinalCleanup","cleanupTrailingStore","_currentStore","useIsomorphicLayoutEffect","window","useLayoutEffect","useEffect","_useSignalsImplementation","storeRef","useRef","current","_ref","effectInstance","onChangeNotifyReact","version","unsubscribe","this","onStoreChange","prevUsage","thisUsage","end","createEffectStore","store","useSyncExternalStore","Object","defineProperties","$$typeof","configurable","_ref3","data","get","ref","useSignals","usage","useSignal","options","useMemo","signal","useComputed","compute","$compute","computed","useSignalEffect","cb","callback"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runtime.module.js","sources":["../src/index.ts"],"sourcesContent":["import {\n\tsignal,\n\tcomputed,\n\teffect,\n\tSignal,\n\tReadonlySignal,\n\tSignalOptions,\n\tEffectOptions,\n} from \"@preact/signals-core\";\nimport {\n\tuseRef,\n\tuseMemo,\n\tuseEffect,\n\tuseLayoutEffect,\n\tversion as reactVersion,\n} from \"react\";\nimport { useSyncExternalStore } from \"use-sync-external-store/shim/index.js\";\nimport type { SignalsDevToolsAPI } from \"../../../debug/src/devtools\";\n\nconst [major] = reactVersion.split(\".\").map(Number);\nconst Empty = [] as const;\n// V19 https://github.com/facebook/react/blob/346c7d4c43a0717302d446da9e7423a8e28d8996/packages/shared/ReactSymbols.js#L15\n// V18 https://github.com/facebook/react/blob/346c7d4c43a0717302d446da9e7423a8e28d8996/packages/shared/ReactSymbols.js#L15\nconst ReactElemType = Symbol.for(\n\tmajor >= 19 ? \"react.transitional.element\" : \"react.element\"\n);\n\nconst DEVTOOLS_ENABLED =\n\ttypeof window !== \"undefined\" && !!window.__PREACT_SIGNALS_DEVTOOLS__;\n\nexport function wrapJsx<T>(jsx: T): T {\n\tif (typeof jsx !== \"function\") return jsx;\n\n\treturn function (type: any, props: any, ...rest: any[]) {\n\t\tif (typeof type === \"string\" && props) {\n\t\t\tfor (let i in props) {\n\t\t\t\tlet v = props[i];\n\t\t\t\tif (i !== \"children\" && v instanceof Signal) {\n\t\t\t\t\tprops[i] = v.value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn jsx.call(jsx, type, props, ...rest);\n\t} as any as T;\n}\n\nconst symDispose: unique symbol =\n\t(Symbol as any).dispose || Symbol.for(\"Symbol.dispose\");\n\ninterface Effect {\n\t_sources: object | undefined;\n\t_debugCallback?: () => void;\n\t_start(): () => void;\n\t_callback(): void;\n\t_dispose(): void;\n}\n\n/**\n * Use this flag to represent a bare `useSignals` call that doesn't manually\n * close its effect store and relies on auto-closing when the next useSignals is\n * called or after a microtask\n */\nconst UNMANAGED = 0;\n/**\n * Use this flag to represent a `useSignals` call that is manually closed by a\n * try/finally block in a component's render method. This is the default usage\n * that the react-transform plugin uses.\n */\nconst MANAGED_COMPONENT = 1;\n/**\n * Use this flag to represent a `useSignals` call that is manually closed by a\n * try/finally block in a hook body. This is the default usage that the\n * react-transform plugin uses.\n */\nconst MANAGED_HOOK = 2;\n\n/**\n * An enum defining how this store is used. See the documentation for each enum\n * member for more details.\n * @see {@link UNMANAGED}\n * @see {@link MANAGED_COMPONENT}\n * @see {@link MANAGED_HOOK}\n */\ntype EffectStoreUsage =\n\t| typeof UNMANAGED\n\t| typeof MANAGED_COMPONENT\n\t| typeof MANAGED_HOOK;\n\nexport interface EffectStore {\n\t/**\n\t * An enum defining how this hook is used and whether it is invoked in a\n\t * component's body or hook body. See the comment on `EffectStoreUsage` for\n\t * more details.\n\t */\n\treadonly _usage: EffectStoreUsage;\n\treadonly effect: Effect;\n\tsubscribe(onStoreChange: () => void): () => void;\n\tgetSnapshot(): number;\n\t/** startEffect - begin tracking signals used in this component */\n\t_start(): void;\n\t/** finishEffect - stop tracking the signals used in this component */\n\tf(): void;\n\t[symDispose](): void;\n}\n\nlet currentStore: EffectStore | undefined;\n\nfunction startComponentEffect(\n\tprevStore: EffectStore | undefined,\n\tnextStore: EffectStore\n) {\n\tconst endEffect = nextStore.effect._start();\n\tcurrentStore = nextStore;\n\n\treturn finishComponentEffect.bind(nextStore, prevStore, endEffect);\n}\n\nfunction finishComponentEffect(\n\tthis: EffectStore,\n\tprevStore: EffectStore | undefined,\n\tendEffect: () => void\n) {\n\tendEffect();\n\tcurrentStore = prevStore;\n}\n\n/**\n * A redux-like store whose store value is a positive 32bit integer (a\n * 'version').\n *\n * React subscribes to this store and gets a snapshot of the current 'version',\n * whenever the 'version' changes, we tell React it's time to update the\n * component (call 'onStoreChange').\n *\n * How we achieve this is by creating a binding with an 'effect', when the\n * `effect._callback' is called, we update our store version and tell React to\n * re-render the component ([1] We don't really care when/how React does it).\n *\n * [1]\n * @see https://react.dev/reference/react/useSyncExternalStore\n * @see\n * https://github.com/reactjs/rfcs/blob/main/text/0214-use-sync-external-store.md\n *\n * @param _usage An enum defining how this hook is used and whether it is\n * invoked in a component's body or hook body. See the comment on\n * `EffectStoreUsage` for more details.\n */\nfunction createEffectStore(_usage: EffectStoreUsage): EffectStore {\n\tlet effectInstance!: Effect;\n\tlet endEffect: (() => void) | undefined;\n\tlet version = 0;\n\tlet onChangeNotifyReact: (() => void) | undefined;\n\n\tlet unsubscribe = effect(function (this: Effect) {\n\t\teffectInstance = this;\n\t});\n\teffectInstance._callback = function () {\n\t\tversion = (version + 1) | 0;\n\t\tif (DEVTOOLS_ENABLED) {\n\t\t\teffectInstance._debugCallback?.call(effectInstance);\n\t\t}\n\t\tif (onChangeNotifyReact) onChangeNotifyReact();\n\t};\n\n\treturn {\n\t\t_usage,\n\t\teffect: effectInstance,\n\t\tsubscribe(onStoreChange) {\n\t\t\tonChangeNotifyReact = onStoreChange;\n\n\t\t\treturn function () {\n\t\t\t\t/**\n\t\t\t\t * Rotate to next version when unsubscribing to ensure that components are re-run\n\t\t\t\t * when subscribing again.\n\t\t\t\t *\n\t\t\t\t * In StrictMode, 'memo'-ed components seem to keep a stale snapshot version, so\n\t\t\t\t * don't re-run after subscribing again if the version is the same as last time.\n\t\t\t\t *\n\t\t\t\t * Because we unsubscribe from the effect, the version may not change. We simply\n\t\t\t\t * set a new initial version in case of stale snapshots here.\n\t\t\t\t */\n\t\t\t\tversion = (version + 1) | 0;\n\t\t\t\tonChangeNotifyReact = undefined;\n\t\t\t\tunsubscribe();\n\t\t\t};\n\t\t},\n\t\tgetSnapshot() {\n\t\t\treturn version;\n\t\t},\n\t\t_start() {\n\t\t\t// In general, we want to support two kinds of usages of useSignals:\n\t\t\t//\n\t\t\t// A) Managed: calling useSignals in a component or hook body wrapped in a\n\t\t\t// try/finally (like what the react-transform plugin does)\n\t\t\t//\n\t\t\t// B) Unmanaged: Calling useSignals directly without wrapping in a\n\t\t\t// try/finally\n\t\t\t//\n\t\t\t// For managed, we finish the effect in the finally block of the component\n\t\t\t// or hook body. For unmanaged, we finish the effect in the next\n\t\t\t// useSignals call or after a microtask.\n\t\t\t//\n\t\t\t// There are different tradeoffs which each approach. With managed, using\n\t\t\t// a try/finally ensures that only signals used in the component or hook\n\t\t\t// body are tracked. However, signals accessed in render props are missed\n\t\t\t// because the render prop is invoked in another component that may or may\n\t\t\t// not realize it is rendering signals accessed in the render prop it is\n\t\t\t// given.\n\t\t\t//\n\t\t\t// The other approach is \"unmanaged\": to call useSignals directly without\n\t\t\t// wrapping in a try/finally. This approach is easier to manually write in\n\t\t\t// situations where a build step isn't available but does open up the\n\t\t\t// possibility of catching signals accessed in other code before the\n\t\t\t// effect is closed (e.g. in a layout effect). Most situations where this\n\t\t\t// could happen are generally consider bad patterns or bugs. For example,\n\t\t\t// using a signal in a component and not having a call to `useSignals`\n\t\t\t// would be an bug. Or using a signal in `useLayoutEffect` is generally\n\t\t\t// not recommended since that layout effect won't update when the signals'\n\t\t\t// value change.\n\t\t\t//\n\t\t\t// To support both approaches, we need to track how each invocation of\n\t\t\t// useSignals is used, so we can properly transition between different\n\t\t\t// kinds of usages.\n\t\t\t//\n\t\t\t// The following table shows the different scenarios and how we should\n\t\t\t// handle them.\n\t\t\t//\n\t\t\t// Key:\n\t\t\t// 0 = UNMANAGED\n\t\t\t// 1 = MANAGED_COMPONENT\n\t\t\t// 2 = MANAGED_HOOK\n\t\t\t//\n\t\t\t// Pattern:\n\t\t\t// prev store usage -> this store usage: action to take\n\t\t\t//\n\t\t\t// - 0 -> 0: finish previous effect (unknown to unknown)\n\t\t\t//\n\t\t\t// We don't know how the previous effect was used, so we need to finish\n\t\t\t// it before starting the next effect.\n\t\t\t//\n\t\t\t// - 0 -> 1: finish previous effect\n\t\t\t//\n\t\t\t// Assume previous invocation was another component or hook from another\n\t\t\t// component. Nested component renders (renderToStaticMarkup within a\n\t\t\t// component's render) won't be supported with bare useSignals calls.\n\t\t\t//\n\t\t\t// - 0 -> 2: capture & restore\n\t\t\t//\n\t\t\t// Previous invocation could be a component or a hook. Either way,\n\t\t\t// restore it after our invocation so that it can continue to capture\n\t\t\t// any signals after we exit.\n\t\t\t//\n\t\t\t// - 1 -> 0: Do nothing. Signals already captured by current effect store\n\t\t\t// - 1 -> 1: capture & restore (e.g. component calls renderToStaticMarkup)\n\t\t\t// - 1 -> 2: capture & restore (e.g. hook)\n\t\t\t//\n\t\t\t// - 2 -> 0: Do nothing. Signals already captured by current effect store\n\t\t\t// - 2 -> 1: capture & restore (e.g. hook calls renderToStaticMarkup)\n\t\t\t// - 2 -> 2: capture & restore (e.g. nested hook calls)\n\n\t\t\tif (currentStore == undefined) {\n\t\t\t\tendEffect = startComponentEffect(undefined, this);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst prevUsage = currentStore._usage;\n\t\t\tconst thisUsage = this._usage;\n\n\t\t\tif (\n\t\t\t\t(prevUsage == UNMANAGED && thisUsage == UNMANAGED) || // 0 -> 0\n\t\t\t\t(prevUsage == UNMANAGED && thisUsage == MANAGED_COMPONENT) // 0 -> 1\n\t\t\t) {\n\t\t\t\t// finish previous effect\n\t\t\t\tcurrentStore.f();\n\t\t\t\tendEffect = startComponentEffect(undefined, this);\n\t\t\t} else if (\n\t\t\t\t(prevUsage == MANAGED_COMPONENT && thisUsage == UNMANAGED) || // 1 -> 0\n\t\t\t\t(prevUsage == MANAGED_HOOK && thisUsage == UNMANAGED) // 2 -> 0\n\t\t\t) {\n\t\t\t\t// Do nothing since it'll be captured by current effect store\n\t\t\t} else {\n\t\t\t\t// nested scenarios, so capture and restore the previous effect store\n\t\t\t\tendEffect = startComponentEffect(currentStore, this);\n\t\t\t}\n\t\t},\n\t\tf() {\n\t\t\tconst end = endEffect;\n\t\t\tendEffect = undefined;\n\t\t\tend?.();\n\t\t},\n\t\t[symDispose]() {\n\t\t\tthis.f();\n\t\t},\n\t};\n}\n\nconst noop = () => {};\n\nfunction createEmptyEffectStore(): EffectStore {\n\treturn {\n\t\t_usage: UNMANAGED,\n\t\teffect: {\n\t\t\t_sources: undefined,\n\t\t\t_callback() {},\n\t\t\t_start() {\n\t\t\t\treturn /* endEffect */ noop;\n\t\t\t},\n\t\t\t_dispose() {},\n\t\t},\n\t\tsubscribe() {\n\t\t\treturn /* unsubscribe */ noop;\n\t\t},\n\t\tgetSnapshot() {\n\t\t\treturn 0;\n\t\t},\n\t\t_start() {},\n\t\tf() {},\n\t\t[symDispose]() {},\n\t};\n}\n\nconst emptyEffectStore = createEmptyEffectStore();\n\nconst _queueMicroTask = Promise.prototype.then.bind(Promise.resolve());\n\nlet finalCleanup: Promise<void> | undefined;\nexport function ensureFinalCleanup() {\n\tif (!finalCleanup) {\n\t\tfinalCleanup = _queueMicroTask(cleanupTrailingStore);\n\t}\n}\nfunction cleanupTrailingStore() {\n\tfinalCleanup = undefined;\n\tcurrentStore?.f();\n}\n\nconst useIsomorphicLayoutEffect =\n\ttypeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n\n/**\n * Custom hook to create the effect to track signals used during render and\n * subscribe to changes to rerender the component when the signals change.\n */\nexport function _useSignalsImplementation(\n\t_usage: EffectStoreUsage = UNMANAGED\n): EffectStore {\n\tensureFinalCleanup();\n\n\tconst storeRef = useRef<EffectStore>();\n\tif (storeRef.current == null) {\n\t\tif (typeof window === \"undefined\") {\n\t\t\tstoreRef.current = emptyEffectStore;\n\t\t} else {\n\t\t\tstoreRef.current = createEffectStore(_usage);\n\t\t}\n\t}\n\n\tconst store = storeRef.current;\n\tuseSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);\n\tstore._start();\n\t// note: _usage is a constant here, so conditional is okay\n\tif (_usage === UNMANAGED) useIsomorphicLayoutEffect(cleanupTrailingStore);\n\n\treturn store;\n}\n\n/**\n * A wrapper component that renders a Signal's value directly as a Text node or JSX.\n */\nfunction SignalValue({ data }: { data: Signal }) {\n\tconst store = _useSignalsImplementation(1);\n\ttry {\n\t\treturn data.value;\n\t} finally {\n\t\tstore.f();\n\t}\n}\n\n// Decorate Signals so React renders them as <SignalValue> components.\nObject.defineProperties(Signal.prototype, {\n\t$$typeof: { configurable: true, value: ReactElemType },\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\tref: { configurable: true, value: null },\n});\n\nexport function useSignals(usage?: EffectStoreUsage): EffectStore {\n\treturn _useSignalsImplementation(usage);\n}\n\nexport function useSignal<T>(value: T, options?: SignalOptions<T>): Signal<T>;\nexport function useSignal<T = undefined>(): Signal<T | undefined>;\nexport function useSignal<T>(value?: T, options?: SignalOptions<T>) {\n\treturn useMemo(\n\t\t() => signal<T | undefined>(value, options as SignalOptions),\n\t\tEmpty\n\t);\n}\n\nexport function useComputed<T>(\n\tcompute: () => T,\n\toptions?: SignalOptions<T>\n): ReadonlySignal<T> {\n\tconst $compute = useRef(compute);\n\t$compute.current = compute;\n\treturn useMemo(() => computed<T>(() => $compute.current(), options), Empty);\n}\n\nexport function useSignalEffect(\n\tcb: () => void | (() => void),\n\toptions?: EffectOptions\n) {\n\tconst callback = useRef(cb);\n\tcallback.current = cb;\n\n\tuseEffect(() => {\n\t\treturn effect(function (this: Effect) {\n\t\t\treturn callback.current();\n\t\t}, options);\n\t}, Empty);\n}\n\ndeclare global {\n\tinterface Window {\n\t\t__PREACT_SIGNALS_DEVTOOLS__: SignalsDevToolsAPI;\n\t}\n}\n"],"names":["Signal","signal","computed","effect","version","useRef","useMemo","useEffect","useLayoutEffect","useSyncExternalStore","major","reactVersion","split","map","Number","Empty","ReactElemType","Symbol","DEVTOOLS_ENABLED","window","__PREACT_SIGNALS_DEVTOOLS__","wrapJsx","jsx","type","props","i","v","value","call","apply","concat","slice","arguments","currentStore","symDispose","dispose","startComponentEffect","prevStore","nextStore","endEffect","_start","finishComponentEffect","bind","_ref2","finalCleanup","noop","emptyEffectStore","_usage","_sources","undefined","_callback","_dispose","subscribe","getSnapshot","f","_queueMicroTask","Promise","prototype","then","resolve","ensureFinalCleanup","cleanupTrailingStore","_currentStore","useIsomorphicLayoutEffect","_useSignalsImplementation","storeRef","current","_ref","effectInstance","onChangeNotifyReact","unsubscribe","this","_effectInstance$_debu","_debugCallback","onStoreChange","prevUsage","thisUsage","end","createEffectStore","store","Object","defineProperties","$$typeof","configurable","_ref3","data","get","ref","useSignals","usage","useSignal","options","useComputed","compute","$compute","useSignalEffect","cb","callback"],"mappings":"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"}
|
package/runtime/package.json
CHANGED
|
@@ -18,9 +18,12 @@
|
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@preact/signals-core": "workspace:^1.
|
|
21
|
+
"@preact/signals-core": "workspace:^1.12.2",
|
|
22
22
|
"use-sync-external-store": "^1.2.0"
|
|
23
23
|
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@preact/signals-react": "workspace:*"
|
|
26
|
+
},
|
|
24
27
|
"peerDependencies": {
|
|
25
28
|
"react": "^16.14.0 || 17.x || 18.x"
|
|
26
29
|
}
|
package/runtime/src/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
version as reactVersion,
|
|
16
16
|
} from "react";
|
|
17
17
|
import { useSyncExternalStore } from "use-sync-external-store/shim/index.js";
|
|
18
|
+
import type { SignalsDevToolsAPI } from "../../../debug/src/devtools";
|
|
18
19
|
|
|
19
20
|
const [major] = reactVersion.split(".").map(Number);
|
|
20
21
|
const Empty = [] as const;
|
|
@@ -24,6 +25,9 @@ const ReactElemType = Symbol.for(
|
|
|
24
25
|
major >= 19 ? "react.transitional.element" : "react.element"
|
|
25
26
|
);
|
|
26
27
|
|
|
28
|
+
const DEVTOOLS_ENABLED =
|
|
29
|
+
typeof window !== "undefined" && !!window.__PREACT_SIGNALS_DEVTOOLS__;
|
|
30
|
+
|
|
27
31
|
export function wrapJsx<T>(jsx: T): T {
|
|
28
32
|
if (typeof jsx !== "function") return jsx;
|
|
29
33
|
|
|
@@ -46,6 +50,7 @@ const symDispose: unique symbol =
|
|
|
46
50
|
|
|
47
51
|
interface Effect {
|
|
48
52
|
_sources: object | undefined;
|
|
53
|
+
_debugCallback?: () => void;
|
|
49
54
|
_start(): () => void;
|
|
50
55
|
_callback(): void;
|
|
51
56
|
_dispose(): void;
|
|
@@ -152,6 +157,9 @@ function createEffectStore(_usage: EffectStoreUsage): EffectStore {
|
|
|
152
157
|
});
|
|
153
158
|
effectInstance._callback = function () {
|
|
154
159
|
version = (version + 1) | 0;
|
|
160
|
+
if (DEVTOOLS_ENABLED) {
|
|
161
|
+
effectInstance._debugCallback?.call(effectInstance);
|
|
162
|
+
}
|
|
155
163
|
if (onChangeNotifyReact) onChangeNotifyReact();
|
|
156
164
|
};
|
|
157
165
|
|
|
@@ -417,3 +425,9 @@ export function useSignalEffect(
|
|
|
417
425
|
}, options);
|
|
418
426
|
}, Empty);
|
|
419
427
|
}
|
|
428
|
+
|
|
429
|
+
declare global {
|
|
430
|
+
interface Window {
|
|
431
|
+
__PREACT_SIGNALS_DEVTOOLS__: SignalsDevToolsAPI;
|
|
432
|
+
}
|
|
433
|
+
}
|
package/utils/package.json
CHANGED
|
@@ -18,10 +18,11 @@
|
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@preact/signals-core": "workspace:^1.
|
|
21
|
+
"@preact/signals-core": "workspace:^1.12.2"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
24
|
"@preact/signals-react": "workspace:*",
|
|
25
25
|
"react": "^16.14.0 || 17.x || 18.x || 19.x"
|
|
26
|
-
}
|
|
26
|
+
},
|
|
27
|
+
"version": null
|
|
27
28
|
}
|