@assistant-ui/tap 0.6.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -6
- package/dist/core/ResourceFiber.d.ts +5 -5
- package/dist/core/ResourceFiber.d.ts.map +1 -1
- package/dist/core/ResourceFiber.js +26 -18
- package/dist/core/ResourceFiber.js.map +1 -1
- package/dist/core/createTapRoot.d.ts +9 -0
- package/dist/core/createTapRoot.d.ts.map +1 -0
- package/dist/core/createTapRoot.js +27 -0
- package/dist/core/createTapRoot.js.map +1 -0
- package/dist/core/helpers/commit.d.ts +1 -1
- package/dist/core/helpers/commit.d.ts.map +1 -1
- package/dist/core/helpers/commit.js +6 -1
- package/dist/core/helpers/commit.js.map +1 -1
- package/dist/core/helpers/execution-context.d.ts +4 -5
- package/dist/core/helpers/execution-context.d.ts.map +1 -1
- package/dist/core/helpers/execution-context.js +1 -7
- package/dist/core/helpers/execution-context.js.map +1 -1
- package/dist/core/helpers/root.d.ts +3 -2
- package/dist/core/helpers/root.d.ts.map +1 -1
- package/dist/core/helpers/root.js +19 -15
- package/dist/core/helpers/root.js.map +1 -1
- package/dist/core/react-dispatcher.d.ts.map +1 -1
- package/dist/core/react-dispatcher.js +17 -16
- package/dist/core/react-dispatcher.js.map +1 -1
- package/dist/core/resource.d.ts +2 -4
- package/dist/core/resource.d.ts.map +1 -1
- package/dist/core/resource.js +5 -10
- package/dist/core/resource.js.map +1 -1
- package/dist/core/scheduler.d.ts +2 -2
- package/dist/core/scheduler.d.ts.map +1 -1
- package/dist/core/scheduler.js +2 -2
- package/dist/core/scheduler.js.map +1 -1
- package/dist/core/types.d.ts +27 -25
- package/dist/core/types.d.ts.map +1 -1
- package/dist/hooks/useResource.d.ts +2 -2
- package/dist/hooks/useResource.d.ts.map +1 -1
- package/dist/hooks/useResource.js +14 -20
- package/dist/hooks/useResource.js.map +1 -1
- package/dist/hooks/useResources.d.ts +1 -1
- package/dist/hooks/useResources.d.ts.map +1 -1
- package/dist/hooks/useResources.js +18 -27
- package/dist/hooks/useResources.js.map +1 -1
- package/dist/hooks/useTapHost.d.ts +21 -0
- package/dist/hooks/useTapHost.d.ts.map +1 -0
- package/dist/hooks/useTapHost.js +30 -0
- package/dist/hooks/useTapHost.js.map +1 -0
- package/dist/hooks/useTapRoot.d.ts +18 -0
- package/dist/hooks/useTapRoot.d.ts.map +1 -0
- package/dist/hooks/useTapRoot.js +77 -0
- package/dist/hooks/useTapRoot.js.map +1 -0
- package/dist/hooks/utils/depsShallowEqual.d.ts.map +1 -1
- package/dist/hooks/utils/depsShallowEqual.js +5 -2
- package/dist/hooks/utils/depsShallowEqual.js.map +1 -1
- package/dist/hooks/utils/useCell.d.ts +2 -2
- package/dist/hooks/utils/useCell.d.ts.map +1 -1
- package/dist/hooks/utils/useCell.js.map +1 -1
- package/dist/hooks/utils/useDevStrictMode.d.ts +5 -0
- package/dist/hooks/utils/useDevStrictMode.d.ts.map +1 -0
- package/dist/hooks/utils/useDevStrictMode.js +25 -0
- package/dist/hooks/utils/useDevStrictMode.js.map +1 -0
- package/dist/hooks/utils/useRenderMemo.d.ts +5 -0
- package/dist/hooks/utils/useRenderMemo.d.ts.map +1 -0
- package/dist/hooks/utils/useRenderMemo.js +25 -0
- package/dist/hooks/utils/useRenderMemo.js.map +1 -0
- package/dist/hooks/utils/useResourceFiberHostUtils.d.ts +10 -0
- package/dist/hooks/utils/useResourceFiberHostUtils.d.ts.map +1 -0
- package/dist/hooks/utils/useResourceFiberHostUtils.js +46 -0
- package/dist/hooks/utils/useResourceFiberHostUtils.js.map +1 -0
- package/dist/index.d.ts +7 -4
- package/dist/index.js +7 -4
- package/dist/{hooks → react-hooks}/index.d.ts +6 -6
- package/dist/{hooks → react-hooks}/index.js +5 -5
- package/dist/{hooks → react-hooks}/use.d.ts +1 -1
- package/dist/{hooks → react-hooks}/use.d.ts.map +1 -1
- package/dist/{hooks → react-hooks}/use.js +1 -1
- package/dist/react-hooks/use.js.map +1 -0
- package/dist/{hooks → react-hooks}/useCallback.d.ts +1 -1
- package/dist/react-hooks/useCallback.d.ts.map +1 -0
- package/dist/{hooks → react-hooks}/useCallback.js +1 -1
- package/dist/react-hooks/useCallback.js.map +1 -0
- package/dist/{hooks → react-hooks}/useEffect.d.ts +1 -1
- package/dist/react-hooks/useEffect.d.ts.map +1 -0
- package/dist/react-hooks/useEffect.js +35 -0
- package/dist/react-hooks/useEffect.js.map +1 -0
- package/dist/{hooks → react-hooks}/useEffectEvent.d.ts +1 -1
- package/dist/react-hooks/useEffectEvent.d.ts.map +1 -0
- package/dist/{hooks → react-hooks}/useEffectEvent.js +2 -2
- package/dist/react-hooks/useEffectEvent.js.map +1 -0
- package/dist/{hooks → react-hooks}/useMemo.d.ts +1 -1
- package/dist/react-hooks/useMemo.d.ts.map +1 -0
- package/dist/{hooks → react-hooks}/useMemo.js +3 -3
- package/dist/react-hooks/useMemo.js.map +1 -0
- package/dist/{hooks → react-hooks}/useMemoCache.d.ts +1 -1
- package/dist/react-hooks/useMemoCache.d.ts.map +1 -0
- package/dist/{hooks → react-hooks}/useMemoCache.js +1 -1
- package/dist/react-hooks/useMemoCache.js.map +1 -0
- package/dist/react-hooks/useReducer.d.ts +9 -0
- package/dist/react-hooks/useReducer.d.ts.map +1 -0
- package/dist/react-hooks/useReducer.js +120 -0
- package/dist/react-hooks/useReducer.js.map +1 -0
- package/dist/{hooks → react-hooks}/useRef.d.ts +1 -1
- package/dist/react-hooks/useRef.d.ts.map +1 -0
- package/dist/{hooks → react-hooks}/useRef.js +1 -1
- package/dist/react-hooks/useRef.js.map +1 -0
- package/dist/{hooks → react-hooks}/useState.d.ts +1 -1
- package/dist/react-hooks/useState.d.ts.map +1 -0
- package/dist/{hooks → react-hooks}/useState.js +3 -3
- package/dist/react-hooks/useState.js.map +1 -0
- package/dist/react-shim/index.d.ts +8 -10
- package/dist/react-shim/index.d.ts.map +1 -1
- package/dist/react-shim/index.js +19 -19
- package/dist/react-shim/index.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/basic/resourceHandle.test.ts +32 -22
- package/src/__tests__/basic/tapEffect.basic.test.ts +8 -8
- package/src/__tests__/basic/tapReducer.basic.test.ts +16 -14
- package/src/__tests__/basic/tapResources.basic.test.ts +19 -16
- package/src/__tests__/basic/tapState.basic.test.ts +11 -11
- package/src/__tests__/bench/hosts.bench.tsx +124 -0
- package/src/__tests__/bench/tree.bench.tsx +166 -0
- package/src/__tests__/errors/errors.effect-errors.test.ts +12 -13
- package/src/__tests__/errors/errors.render-errors.test.ts +65 -22
- package/src/__tests__/lifecycle/lifecycle.dependencies.test.ts +19 -19
- package/src/__tests__/lifecycle/lifecycle.mount-unmount.test.ts +14 -14
- package/src/__tests__/parity/describeParity.tsx +217 -0
- package/src/__tests__/parity/parity.adversarial.test.tsx +375 -0
- package/src/__tests__/parity/parity.basics.test.tsx +281 -0
- package/src/__tests__/parity/parity.divergences.test.tsx +208 -0
- package/src/__tests__/parity/parity.smoke.test.tsx +43 -0
- package/src/__tests__/react/concurrent-mode.test.tsx +10 -6
- package/src/__tests__/react/concurrent-pending-updates.test.tsx +351 -0
- package/src/__tests__/react/concurrent-render-phase.test.tsx +350 -0
- package/src/__tests__/react/react-shim.test.tsx +1 -1
- package/src/__tests__/react/useResource.test.tsx +41 -26
- package/src/__tests__/react/useTapHost.test.tsx +233 -0
- package/src/__tests__/react-dispatcher.test.ts +4 -4
- package/src/__tests__/rules/rules.hook-count.test.ts +21 -21
- package/src/__tests__/rules/rules.hook-order.test.ts +17 -17
- package/src/__tests__/strictmode/strictmode-parity.test.tsx +420 -0
- package/src/__tests__/strictmode/strictmode.test.ts +39 -209
- package/src/__tests__/test-utils.ts +33 -23
- package/src/core/ResourceFiber.ts +43 -35
- package/src/core/createTapRoot.ts +45 -0
- package/src/core/helpers/commit.ts +12 -2
- package/src/core/helpers/execution-context.ts +4 -13
- package/src/core/helpers/root.ts +24 -12
- package/src/core/react-dispatcher.ts +14 -13
- package/src/core/resource.ts +5 -20
- package/src/core/scheduler.ts +1 -1
- package/src/core/types.ts +27 -21
- package/src/hooks/useResource.ts +18 -27
- package/src/hooks/useResources.ts +18 -42
- package/src/hooks/useTapHost.ts +60 -0
- package/src/hooks/useTapRoot.ts +135 -0
- package/src/hooks/utils/depsShallowEqual.ts +12 -2
- package/src/hooks/utils/useCell.ts +2 -2
- package/src/hooks/utils/useDevStrictMode.ts +34 -0
- package/src/hooks/utils/useRenderMemo.ts +27 -0
- package/src/hooks/utils/useResourceFiberHostUtils.ts +61 -0
- package/src/index.ts +6 -3
- package/src/{hooks → react-hooks}/index.ts +4 -4
- package/src/react-hooks/useEffect.ts +58 -0
- package/src/{hooks → react-hooks}/useMemo.ts +1 -1
- package/src/react-hooks/useReducer.ts +254 -0
- package/src/{hooks → react-hooks}/useState.ts +2 -2
- package/src/react-shim/index.ts +24 -13
- package/dist/core/createResourceRoot.d.ts +0 -11
- package/dist/core/createResourceRoot.d.ts.map +0 -1
- package/dist/core/createResourceRoot.js +0 -31
- package/dist/core/createResourceRoot.js.map +0 -1
- package/dist/core/helpers/callResourceFn.d.ts +0 -1
- package/dist/core/helpers/callResourceFn.js +0 -19
- package/dist/core/helpers/callResourceFn.js.map +0 -1
- package/dist/hooks/use.js.map +0 -1
- package/dist/hooks/useCallback.d.ts.map +0 -1
- package/dist/hooks/useCallback.js.map +0 -1
- package/dist/hooks/useEffect.d.ts.map +0 -1
- package/dist/hooks/useEffect.js +0 -40
- package/dist/hooks/useEffect.js.map +0 -1
- package/dist/hooks/useEffectEvent.d.ts.map +0 -1
- package/dist/hooks/useEffectEvent.js.map +0 -1
- package/dist/hooks/useMemo.d.ts.map +0 -1
- package/dist/hooks/useMemo.js.map +0 -1
- package/dist/hooks/useMemoCache.d.ts.map +0 -1
- package/dist/hooks/useMemoCache.js.map +0 -1
- package/dist/hooks/useReducer.d.ts +0 -21
- package/dist/hooks/useReducer.d.ts.map +0 -1
- package/dist/hooks/useReducer.js +0 -81
- package/dist/hooks/useReducer.js.map +0 -1
- package/dist/hooks/useRef.d.ts.map +0 -1
- package/dist/hooks/useRef.js.map +0 -1
- package/dist/hooks/useResourceRoot.d.ts +0 -20
- package/dist/hooks/useResourceRoot.d.ts.map +0 -1
- package/dist/hooks/useResourceRoot.js +0 -77
- package/dist/hooks/useResourceRoot.js.map +0 -1
- package/dist/hooks/useState.d.ts.map +0 -1
- package/dist/hooks/useState.js.map +0 -1
- package/dist/react/hooks.d.ts +0 -25
- package/dist/react/hooks.d.ts.map +0 -1
- package/dist/react/hooks.js +0 -69
- package/dist/react/hooks.js.map +0 -1
- package/src/__tests__/strictmode/react-strictmode-behavior.test.tsx +0 -920
- package/src/__tests__/strictmode/react-strictmode-rerender-sources.test.tsx +0 -488
- package/src/__tests__/strictmode/tap-strictmode-rerender-sources.test.ts +0 -687
- package/src/core/createResourceRoot.ts +0 -53
- package/src/core/helpers/callResourceFn.ts +0 -21
- package/src/hooks/useEffect.ts +0 -72
- package/src/hooks/useReducer.ts +0 -160
- package/src/hooks/useResourceRoot.ts +0 -130
- package/src/react/hooks.ts +0 -112
- /package/src/{hooks → react-hooks}/use.ts +0 -0
- /package/src/{hooks → react-hooks}/useCallback.ts +0 -0
- /package/src/{hooks → react-hooks}/useEffectEvent.ts +0 -0
- /package/src/{hooks → react-hooks}/useMemoCache.ts +0 -0
- /package/src/{hooks → react-hooks}/useRef.ts +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/react-shim/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/react-shim/index.ts"],"mappings":";;;;cAwBa,QAAA,GAAY,YAAkB;AAAA,cAG9B,UAAA,GAAc,OAAA,OAAc,UAAA,OAAiB,IAAA;AAAA,cAK7C,MAAA,GAAU,YAAkB;AAAA,cAG5B,OAAA,GAAW,OAAA,OAAc,IAAS;AAAA,cAGlC,WAAA,GAAe,QAAA,OAAe,IAAS;AAAA,cAKvC,SAAA,GAAa,MAAA,OAAa,IAAU;AAAA,cAMpC,eAAA,GAAmB,MAAA,OAAa,IAAU;AAAA,cAM1C,cAAA,GAAkB,QAAa;AAAA,cAS/B,GAAA,GAAO,MAAW;AAAA,cAOlB,UAAA,GAAc,OAAY"}
|
package/dist/react-shim/index.js
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
import { peekResourceFiber } from "../core/helpers/execution-context.js";
|
|
2
|
-
import { useReducer as useReducer$2 } from "../hooks/useReducer.js";
|
|
3
|
-
import { useState as useState$2 } from "../hooks/useState.js";
|
|
4
|
-
import { useRef as useRef$2 } from "../hooks/useRef.js";
|
|
5
|
-
import { useMemo as useMemo$2 } from "../hooks/useMemo.js";
|
|
6
|
-
import { useCallback as useCallback$
|
|
7
|
-
import { useEffect as useEffect$
|
|
8
|
-
import { useEffectEvent as useEffectEvent$
|
|
2
|
+
import { useReducer as useReducer$2 } from "../react-hooks/useReducer.js";
|
|
3
|
+
import { useState as useState$2 } from "../react-hooks/useState.js";
|
|
4
|
+
import { useRef as useRef$2 } from "../react-hooks/useRef.js";
|
|
5
|
+
import { useMemo as useMemo$2 } from "../react-hooks/useMemo.js";
|
|
6
|
+
import { useCallback as useCallback$2 } from "../react-hooks/useCallback.js";
|
|
7
|
+
import { useEffect as useEffect$2 } from "../react-hooks/useEffect.js";
|
|
8
|
+
import { useEffectEvent as useEffectEvent$2 } from "../react-hooks/useEffectEvent.js";
|
|
9
9
|
import { isResourceContext, useResourceContext } from "../core/context.js";
|
|
10
|
-
import
|
|
11
|
-
import react_default from "react";
|
|
10
|
+
import React, { default as react_default } from "react";
|
|
12
11
|
export * from "react";
|
|
13
12
|
//#region src/react-shim/index.ts
|
|
14
13
|
const inTap = () => peekResourceFiber() !== null;
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const
|
|
14
|
+
const ReactRuntime = React;
|
|
15
|
+
const useState = (initialState) => inTap() ? useState$2(initialState) : ReactRuntime.useState(initialState);
|
|
16
|
+
const useReducer = (reducer, initialArg, init) => inTap() ? useReducer$2(reducer, initialArg, init) : ReactRuntime.useReducer(reducer, initialArg, init);
|
|
17
|
+
const useRef = (initialValue) => inTap() ? useRef$2(initialValue) : ReactRuntime.useRef(initialValue);
|
|
18
|
+
const useMemo = (factory, deps) => inTap() ? useMemo$2(factory, deps) : ReactRuntime.useMemo(factory, deps);
|
|
19
|
+
const useCallback = (callback, deps) => inTap() ? useCallback$2(callback, deps) : ReactRuntime.useCallback(callback, deps);
|
|
20
|
+
const useEffect = (effect, deps) => inTap() ? useEffect$2(effect, deps) : ReactRuntime.useEffect(effect, deps);
|
|
21
|
+
const useLayoutEffect = (effect, deps) => inTap() ? useEffect$2(effect, deps) : ReactRuntime.useLayoutEffect(effect, deps);
|
|
22
|
+
const useEffectEvent = (callback) => inTap() ? useEffectEvent$2(callback) : ReactRuntime.useEffectEvent(callback);
|
|
23
|
+
const use = (usable) => isResourceContext(usable) ? useResourceContext(usable) : ReactRuntime.use(usable);
|
|
24
|
+
const useContext = (context) => isResourceContext(context) ? useResourceContext(context) : ReactRuntime.useContext(context);
|
|
25
25
|
//#endregion
|
|
26
26
|
export { react_default as default, use, useCallback, useContext, useEffect, useEffectEvent, useLayoutEffect, useMemo, useReducer, useRef, useState };
|
|
27
27
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["hooks.useState","hooks.useReducer","hooks.useRef","hooks.useMemo","hooks.useCallback","hooks.useEffect","hooks.useEffectEvent"],"sources":["../../src/react-shim/index.ts"],"sourcesContent":["/* oxlint-disable react/rules-of-hooks -- this module deliberately routes hook calls between tap and React at runtime */\n/* oxlint-disable react/exhaustive-deps -- dependency arrays are forwarded verbatim from the caller */\n// Runtime drop-in for \"react\": forward everything from react, then override the\n// hooks that have a tap equivalent so they route to tap inside a resource render\n// and to React otherwise. Alias `react` to this module (build `output.paths` /\n// vitest resolver) in code that can run inside a tap resource.\n//\n// This subpath ships no type declarations: the build reverts the aliased\n// specifier back to `\"react\"` in emitted `.d.ts`, so consumer types resolve to\n// React's own. The source-level TS2498 from the `export *` below is suppressed.\nimport
|
|
1
|
+
{"version":3,"file":"index.js","names":["hooks.useState","hooks.useReducer","hooks.useRef","hooks.useMemo","hooks.useCallback","hooks.useEffect","hooks.useEffectEvent"],"sources":["../../src/react-shim/index.ts"],"sourcesContent":["/* oxlint-disable react/rules-of-hooks -- this module deliberately routes hook calls between tap and React at runtime */\n/* oxlint-disable react/exhaustive-deps -- dependency arrays are forwarded verbatim from the caller */\n// Runtime drop-in for \"react\": forward everything from react, then override the\n// hooks that have a tap equivalent so they route to tap inside a resource render\n// and to React otherwise. Alias `react` to this module (build `output.paths` /\n// vitest resolver) in code that can run inside a tap resource.\n//\n// This subpath ships no type declarations: the build reverts the aliased\n// specifier back to `\"react\"` in emitted `.d.ts`, so consumer types resolve to\n// React's own. The source-level TS2498 from the `export *` below is suppressed.\nimport React from \"react\";\nimport { peekResourceFiber } from \"../core/helpers/execution-context\";\nimport * as hooks from \"../react-hooks\";\nimport { useResourceContext, isResourceContext } from \"../core/context\";\n\n// @ts-expect-error -- @types/react uses `export =`; this is valid at runtime.\nexport * from \"react\";\nexport { default } from \"react\";\n\nconst inTap = () => peekResourceFiber() !== null;\nconst ReactRuntime = React as any;\n\n// --- hooks with a tap equivalent: override the star-exported react hooks ---\n\nexport const useState = (initialState?: any) =>\n inTap() ? hooks.useState(initialState) : ReactRuntime.useState(initialState);\n\nexport const useReducer = (reducer: any, initialArg: any, init?: any) =>\n inTap()\n ? hooks.useReducer(reducer, initialArg, init)\n : ReactRuntime.useReducer(reducer, initialArg, init);\n\nexport const useRef = (initialValue?: any) =>\n inTap() ? hooks.useRef(initialValue) : ReactRuntime.useRef(initialValue);\n\nexport const useMemo = (factory: any, deps: any) =>\n inTap() ? hooks.useMemo(factory, deps) : ReactRuntime.useMemo(factory, deps);\n\nexport const useCallback = (callback: any, deps: any) =>\n inTap()\n ? hooks.useCallback(callback, deps)\n : ReactRuntime.useCallback(callback, deps);\n\nexport const useEffect = (effect: any, deps?: any) =>\n inTap()\n ? hooks.useEffect(effect, deps)\n : ReactRuntime.useEffect(effect, deps);\n\n// tap has a single effect primitive; layout effects collapse onto it\nexport const useLayoutEffect = (effect: any, deps?: any) =>\n inTap()\n ? hooks.useEffect(effect, deps)\n : ReactRuntime.useLayoutEffect(effect, deps);\n\n// The non-tap fallback requires a React version that provides useEffectEvent.\nexport const useEffectEvent = (callback: any) =>\n inTap()\n ? hooks.useEffectEvent(callback)\n : ReactRuntime.useEffectEvent(callback);\n\n// `use(usable)` reads tap resource context when handed a tap context (routed by\n// its brand, not by ambient render state), and falls back to React's `use`\n// (promises / React context) for everything else. The non-tap fallback requires\n// React 19.\nexport const use = (usable: any) =>\n isResourceContext(usable)\n ? useResourceContext(usable)\n : ReactRuntime.use(usable);\n\n// `useContext(context)` reads tap resource context when handed a tap context\n// (routed by its brand), and falls back to React's `useContext` otherwise.\nexport const useContext = (context: any) =>\n isResourceContext(context)\n ? useResourceContext(context)\n : ReactRuntime.useContext(context);\n"],"mappings":";;;;;;;;;;;;AAmBA,MAAM,cAAc,kBAAkB,MAAM;AAC5C,MAAM,eAAe;AAIrB,MAAa,YAAY,iBACvB,MAAM,IAAIA,WAAe,YAAY,IAAI,aAAa,SAAS,YAAY;AAE7E,MAAa,cAAc,SAAc,YAAiB,SACxD,MAAM,IACFC,aAAiB,SAAS,YAAY,IAAI,IAC1C,aAAa,WAAW,SAAS,YAAY,IAAI;AAEvD,MAAa,UAAU,iBACrB,MAAM,IAAIC,SAAa,YAAY,IAAI,aAAa,OAAO,YAAY;AAEzE,MAAa,WAAW,SAAc,SACpC,MAAM,IAAIC,UAAc,SAAS,IAAI,IAAI,aAAa,QAAQ,SAAS,IAAI;AAE7E,MAAa,eAAe,UAAe,SACzC,MAAM,IACFC,cAAkB,UAAU,IAAI,IAChC,aAAa,YAAY,UAAU,IAAI;AAE7C,MAAa,aAAa,QAAa,SACrC,MAAM,IACFC,YAAgB,QAAQ,IAAI,IAC5B,aAAa,UAAU,QAAQ,IAAI;AAGzC,MAAa,mBAAmB,QAAa,SAC3C,MAAM,IACFA,YAAgB,QAAQ,IAAI,IAC5B,aAAa,gBAAgB,QAAQ,IAAI;AAG/C,MAAa,kBAAkB,aAC7B,MAAM,IACFC,iBAAqB,QAAQ,IAC7B,aAAa,eAAe,QAAQ;AAM1C,MAAa,OAAO,WAClB,kBAAkB,MAAM,IACpB,mBAAmB,MAAM,IACzB,aAAa,IAAI,MAAM;AAI7B,MAAa,cAAc,YACzB,kBAAkB,OAAO,IACrB,mBAAmB,OAAO,IAC1B,aAAa,WAAW,OAAO"}
|
package/package.json
CHANGED
|
@@ -1,50 +1,60 @@
|
|
|
1
1
|
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { createTapRoot } from "../../core/createTapRoot";
|
|
3
|
+
import { flushTapSync } from "../../core/scheduler";
|
|
4
|
+
import { useState } from "../../react-hooks/useState";
|
|
4
5
|
|
|
5
6
|
describe("ResourceHandle - Basic Usage", () => {
|
|
6
7
|
it("should create a resource handle with const API", () => {
|
|
7
|
-
const
|
|
8
|
+
const useTestResource = (props: number) => {
|
|
8
9
|
return {
|
|
9
10
|
value: props * 2,
|
|
10
11
|
propsUsed: props,
|
|
11
12
|
};
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const sub = createTapRoot(function Root() {
|
|
16
|
+
return useTestResource(5);
|
|
12
17
|
});
|
|
13
|
-
const root = createResourceRoot();
|
|
14
|
-
const sub = root.render(TestResource(5));
|
|
15
18
|
|
|
16
|
-
// The
|
|
19
|
+
// The root provides getValue, subscribe, and unmount
|
|
17
20
|
expect(typeof sub.getValue).toBe("function");
|
|
18
21
|
expect(typeof sub.subscribe).toBe("function");
|
|
19
|
-
expect(typeof
|
|
22
|
+
expect(typeof sub.unmount).toBe("function");
|
|
20
23
|
|
|
21
24
|
// Initial state
|
|
22
25
|
expect(sub.getValue().value).toBe(10);
|
|
23
26
|
expect(sub.getValue().propsUsed).toBe(5);
|
|
24
27
|
});
|
|
25
28
|
|
|
26
|
-
it("should
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
it("should re-render and notify on internal state change", () => {
|
|
30
|
+
const useTestResource = () => {
|
|
31
|
+
const [count, setCount] = useState(0);
|
|
32
|
+
return { count, increment: () => setCount((c) => c + 1) };
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const sub = createTapRoot(function Root() {
|
|
36
|
+
return useTestResource();
|
|
31
37
|
});
|
|
32
|
-
const root = createResourceRoot();
|
|
33
|
-
const sub = root.render(TestResource({ multiplier: 2 }));
|
|
34
38
|
|
|
35
|
-
|
|
36
|
-
expect(sub.getValue().result).toBe(20);
|
|
39
|
+
expect(sub.getValue().count).toBe(0);
|
|
37
40
|
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
const listener = vi.fn();
|
|
42
|
+
sub.subscribe(listener);
|
|
43
|
+
|
|
44
|
+
flushTapSync(() => sub.getValue().increment());
|
|
45
|
+
|
|
46
|
+
expect(sub.getValue().count).toBe(1);
|
|
47
|
+
expect(listener).toHaveBeenCalled();
|
|
40
48
|
});
|
|
41
49
|
|
|
42
50
|
it("should support subscribing and unsubscribing", () => {
|
|
43
|
-
const
|
|
44
|
-
return { timestamp:
|
|
51
|
+
const useTestResource = () => {
|
|
52
|
+
return { timestamp: 0 };
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const sub = createTapRoot(function Root() {
|
|
56
|
+
return useTestResource();
|
|
45
57
|
});
|
|
46
|
-
const root = createResourceRoot();
|
|
47
|
-
const sub = root.render(TestResource());
|
|
48
58
|
|
|
49
59
|
const subscriber1 = vi.fn();
|
|
50
60
|
const subscriber2 = vi.fn();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
2
|
-
import { useEffect } from "../../hooks/useEffect";
|
|
3
|
-
import { useState } from "../../hooks/useState";
|
|
2
|
+
import { useEffect } from "../../react-hooks/useEffect";
|
|
3
|
+
import { useState } from "../../react-hooks/useState";
|
|
4
4
|
import {
|
|
5
5
|
createTestResource,
|
|
6
6
|
renderTest,
|
|
@@ -31,7 +31,7 @@ describe("useEffect - Basic Functionality", () => {
|
|
|
31
31
|
const manager = new TestResourceManager(testFiber);
|
|
32
32
|
|
|
33
33
|
// Mount and render
|
|
34
|
-
manager.renderAndMount(
|
|
34
|
+
manager.renderAndMount();
|
|
35
35
|
|
|
36
36
|
// Effect should run after commit
|
|
37
37
|
expect(executionOrder).toEqual(["render", "effect"]);
|
|
@@ -49,7 +49,7 @@ describe("useEffect - Basic Functionality", () => {
|
|
|
49
49
|
});
|
|
50
50
|
|
|
51
51
|
const manager = new TestResourceManager(testFiber);
|
|
52
|
-
manager.renderAndMount(
|
|
52
|
+
manager.renderAndMount();
|
|
53
53
|
|
|
54
54
|
// Effect should be called, but not cleanup
|
|
55
55
|
expect(effect).toHaveBeenCalledTimes(1);
|
|
@@ -80,7 +80,7 @@ describe("useEffect - Basic Functionality", () => {
|
|
|
80
80
|
});
|
|
81
81
|
|
|
82
82
|
const manager = new TestResourceManager(testFiber);
|
|
83
|
-
manager.renderAndMount(
|
|
83
|
+
manager.renderAndMount();
|
|
84
84
|
manager.cleanup();
|
|
85
85
|
|
|
86
86
|
// Cleanup should run in reverse order (LIFO)
|
|
@@ -111,7 +111,7 @@ describe("useEffect - Basic Functionality", () => {
|
|
|
111
111
|
return null;
|
|
112
112
|
});
|
|
113
113
|
|
|
114
|
-
renderTest(testFiber
|
|
114
|
+
renderTest(testFiber);
|
|
115
115
|
expect(executionOrder).toEqual(["effect1", "effect2", "effect3"]);
|
|
116
116
|
});
|
|
117
117
|
|
|
@@ -173,7 +173,7 @@ describe("useEffect - Basic Functionality", () => {
|
|
|
173
173
|
});
|
|
174
174
|
|
|
175
175
|
// Initial render
|
|
176
|
-
renderTest(testFiber
|
|
176
|
+
renderTest(testFiber);
|
|
177
177
|
expect(effect).toHaveBeenCalledTimes(1);
|
|
178
178
|
|
|
179
179
|
// Trigger re-render
|
|
@@ -234,7 +234,7 @@ describe("useEffect - Basic Functionality", () => {
|
|
|
234
234
|
const manager = new TestResourceManager(testFiber);
|
|
235
235
|
|
|
236
236
|
// Initial render
|
|
237
|
-
manager.renderAndMount(
|
|
237
|
+
manager.renderAndMount();
|
|
238
238
|
// Without mount tracking, the effect runs immediately during commit
|
|
239
239
|
// This triggers setState which causes a synchronous re-render
|
|
240
240
|
expect(events).toEqual([
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, afterEach } from "vitest";
|
|
2
|
-
import { useReducer } from "../../hooks/useReducer";
|
|
3
|
-
import { useEffect } from "../../hooks/useEffect";
|
|
2
|
+
import { useReducer } from "../../react-hooks/useReducer";
|
|
3
|
+
import { useEffect } from "../../react-hooks/useEffect";
|
|
4
4
|
import {
|
|
5
5
|
createTestResource,
|
|
6
6
|
renderTest,
|
|
@@ -23,7 +23,7 @@ describe("useReducer - Basic Functionality", () => {
|
|
|
23
23
|
return count;
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
-
const result = renderTest(testFiber
|
|
26
|
+
const result = renderTest(testFiber);
|
|
27
27
|
expect(result).toBe(0);
|
|
28
28
|
});
|
|
29
29
|
|
|
@@ -39,12 +39,12 @@ describe("useReducer - Basic Functionality", () => {
|
|
|
39
39
|
return count;
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
-
const result = renderTest(testFiber
|
|
42
|
+
const result = renderTest(testFiber);
|
|
43
43
|
expect(result).toBe(20);
|
|
44
44
|
expect(initCalled).toBe(1);
|
|
45
45
|
|
|
46
46
|
// Re-render should not call init again
|
|
47
|
-
renderTest(testFiber
|
|
47
|
+
renderTest(testFiber);
|
|
48
48
|
expect(initCalled).toBe(1);
|
|
49
49
|
});
|
|
50
50
|
});
|
|
@@ -73,7 +73,7 @@ describe("useReducer - Basic Functionality", () => {
|
|
|
73
73
|
return count;
|
|
74
74
|
});
|
|
75
75
|
|
|
76
|
-
renderTest(testFiber
|
|
76
|
+
renderTest(testFiber);
|
|
77
77
|
expect(getCommittedOutput(testFiber)).toBe(0);
|
|
78
78
|
|
|
79
79
|
dispatchFn!({ type: "increment" });
|
|
@@ -91,7 +91,9 @@ describe("useReducer - Basic Functionality", () => {
|
|
|
91
91
|
});
|
|
92
92
|
|
|
93
93
|
describe("Same-state bailout", () => {
|
|
94
|
-
it("
|
|
94
|
+
it("re-renders once when the reducer returns the same state, like React", async () => {
|
|
95
|
+
// React computes user reducers during render (no eager dispatch-time
|
|
96
|
+
// bailout), so a same-state dispatch still renders once.
|
|
95
97
|
let renderCount = 0;
|
|
96
98
|
const reducer = (state: number, action: number) =>
|
|
97
99
|
action === 0 ? state : state + action;
|
|
@@ -109,13 +111,13 @@ describe("useReducer - Basic Functionality", () => {
|
|
|
109
111
|
return count;
|
|
110
112
|
});
|
|
111
113
|
|
|
112
|
-
renderTest(testFiber
|
|
114
|
+
renderTest(testFiber);
|
|
113
115
|
expect(renderCount).toBe(1);
|
|
114
116
|
|
|
115
117
|
// Dispatch action that returns same state
|
|
116
118
|
dispatchFn!(0);
|
|
117
119
|
await waitForNextTick();
|
|
118
|
-
expect(renderCount).toBe(
|
|
120
|
+
expect(renderCount).toBe(2);
|
|
119
121
|
});
|
|
120
122
|
});
|
|
121
123
|
|
|
@@ -136,7 +138,7 @@ describe("useReducer - Basic Functionality", () => {
|
|
|
136
138
|
return count;
|
|
137
139
|
});
|
|
138
140
|
|
|
139
|
-
renderTest(testFiber
|
|
141
|
+
renderTest(testFiber);
|
|
140
142
|
expect(getCommittedOutput(testFiber)).toBe(0);
|
|
141
143
|
|
|
142
144
|
// Dispatch with multiplier=1
|
|
@@ -146,7 +148,7 @@ describe("useReducer - Basic Functionality", () => {
|
|
|
146
148
|
|
|
147
149
|
// Change multiplier and dispatch
|
|
148
150
|
multiplier = 10;
|
|
149
|
-
renderTest(testFiber
|
|
151
|
+
renderTest(testFiber); // re-render to update reducer
|
|
150
152
|
dispatchFn!(5);
|
|
151
153
|
await waitForNextTick();
|
|
152
154
|
expect(getCommittedOutput(testFiber)).toBe(55); // 5 + 5*10
|
|
@@ -168,7 +170,7 @@ describe("useReducer - Basic Functionality", () => {
|
|
|
168
170
|
return count;
|
|
169
171
|
});
|
|
170
172
|
|
|
171
|
-
renderTest(testFiber
|
|
173
|
+
renderTest(testFiber);
|
|
172
174
|
expect(getCommittedOutput(testFiber)).toBe(0);
|
|
173
175
|
|
|
174
176
|
// Multiple dispatches
|
|
@@ -191,8 +193,8 @@ describe("useReducer - Basic Functionality", () => {
|
|
|
191
193
|
return count;
|
|
192
194
|
});
|
|
193
195
|
|
|
194
|
-
renderTest(testFiber
|
|
195
|
-
renderTest(testFiber
|
|
196
|
+
renderTest(testFiber);
|
|
197
|
+
renderTest(testFiber);
|
|
196
198
|
|
|
197
199
|
expect(dispatches[0]).toBe(dispatches[1]);
|
|
198
200
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, afterEach } from "vitest";
|
|
2
2
|
import { useResources } from "../../hooks/useResources";
|
|
3
|
-
import { useState } from "../../hooks/useState";
|
|
3
|
+
import { useState } from "../../react-hooks/useState";
|
|
4
4
|
import { resource } from "../../core/resource";
|
|
5
5
|
import { withKey } from "../../core/withKey";
|
|
6
6
|
import {
|
|
@@ -13,25 +13,24 @@ import {
|
|
|
13
13
|
const SimpleCounter = resource(createCounterResource());
|
|
14
14
|
|
|
15
15
|
// Stateful counter that tracks its own count
|
|
16
|
-
const
|
|
17
|
-
initial: number;
|
|
18
|
-
}) {
|
|
16
|
+
const useStatefulCounter = (props: { initial: number }) => {
|
|
19
17
|
const [count] = useState(props.initial);
|
|
20
18
|
return { count };
|
|
21
|
-
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const StatefulCounter = resource(useStatefulCounter);
|
|
22
22
|
|
|
23
23
|
// Display component for testing type changes
|
|
24
|
-
const
|
|
24
|
+
const useDisplay = (props: { text: string }) => {
|
|
25
25
|
return { type: "display", text: props.text };
|
|
26
|
-
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const Display = resource(useDisplay);
|
|
27
29
|
|
|
28
30
|
// Counter with render tracking for testing instance preservation
|
|
29
31
|
const renderCounts = new Map<string, number>();
|
|
30
32
|
const instances = new Map<string, object>();
|
|
31
|
-
const
|
|
32
|
-
value: number;
|
|
33
|
-
id: string;
|
|
34
|
-
}) {
|
|
33
|
+
const useTrackingCounter = (props: { value: number; id: string }) => {
|
|
35
34
|
const currentCount = (renderCounts.get(props.id) || 0) + 1;
|
|
36
35
|
renderCounts.set(props.id, currentCount);
|
|
37
36
|
|
|
@@ -45,7 +44,9 @@ const TrackingCounter = resource(function TrackingCounter(props: {
|
|
|
45
44
|
renderCount: currentCount,
|
|
46
45
|
instance: instances.get(props.id),
|
|
47
46
|
};
|
|
48
|
-
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const TrackingCounter = resource(useTrackingCounter);
|
|
49
50
|
|
|
50
51
|
describe("useResources - Basic Functionality", () => {
|
|
51
52
|
afterEach(() => {
|
|
@@ -67,15 +68,17 @@ describe("useResources - Basic Functionality", () => {
|
|
|
67
68
|
return results;
|
|
68
69
|
});
|
|
69
70
|
|
|
70
|
-
const result = renderTest(testFiber
|
|
71
|
+
const result = renderTest(testFiber);
|
|
71
72
|
expect(result).toEqual([{ count: 10 }, { count: 20 }, { count: 30 }]);
|
|
72
73
|
});
|
|
73
74
|
|
|
74
75
|
it("should work with resource constructor syntax", () => {
|
|
75
|
-
const
|
|
76
|
+
const useCounter = (props: { value: number }) => {
|
|
76
77
|
const [count] = useState(props.value);
|
|
77
78
|
return { count, double: count * 2 };
|
|
78
|
-
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const Counter = resource(useCounter);
|
|
79
82
|
|
|
80
83
|
const items = [
|
|
81
84
|
{ key: "first", value: 5 },
|
|
@@ -95,7 +98,7 @@ describe("useResources - Basic Functionality", () => {
|
|
|
95
98
|
return results;
|
|
96
99
|
});
|
|
97
100
|
|
|
98
|
-
const result = renderTest(testFiber
|
|
101
|
+
const result = renderTest(testFiber);
|
|
99
102
|
expect(result).toEqual([
|
|
100
103
|
{ count: 5, double: 10 },
|
|
101
104
|
{ count: 10, double: 20 },
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, afterEach } from "vitest";
|
|
2
|
-
import { useState } from "../../hooks/useState";
|
|
3
|
-
import { useEffect } from "../../hooks/useEffect";
|
|
2
|
+
import { useState } from "../../react-hooks/useState";
|
|
3
|
+
import { useEffect } from "../../react-hooks/useEffect";
|
|
4
4
|
import {
|
|
5
5
|
createTestResource,
|
|
6
6
|
renderTest,
|
|
@@ -21,7 +21,7 @@ describe("useState - Basic Functionality", () => {
|
|
|
21
21
|
return count;
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
const result = renderTest(testFiber
|
|
24
|
+
const result = renderTest(testFiber);
|
|
25
25
|
expect(result).toBe(42);
|
|
26
26
|
});
|
|
27
27
|
|
|
@@ -37,12 +37,12 @@ describe("useState - Basic Functionality", () => {
|
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
// First render
|
|
40
|
-
const result = renderTest(testFiber
|
|
40
|
+
const result = renderTest(testFiber);
|
|
41
41
|
expect(result).toBe(100);
|
|
42
42
|
expect(initCalled).toBe(1);
|
|
43
43
|
|
|
44
44
|
// Re-render should not call initializer again
|
|
45
|
-
renderTest(testFiber
|
|
45
|
+
renderTest(testFiber);
|
|
46
46
|
expect(initCalled).toBe(1);
|
|
47
47
|
});
|
|
48
48
|
|
|
@@ -52,7 +52,7 @@ describe("useState - Basic Functionality", () => {
|
|
|
52
52
|
return value;
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
-
const result = renderTest(testFiber
|
|
55
|
+
const result = renderTest(testFiber);
|
|
56
56
|
expect(result).toBeUndefined();
|
|
57
57
|
});
|
|
58
58
|
});
|
|
@@ -75,7 +75,7 @@ describe("useState - Basic Functionality", () => {
|
|
|
75
75
|
});
|
|
76
76
|
|
|
77
77
|
// Initial render
|
|
78
|
-
const result1 = renderTest(testFiber
|
|
78
|
+
const result1 = renderTest(testFiber);
|
|
79
79
|
expect(result1).toEqual({ count: 0, renderCount: 1 });
|
|
80
80
|
|
|
81
81
|
// Update state
|
|
@@ -107,7 +107,7 @@ describe("useState - Basic Functionality", () => {
|
|
|
107
107
|
});
|
|
108
108
|
|
|
109
109
|
// Initial render
|
|
110
|
-
renderTest(testFiber
|
|
110
|
+
renderTest(testFiber);
|
|
111
111
|
expect(renderCount).toBe(1);
|
|
112
112
|
|
|
113
113
|
// Set same value
|
|
@@ -135,7 +135,7 @@ describe("useState - Basic Functionality", () => {
|
|
|
135
135
|
});
|
|
136
136
|
|
|
137
137
|
// Initial render
|
|
138
|
-
renderTest(testFiber
|
|
138
|
+
renderTest(testFiber);
|
|
139
139
|
expect(getCommittedOutput(testFiber)).toBe(10);
|
|
140
140
|
|
|
141
141
|
// Functional update
|
|
@@ -167,7 +167,7 @@ describe("useState - Basic Functionality", () => {
|
|
|
167
167
|
};
|
|
168
168
|
});
|
|
169
169
|
|
|
170
|
-
const result = renderTest(testFiber
|
|
170
|
+
const result = renderTest(testFiber);
|
|
171
171
|
expect(result).toMatchObject({
|
|
172
172
|
count1: 1,
|
|
173
173
|
count2: 2,
|
|
@@ -191,7 +191,7 @@ describe("useState - Basic Functionality", () => {
|
|
|
191
191
|
});
|
|
192
192
|
|
|
193
193
|
// Initial render
|
|
194
|
-
renderTest(testFiber
|
|
194
|
+
renderTest(testFiber);
|
|
195
195
|
expect(getCommittedOutput(testFiber)).toEqual({ a: "a", b: "b", c: "c" });
|
|
196
196
|
|
|
197
197
|
// Update only B
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-type benchmark: mount+unmount and update cost of a body with N
|
|
3
|
+
* hooks under each host. Runs against the BUILT package (dist), which the
|
|
4
|
+
* vitest config externalizes so tap and React both run as plain Node
|
|
5
|
+
* modules, outside vitest's module evaluator. Build first, then:
|
|
6
|
+
*
|
|
7
|
+
* pnpm build
|
|
8
|
+
* pnpm exec vitest bench --run --project prod src/__tests__/bench/hosts.bench.tsx
|
|
9
|
+
*
|
|
10
|
+
* Not part of the test suite; vitest only picks this up in bench mode.
|
|
11
|
+
*/
|
|
12
|
+
/* oxlint-disable react/rules-of-hooks -- fixed-count hook loop, benchmark only */
|
|
13
|
+
import { bench, describe } from "vitest";
|
|
14
|
+
import { createElement, useState } from "react";
|
|
15
|
+
import { createRoot } from "react-dom/client";
|
|
16
|
+
import { flushSync } from "react-dom";
|
|
17
|
+
import {
|
|
18
|
+
createTapRoot,
|
|
19
|
+
flushTapSync,
|
|
20
|
+
resource,
|
|
21
|
+
useResource,
|
|
22
|
+
useTapRoot,
|
|
23
|
+
} from "@assistant-ui/tap";
|
|
24
|
+
|
|
25
|
+
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = false;
|
|
26
|
+
|
|
27
|
+
const N = 10_000;
|
|
28
|
+
|
|
29
|
+
type Api = { sum: number; bump: () => void };
|
|
30
|
+
|
|
31
|
+
const useManyHooks = (): Api => {
|
|
32
|
+
let sum = 0;
|
|
33
|
+
let lastSet!: (updater: (v: number) => number) => void;
|
|
34
|
+
for (let i = 0; i < N; i++) {
|
|
35
|
+
const [v, setV] = useState(0);
|
|
36
|
+
sum += v;
|
|
37
|
+
lastSet = setV;
|
|
38
|
+
}
|
|
39
|
+
return { sum, bump: () => lastSet((v) => v + 1) };
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
type Host = {
|
|
43
|
+
api: () => Api;
|
|
44
|
+
flush: (fn: () => void) => void;
|
|
45
|
+
unmount: () => void;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const mountReactHost = (useProbe: () => () => Api): Host => {
|
|
49
|
+
let api!: () => Api;
|
|
50
|
+
function Probe() {
|
|
51
|
+
api = useProbe();
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const root = createRoot(document.createElement("div"));
|
|
55
|
+
flushSync(() => root.render(createElement(Probe)));
|
|
56
|
+
return {
|
|
57
|
+
api: () => api(),
|
|
58
|
+
flush: (fn) => flushSync(fn),
|
|
59
|
+
unmount: () => flushSync(() => root.unmount()),
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const ManyHooks = resource(useManyHooks);
|
|
64
|
+
|
|
65
|
+
const HOSTS: Record<string, () => Host> = {
|
|
66
|
+
react: () =>
|
|
67
|
+
mountReactHost(() => {
|
|
68
|
+
const value = useManyHooks();
|
|
69
|
+
return () => value;
|
|
70
|
+
}),
|
|
71
|
+
bridge: () =>
|
|
72
|
+
mountReactHost(() => {
|
|
73
|
+
const value = useResource(ManyHooks());
|
|
74
|
+
return () => value;
|
|
75
|
+
}),
|
|
76
|
+
tapRoot: () => {
|
|
77
|
+
const host = mountReactHost(() => {
|
|
78
|
+
const root = useTapRoot(function Root() {
|
|
79
|
+
return useManyHooks();
|
|
80
|
+
});
|
|
81
|
+
return () => root.getValue();
|
|
82
|
+
});
|
|
83
|
+
return { ...host, flush: (fn) => flushTapSync(fn) };
|
|
84
|
+
},
|
|
85
|
+
createTapRoot: () => {
|
|
86
|
+
const root = createTapRoot(function Root() {
|
|
87
|
+
return useManyHooks();
|
|
88
|
+
});
|
|
89
|
+
return {
|
|
90
|
+
api: () => root.getValue(),
|
|
91
|
+
flush: (fn) => flushTapSync(fn),
|
|
92
|
+
unmount: () => root.unmount(),
|
|
93
|
+
};
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
describe(`mount+unmount, ${N} useState hooks`, () => {
|
|
98
|
+
for (const [name, make] of Object.entries(HOSTS)) {
|
|
99
|
+
bench(name, () => {
|
|
100
|
+
const host = make();
|
|
101
|
+
host.unmount();
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe(`update (one dispatch, full re-render), ${N} useState hooks`, () => {
|
|
107
|
+
for (const [name, make] of Object.entries(HOSTS)) {
|
|
108
|
+
let host: Host;
|
|
109
|
+
bench(
|
|
110
|
+
name,
|
|
111
|
+
() => {
|
|
112
|
+
host.flush(() => host.api().bump());
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
setup: () => {
|
|
116
|
+
host = make();
|
|
117
|
+
},
|
|
118
|
+
teardown: () => {
|
|
119
|
+
host.unmount();
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
});
|