From 9f786168ad90f1a0aa8a66ba38b9e724ef026e6c Mon Sep 17 00:00:00 2001 From: houstonhaynes <8174976+houstonhaynes@users.noreply.github.com> Date: Wed, 7 Jan 2026 20:00:12 -0500 Subject: [PATCH 1/4] feat: Add TanStack Store bindings and tests --- .../Partas.Solid.TanStack.Store.fsproj | 27 ++++++ Partas.Solid.TanStack.Store/Spec.fs | 5 + Partas.Solid.TanStack.Store/Store.fs | 47 ++++++++++ .../Partas.Solid.Tests.Plugin.Compiled.fsproj | 3 + .../BasicStore/BasicStore.expected | 13 +++ .../BasicStore/BasicStore.fs | 13 +++ Partas.Solid.Tests.Plugin/Tests.fs | 16 ++++ Partas.Solid.sln | 91 +++++++++++++++---- package-lock.json | 6 ++ 9 files changed, 204 insertions(+), 17 deletions(-) create mode 100644 Partas.Solid.TanStack.Store/Partas.Solid.TanStack.Store.fsproj create mode 100644 Partas.Solid.TanStack.Store/Spec.fs create mode 100644 Partas.Solid.TanStack.Store/Store.fs create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BasicStore/BasicStore.expected create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BasicStore/BasicStore.fs create mode 100644 package-lock.json diff --git a/Partas.Solid.TanStack.Store/Partas.Solid.TanStack.Store.fsproj b/Partas.Solid.TanStack.Store/Partas.Solid.TanStack.Store.fsproj new file mode 100644 index 0000000..df22699 --- /dev/null +++ b/Partas.Solid.TanStack.Store/Partas.Solid.TanStack.Store.fsproj @@ -0,0 +1,27 @@ + + + + library + net6.0 + Partas.Solid.TanStack.Store + Partas.Solid.TanStack.Store + Partas.Solid.TanStack.Store + Shayan Habibi, Vladimir Schur and Contributors + MIT + Partas;Solid;TanStack;Store;Fable + + + + + + + + + + + + + + + + diff --git a/Partas.Solid.TanStack.Store/Spec.fs b/Partas.Solid.TanStack.Store/Spec.fs new file mode 100644 index 0000000..de725b2 --- /dev/null +++ b/Partas.Solid.TanStack.Store/Spec.fs @@ -0,0 +1,5 @@ +namespace Partas.Solid.TanStack.Store + +module Spec = + [] + let PackageName = "@tanstack/solid-store" diff --git a/Partas.Solid.TanStack.Store/Store.fs b/Partas.Solid.TanStack.Store/Store.fs new file mode 100644 index 0000000..0e3c076 --- /dev/null +++ b/Partas.Solid.TanStack.Store/Store.fs @@ -0,0 +1,47 @@ +namespace Partas.Solid.TanStack.Store + +open Fable.Core +open Fable.Core.JS +open Partas.Solid +open System + +[] +module Bindings = + + [] + type Store<'T>(initialValue: 'T) = + member _.state : 'T = jsNative + member _.setState(updater: 'T -> 'T) : unit = jsNative + member _.setState(newState: 'T) : unit = jsNative + + type DerivedFnProps<'T> = + abstract prevVal: 'T option with get + abstract prevDepVals: obj[] option with get + abstract currDepVals: obj[] with get + + [] + type DerivedOptions<'T>(fn: DerivedFnProps<'T> -> 'T, deps: obj[]) = + member val fn = fn with get, set + member val deps = deps with get, set + member val onSubscribe: (Action<'T> * Derived<'T> -> (unit -> unit)) option = None with get, set + member val onUpdate: (unit -> unit) option = None with get, set + + and [] Derived<'T>(options: DerivedOptions<'T>) = + member _.state : 'T = jsNative + member _.mount() : (unit -> unit) = jsNative + + [] + type EffectOptions(fn: unit -> unit, deps: obj[]) = + member val fn = fn with get, set + member val deps = deps with get, set + member val eager = false with get, set + + [] + type Effect(options: EffectOptions) = + member _.mount() : (unit -> unit) = jsNative + + [] + let useStore (store: Store<'T>) (selector: 'T -> 'U) : Accessor<'U> = jsNative + + [] + let useStoreFull (store: Store<'T>) : Accessor<'T> = jsNative \ No newline at end of file diff --git a/Partas.Solid.Tests.Plugin/Compiled/Partas.Solid.Tests.Plugin.Compiled.fsproj b/Partas.Solid.Tests.Plugin/Compiled/Partas.Solid.Tests.Plugin.Compiled.fsproj index 6e9024a..6aee273 100644 --- a/Partas.Solid.Tests.Plugin/Compiled/Partas.Solid.Tests.Plugin.Compiled.fsproj +++ b/Partas.Solid.Tests.Plugin/Compiled/Partas.Solid.Tests.Plugin.Compiled.fsproj @@ -95,12 +95,15 @@ + + + diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BasicStore/BasicStore.expected b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BasicStore/BasicStore.expected new file mode 100644 index 0000000..3610562 --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BasicStore/BasicStore.expected @@ -0,0 +1,13 @@ +import { Store, useStore } from "@tanstack/solid-store"; +import { int32ToString } from "fable-library/Util.js"; + +export const store = new Store(0); + +export function Component() { + const count = useStore(store, (state) => state); + return
{ + store.setState((s) => s + 1); + }}> + {int32ToString(count())} +
; +} diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BasicStore/BasicStore.fs b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BasicStore/BasicStore.fs new file mode 100644 index 0000000..8ed1227 --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BasicStore/BasicStore.fs @@ -0,0 +1,13 @@ +module BasicStore + +open Partas.Solid +open Partas.Solid.TanStack.Store + +let store = new Store(0) + +let Component () = + let count = useStore store (fun state -> state) + + div().on("click", fun _ -> store.setState(fun s -> s + 1)) { + string (count ()) + } \ No newline at end of file diff --git a/Partas.Solid.Tests.Plugin/Tests.fs b/Partas.Solid.Tests.Plugin/Tests.fs index 0e28b82..65e840d 100644 --- a/Partas.Solid.Tests.Plugin/Tests.fs +++ b/Partas.Solid.Tests.Plugin/Tests.fs @@ -106,3 +106,19 @@ let AttributeCases = |> runAttributeCase "PartasImport Attribute" "Pojo" |> runAttributeCase "Pojo Optimisation" ] + +let runTanStackStoreCase name caseName = + let runTanStackStoreCase' caseName = + fun _ -> + built.Value + let folderName = "TanStackStoreCases" + runCase folderName caseName + + testCase name + <| runTanStackStoreCase' caseName + +[] +let TanStackStoreCases = + testList + "TanStackStoreCases" + [ "BasicStore" |> runTanStackStoreCase "Basic Store Usage" ] diff --git a/Partas.Solid.sln b/Partas.Solid.sln index 04b6e58..b551dd4 100644 --- a/Partas.Solid.sln +++ b/Partas.Solid.sln @@ -7,9 +7,9 @@ EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "ScratchTests", "ScratchTests\ScratchTests.fsproj", "{D37CEC92-48BD-4872-8D8B-66721E37E85F}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scripts", "Scripts", "{18D88D48-EDE5-4FB6-8BC4-BE6602CA3871}" - ProjectSection(SolutionItems) = preProject - build.fsx = build.fsx - EndProjectSection + ProjectSection(SolutionItems) = preProject + build.fsx = build.fsx + EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Src", "Src", "{44C0FC57-5623-45D9-8E50-84717E7246CA}" EndProject @@ -24,51 +24,108 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{6B45 EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".environment", ".environment", "{6C4442D5-CCA1-4419-853E-04F17CF40674}" - ProjectSection(SolutionItems) = preProject - .gitignore = .gitignore - LICENSE.txt = LICENSE.txt - README.md = README.md - .editorconfig = .editorconfig - .config/dotnet-tools.json = .config/dotnet-tools.json - docs/RELEASE_NOTES.md = docs/RELEASE_NOTES.md - cliff.toml = cliff.toml - .cliffignore = .cliffignore - EndProjectSection + ProjectSection(SolutionItems) = preProject + .gitignore = .gitignore + LICENSE.txt = LICENSE.txt + README.md = README.md + .editorconfig = .editorconfig + .config\dotnet-tools.json = .config\dotnet-tools.json + docs\RELEASE_NOTES.md = docs\RELEASE_NOTES.md + cliff.toml = cliff.toml + .cliffignore = .cliffignore + EndProjectSection EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Partas.Solid.Tests.Core", "Partas.Solid.Tests.Core\Partas.Solid.Tests.Core.fsproj", "{B3D8EF27-8A7F-46A9-9E71-3AD47B666C60}" EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Partas.Solid.Tests.Plugin", "Partas.Solid.Tests.Plugin\Partas.Solid.Tests.Plugin.fsproj", "{3C3EE6AA-E4DB-4852-BBC9-620A3FD48CB4}" EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Partas.Solid.TanStack.Store", "Partas.Solid.TanStack.Store\Partas.Solid.TanStack.Store.fsproj", "{B2A650F2-8401-43D1-B107-41B8361859DF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {2360B137-35B4-4CC8-A004-E19DEC1D5759}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2360B137-35B4-4CC8-A004-E19DEC1D5759}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2360B137-35B4-4CC8-A004-E19DEC1D5759}.Debug|x64.ActiveCfg = Debug|Any CPU + {2360B137-35B4-4CC8-A004-E19DEC1D5759}.Debug|x64.Build.0 = Debug|Any CPU + {2360B137-35B4-4CC8-A004-E19DEC1D5759}.Debug|x86.ActiveCfg = Debug|Any CPU + {2360B137-35B4-4CC8-A004-E19DEC1D5759}.Debug|x86.Build.0 = Debug|Any CPU {2360B137-35B4-4CC8-A004-E19DEC1D5759}.Release|Any CPU.ActiveCfg = Release|Any CPU {2360B137-35B4-4CC8-A004-E19DEC1D5759}.Release|Any CPU.Build.0 = Release|Any CPU + {2360B137-35B4-4CC8-A004-E19DEC1D5759}.Release|x64.ActiveCfg = Release|Any CPU + {2360B137-35B4-4CC8-A004-E19DEC1D5759}.Release|x64.Build.0 = Release|Any CPU + {2360B137-35B4-4CC8-A004-E19DEC1D5759}.Release|x86.ActiveCfg = Release|Any CPU + {2360B137-35B4-4CC8-A004-E19DEC1D5759}.Release|x86.Build.0 = Release|Any CPU {1AE69025-B907-4C3A-AB48-0093DB5070A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1AE69025-B907-4C3A-AB48-0093DB5070A4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1AE69025-B907-4C3A-AB48-0093DB5070A4}.Debug|x64.ActiveCfg = Debug|Any CPU + {1AE69025-B907-4C3A-AB48-0093DB5070A4}.Debug|x64.Build.0 = Debug|Any CPU + {1AE69025-B907-4C3A-AB48-0093DB5070A4}.Debug|x86.ActiveCfg = Debug|Any CPU + {1AE69025-B907-4C3A-AB48-0093DB5070A4}.Debug|x86.Build.0 = Debug|Any CPU {1AE69025-B907-4C3A-AB48-0093DB5070A4}.Release|Any CPU.ActiveCfg = Release|Any CPU {1AE69025-B907-4C3A-AB48-0093DB5070A4}.Release|Any CPU.Build.0 = Release|Any CPU - {107B9E6E-5EF2-4ECA-91F1-11D3A7940D4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {107B9E6E-5EF2-4ECA-91F1-11D3A7940D4F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {107B9E6E-5EF2-4ECA-91F1-11D3A7940D4F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {107B9E6E-5EF2-4ECA-91F1-11D3A7940D4F}.Release|Any CPU.Build.0 = Release|Any CPU + {1AE69025-B907-4C3A-AB48-0093DB5070A4}.Release|x64.ActiveCfg = Release|Any CPU + {1AE69025-B907-4C3A-AB48-0093DB5070A4}.Release|x64.Build.0 = Release|Any CPU + {1AE69025-B907-4C3A-AB48-0093DB5070A4}.Release|x86.ActiveCfg = Release|Any CPU + {1AE69025-B907-4C3A-AB48-0093DB5070A4}.Release|x86.Build.0 = Release|Any CPU {D37CEC92-48BD-4872-8D8B-66721E37E85F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D37CEC92-48BD-4872-8D8B-66721E37E85F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D37CEC92-48BD-4872-8D8B-66721E37E85F}.Debug|x64.ActiveCfg = Debug|Any CPU + {D37CEC92-48BD-4872-8D8B-66721E37E85F}.Debug|x64.Build.0 = Debug|Any CPU + {D37CEC92-48BD-4872-8D8B-66721E37E85F}.Debug|x86.ActiveCfg = Debug|Any CPU + {D37CEC92-48BD-4872-8D8B-66721E37E85F}.Debug|x86.Build.0 = Debug|Any CPU {D37CEC92-48BD-4872-8D8B-66721E37E85F}.Release|Any CPU.ActiveCfg = Release|Any CPU {D37CEC92-48BD-4872-8D8B-66721E37E85F}.Release|Any CPU.Build.0 = Release|Any CPU + {D37CEC92-48BD-4872-8D8B-66721E37E85F}.Release|x64.ActiveCfg = Release|Any CPU + {D37CEC92-48BD-4872-8D8B-66721E37E85F}.Release|x64.Build.0 = Release|Any CPU + {D37CEC92-48BD-4872-8D8B-66721E37E85F}.Release|x86.ActiveCfg = Release|Any CPU + {D37CEC92-48BD-4872-8D8B-66721E37E85F}.Release|x86.Build.0 = Release|Any CPU {B3D8EF27-8A7F-46A9-9E71-3AD47B666C60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B3D8EF27-8A7F-46A9-9E71-3AD47B666C60}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B3D8EF27-8A7F-46A9-9E71-3AD47B666C60}.Debug|x64.ActiveCfg = Debug|Any CPU + {B3D8EF27-8A7F-46A9-9E71-3AD47B666C60}.Debug|x64.Build.0 = Debug|Any CPU + {B3D8EF27-8A7F-46A9-9E71-3AD47B666C60}.Debug|x86.ActiveCfg = Debug|Any CPU + {B3D8EF27-8A7F-46A9-9E71-3AD47B666C60}.Debug|x86.Build.0 = Debug|Any CPU {B3D8EF27-8A7F-46A9-9E71-3AD47B666C60}.Release|Any CPU.ActiveCfg = Release|Any CPU {B3D8EF27-8A7F-46A9-9E71-3AD47B666C60}.Release|Any CPU.Build.0 = Release|Any CPU + {B3D8EF27-8A7F-46A9-9E71-3AD47B666C60}.Release|x64.ActiveCfg = Release|Any CPU + {B3D8EF27-8A7F-46A9-9E71-3AD47B666C60}.Release|x64.Build.0 = Release|Any CPU + {B3D8EF27-8A7F-46A9-9E71-3AD47B666C60}.Release|x86.ActiveCfg = Release|Any CPU + {B3D8EF27-8A7F-46A9-9E71-3AD47B666C60}.Release|x86.Build.0 = Release|Any CPU {3C3EE6AA-E4DB-4852-BBC9-620A3FD48CB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3C3EE6AA-E4DB-4852-BBC9-620A3FD48CB4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3C3EE6AA-E4DB-4852-BBC9-620A3FD48CB4}.Debug|x64.ActiveCfg = Debug|Any CPU + {3C3EE6AA-E4DB-4852-BBC9-620A3FD48CB4}.Debug|x64.Build.0 = Debug|Any CPU + {3C3EE6AA-E4DB-4852-BBC9-620A3FD48CB4}.Debug|x86.ActiveCfg = Debug|Any CPU + {3C3EE6AA-E4DB-4852-BBC9-620A3FD48CB4}.Debug|x86.Build.0 = Debug|Any CPU {3C3EE6AA-E4DB-4852-BBC9-620A3FD48CB4}.Release|Any CPU.ActiveCfg = Release|Any CPU {3C3EE6AA-E4DB-4852-BBC9-620A3FD48CB4}.Release|Any CPU.Build.0 = Release|Any CPU + {3C3EE6AA-E4DB-4852-BBC9-620A3FD48CB4}.Release|x64.ActiveCfg = Release|Any CPU + {3C3EE6AA-E4DB-4852-BBC9-620A3FD48CB4}.Release|x64.Build.0 = Release|Any CPU + {3C3EE6AA-E4DB-4852-BBC9-620A3FD48CB4}.Release|x86.ActiveCfg = Release|Any CPU + {3C3EE6AA-E4DB-4852-BBC9-620A3FD48CB4}.Release|x86.Build.0 = Release|Any CPU + {B2A650F2-8401-43D1-B107-41B8361859DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B2A650F2-8401-43D1-B107-41B8361859DF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B2A650F2-8401-43D1-B107-41B8361859DF}.Debug|x64.ActiveCfg = Debug|Any CPU + {B2A650F2-8401-43D1-B107-41B8361859DF}.Debug|x64.Build.0 = Debug|Any CPU + {B2A650F2-8401-43D1-B107-41B8361859DF}.Debug|x86.ActiveCfg = Debug|Any CPU + {B2A650F2-8401-43D1-B107-41B8361859DF}.Debug|x86.Build.0 = Debug|Any CPU + {B2A650F2-8401-43D1-B107-41B8361859DF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B2A650F2-8401-43D1-B107-41B8361859DF}.Release|Any CPU.Build.0 = Release|Any CPU + {B2A650F2-8401-43D1-B107-41B8361859DF}.Release|x64.ActiveCfg = Release|Any CPU + {B2A650F2-8401-43D1-B107-41B8361859DF}.Release|x64.Build.0 = Release|Any CPU + {B2A650F2-8401-43D1-B107-41B8361859DF}.Release|x86.ActiveCfg = Release|Any CPU + {B2A650F2-8401-43D1-B107-41B8361859DF}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {2360B137-35B4-4CC8-A004-E19DEC1D5759} = {44C0FC57-5623-45D9-8E50-84717E7246CA} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a68515f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "Partas.Solid", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} From 811fdb2a3996a23988af2e8765c951ae072ebe54 Mon Sep 17 00:00:00 2001 From: houstonhaynes <8174976+houstonhaynes@users.noreply.github.com> Date: Wed, 7 Jan 2026 20:48:28 -0500 Subject: [PATCH 2/4] introduce TanStack.Store --- .../Partas.Solid.FablePlugin.fsproj | 4 +- Partas.Solid.TanStack.Store/Store.fs | 95 +++---- .../Partas.Solid.Tests.Plugin.Compiled.fsproj | 8 + .../BasicStore/BasicStore.expected | 14 +- .../BasicStore/BasicStore.fs | 25 +- .../DerivedStore/DerivedStore.expected | 29 ++ .../DerivedStore/DerivedStore.fs | 28 ++ .../EffectStore/EffectStore.expected | 30 ++ .../EffectStore/EffectStore.fs | 21 ++ .../RecordState/RecordState.expected | 38 +++ .../RecordState/RecordState.fs | 31 +++ .../UseStoreFull/UseStoreFull.expected | 13 + .../UseStoreFull/UseStoreFull.fs | 12 + Partas.Solid.Tests.Plugin/Tests.fs | 257 +++++++++--------- 14 files changed, 414 insertions(+), 191 deletions(-) create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedStore/DerivedStore.expected create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedStore/DerivedStore.fs create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/EffectStore/EffectStore.expected create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/EffectStore/EffectStore.fs create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/RecordState/RecordState.expected create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/RecordState/RecordState.fs create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/UseStoreFull/UseStoreFull.expected create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/UseStoreFull/UseStoreFull.fs diff --git a/Partas.Solid.FablePlugin/Partas.Solid.FablePlugin.fsproj b/Partas.Solid.FablePlugin/Partas.Solid.FablePlugin.fsproj index 9dd6e2c..843f0cd 100644 --- a/Partas.Solid.FablePlugin/Partas.Solid.FablePlugin.fsproj +++ b/Partas.Solid.FablePlugin/Partas.Solid.FablePlugin.fsproj @@ -1,7 +1,8 @@  - net6.0 + net8.0 + false 3239;0025 Partas.Solid.FablePlugin Partas.Solid.FablePlugin @@ -27,6 +28,7 @@ + diff --git a/Partas.Solid.TanStack.Store/Store.fs b/Partas.Solid.TanStack.Store/Store.fs index 0e3c076..383e98b 100644 --- a/Partas.Solid.TanStack.Store/Store.fs +++ b/Partas.Solid.TanStack.Store/Store.fs @@ -1,47 +1,48 @@ -namespace Partas.Solid.TanStack.Store - -open Fable.Core -open Fable.Core.JS -open Partas.Solid -open System - -[] -module Bindings = - - [] - type Store<'T>(initialValue: 'T) = - member _.state : 'T = jsNative - member _.setState(updater: 'T -> 'T) : unit = jsNative - member _.setState(newState: 'T) : unit = jsNative - - type DerivedFnProps<'T> = - abstract prevVal: 'T option with get - abstract prevDepVals: obj[] option with get - abstract currDepVals: obj[] with get - - [] - type DerivedOptions<'T>(fn: DerivedFnProps<'T> -> 'T, deps: obj[]) = - member val fn = fn with get, set - member val deps = deps with get, set - member val onSubscribe: (Action<'T> * Derived<'T> -> (unit -> unit)) option = None with get, set - member val onUpdate: (unit -> unit) option = None with get, set - - and [] Derived<'T>(options: DerivedOptions<'T>) = - member _.state : 'T = jsNative - member _.mount() : (unit -> unit) = jsNative - - [] - type EffectOptions(fn: unit -> unit, deps: obj[]) = - member val fn = fn with get, set - member val deps = deps with get, set - member val eager = false with get, set - - [] - type Effect(options: EffectOptions) = - member _.mount() : (unit -> unit) = jsNative - - [] - let useStore (store: Store<'T>) (selector: 'T -> 'U) : Accessor<'U> = jsNative - - [] - let useStoreFull (store: Store<'T>) : Accessor<'T> = jsNative \ No newline at end of file +namespace Partas.Solid.TanStack.Store + +open Fable.Core +open Fable.Core.JS +open Partas.Solid +open System + +[] +module Bindings = + + [] + type Store<'T>(initialValue: 'T) = + member _.state: 'T = jsNative + member _.setState(updater: 'T -> 'T) : unit = jsNative + member _.setState(newState: 'T) : unit = jsNative + + [] + type DerivedFnProps<'T> = + abstract member prevVal: 'T option with get + abstract member prevDepVals: obj[] option with get + abstract member currDepVals: obj[] with get + + [] + type DerivedOptions<'T>(fn: DerivedFnProps<'T> -> 'T, deps: obj[]) = + member val fn = fn with get, set + member val deps = deps with get, set + member val onSubscribe: (Action<'T> * Derived<'T> -> (unit -> unit)) option = None with get, set + member val onUpdate: (unit -> unit) option = None with get, set + + and [] Derived<'T>(options: DerivedOptions<'T>) = + member _.state: 'T = jsNative + member _.mount() : (unit -> unit) = jsNative + + [] + type EffectOptions(fn: unit -> unit, deps: obj[]) = + member val fn = fn with get, set + member val deps = deps with get, set + member val eager = false with get, set + + [] + type Effect(options: EffectOptions) = + member _.mount() : (unit -> unit) = jsNative + + [] + let useStore (store: Store<'T>) (selector: 'T -> 'U) : Accessor<'U> = jsNative + + [] + let useStoreFull (store: Store<'T>) : Accessor<'T> = jsNative diff --git a/Partas.Solid.Tests.Plugin/Compiled/Partas.Solid.Tests.Plugin.Compiled.fsproj b/Partas.Solid.Tests.Plugin/Compiled/Partas.Solid.Tests.Plugin.Compiled.fsproj index 6aee273..c05246a 100644 --- a/Partas.Solid.Tests.Plugin/Compiled/Partas.Solid.Tests.Plugin.Compiled.fsproj +++ b/Partas.Solid.Tests.Plugin/Compiled/Partas.Solid.Tests.Plugin.Compiled.fsproj @@ -97,6 +97,14 @@ + + + + + + + + diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BasicStore/BasicStore.expected b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BasicStore/BasicStore.expected index 3610562..664e93a 100644 --- a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BasicStore/BasicStore.expected +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BasicStore/BasicStore.expected @@ -1,13 +1,15 @@ -import { Store, useStore } from "@tanstack/solid-store"; -import { int32ToString } from "fable-library/Util.js"; + +import { useStore, Store } from "@tanstack/solid-store"; +import { int32ToString } from "../../fable_modules/fable-library-js.5.0.0-alpha.14/Util.js"; export const store = new Store(0); export function Component() { - const count = useStore(store, (state) => state); - return
{ - store.setState((s) => s + 1); - }}> + const count = useStore(store, (state) => (state | 0)); + return
{ + store.setState((s) => ((s + 1) | 0)); + }}> {int32ToString(count())}
; } + diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BasicStore/BasicStore.fs b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BasicStore/BasicStore.fs index 8ed1227..136211d 100644 --- a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BasicStore/BasicStore.fs +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BasicStore/BasicStore.fs @@ -1,13 +1,12 @@ -module BasicStore - -open Partas.Solid -open Partas.Solid.TanStack.Store - -let store = new Store(0) - -let Component () = - let count = useStore store (fun state -> state) - - div().on("click", fun _ -> store.setState(fun s -> s + 1)) { - string (count ()) - } \ No newline at end of file +module BasicStore + +open Partas.Solid +open Partas.Solid.TanStack.Store + +let store = new Store (0) + +[] +let Component () = + let count = useStore store (fun state -> state) + + div().on ("click", fun _ -> store.setState (fun s -> s + 1)) { string (count ()) } diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedStore/DerivedStore.expected b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedStore/DerivedStore.expected new file mode 100644 index 0000000..dc4a7c9 --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedStore/DerivedStore.expected @@ -0,0 +1,29 @@ + +import { useStore, Derived, Store } from "@tanstack/solid-store"; + +export const countStore = new Store(5); + +export const doubledOptions = { + fn: (props) => ((countStore.state * 2) | 0), + deps: [countStore], +}; + +export const doubled = new Derived(doubledOptions); + +export function Component() { + const count = useStore(countStore, (s) => (s | 0)); + return
+ + {`Count: ${count()}`} + + + {`Doubled: ${doubled.state}`} + + +
; +} + diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedStore/DerivedStore.fs b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedStore/DerivedStore.fs new file mode 100644 index 0000000..b52669d --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedStore/DerivedStore.fs @@ -0,0 +1,28 @@ +module DerivedStore + +open Partas.Solid +open Partas.Solid.TanStack.Store + +let countStore = new Store (5) + +let doubledOptions = + DerivedOptions ( + (fun props -> + let current = countStore.state + + current + * 2), + [| countStore |] + ) + +let doubled = new Derived (doubledOptions) + +[] +let Component () = + let count = useStore countStore (fun s -> s) + + div () { + span () { $"Count: {count ()}" } + span () { $"Doubled: {doubled.state}" } + button().on ("click", fun _ -> countStore.setState (fun s -> s + 1)) { "Increment" } + } diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/EffectStore/EffectStore.expected b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/EffectStore/EffectStore.expected new file mode 100644 index 0000000..a08ccbe --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/EffectStore/EffectStore.expected @@ -0,0 +1,30 @@ + +import { useStore, Effect, Store } from "@tanstack/solid-store"; +import { some } from "../../fable_modules/fable-library-js.5.0.0-alpha.14/Option.js"; +import { int32ToString } from "../../fable_modules/fable-library-js.5.0.0-alpha.14/Util.js"; + +export const store = new Store(0); + +export const effectOptions = { + fn: () => { + console.log(some("Store changed:"), store.state); + }, + deps: [store], +}; + +export const storeEffect = new Effect(effectOptions); + +export function Component() { + const count = useStore(store, (s) => (s | 0)); + return
+ + {int32ToString(count())} + + +
; +} + diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/EffectStore/EffectStore.fs b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/EffectStore/EffectStore.fs new file mode 100644 index 0000000..883e53f --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/EffectStore/EffectStore.fs @@ -0,0 +1,21 @@ +module EffectStore + +open Partas.Solid +open Partas.Solid.TanStack.Store +open Fable.Core + +let store = new Store (0) + +let effectOptions = + EffectOptions ((fun () -> JS.console.log ("Store changed:", store.state)), [| store |]) + +let storeEffect = new Effect (effectOptions) + +[] +let Component () = + let count = useStore store (fun s -> s) + + div () { + span () { string (count ()) } + button().on ("click", fun _ -> store.setState (fun s -> s + 1)) { "Increment" } + } diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/RecordState/RecordState.expected b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/RecordState/RecordState.expected new file mode 100644 index 0000000..cf66933 --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/RecordState/RecordState.expected @@ -0,0 +1,38 @@ + +import { Record } from "../../fable_modules/fable-library-js.5.0.0-alpha.14/Types.js"; +import { record_type, string_type, int32_type } from "../../fable_modules/fable-library-js.5.0.0-alpha.14/Reflection.js"; +import { useStore, Store } from "@tanstack/solid-store"; +import { int32ToString } from "../../fable_modules/fable-library-js.5.0.0-alpha.14/Util.js"; + +export class AppState extends Record { + constructor(count, name) { + super(); + this.count = (count | 0); + this.name = name; + } +} + +export function AppState_$reflection() { + return record_type("RecordState.AppState", [], AppState, () => [["count", int32_type], ["name", string_type]]); +} + +export const store = new Store(new AppState(0, "Test")); + +export function Component() { + const count = useStore(store, (s) => (s.count | 0)); + const name = useStore(store, (s_1) => s_1.name); + return
+ + {name()} + + + {int32ToString(count())} + + +
; +} + diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/RecordState/RecordState.fs b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/RecordState/RecordState.fs new file mode 100644 index 0000000..1473937 --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/RecordState/RecordState.fs @@ -0,0 +1,31 @@ +module RecordState + +open Partas.Solid +open Partas.Solid.TanStack.Store + +type AppState = { count: int; name: string } + +let store = new Store ({ count = 0; name = "Test" }) + +[] +let Component () = + let count = useStore store (fun s -> s.count) + let name = useStore store (fun s -> s.name) + + div () { + span () { name () } + span () { string (count ()) } + + button() + .on ( + "click", + fun _ -> + store.setState (fun s -> + { s with + count = + s.count + + 1 }) + ) { + "Increment" + } + } diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/UseStoreFull/UseStoreFull.expected b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/UseStoreFull/UseStoreFull.expected new file mode 100644 index 0000000..fcd99c9 --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/UseStoreFull/UseStoreFull.expected @@ -0,0 +1,13 @@ + +import { useStoreFull, Store } from "@tanstack/solid-store"; +import { int32ToString } from "../../fable_modules/fable-library-js.5.0.0-alpha.14/Util.js"; + +export const store = new Store(42); + +export function Component() { + const state = useStoreFull(store); + return
+ {int32ToString(state())} +
; +} + diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/UseStoreFull/UseStoreFull.fs b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/UseStoreFull/UseStoreFull.fs new file mode 100644 index 0000000..47396e0 --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/UseStoreFull/UseStoreFull.fs @@ -0,0 +1,12 @@ +module UseStoreFull + +open Partas.Solid +open Partas.Solid.TanStack.Store + +let store = new Store (42) + +[] +let Component () = + let state = useStoreFull store + + div () { string (state ()) } diff --git a/Partas.Solid.Tests.Plugin/Tests.fs b/Partas.Solid.Tests.Plugin/Tests.fs index 65e840d..0c73858 100644 --- a/Partas.Solid.Tests.Plugin/Tests.fs +++ b/Partas.Solid.Tests.Plugin/Tests.fs @@ -1,124 +1,133 @@ -module Partas.Solid.Tests.Plugin.IssueTests - -open Expecto -open Partas.Solid.Tests.Plugin.Common - -let built = lazy buildCases () - -let runIssueCase caseName = - fun _ -> - built.Value - let folderName = "IssueCases" - runCase folderName caseName - -let runSolidCase name caseName = - let runSolidCase' caseName = - fun _ -> - built.Value - let folderName = "SolidCases" - runCase folderName caseName - - testCase name - <| runSolidCase' caseName - -let runAttributeCase name caseName = - let runAttributeCase' caseName = - fun _ -> - built.Value - let folderName = "AttributeCases" - runCase folderName caseName - - testCase name - <| runAttributeCase' caseName - - -[] -let IssueCases = - testList - "IssueCases" - [ testCase "#2 createSignal getting converted into Tag" - <| runIssueCase "CreateSignalTagConstructor" - testCase "#9 Index access is rendered" - <| runIssueCase "IndexAccess" - testCase "#11 String interpolation is transformed" - <| runIssueCase "TransformInsideStringInterpolation" - testCase "#13 Getter extensions are transformed" - <| runIssueCase "TransformGetterExtensions" - testCase "#14 Object Expressions are transformed" - <| runIssueCase "ObjectExpressions" - testCase "#15 props.words.ToCharArray() |> Array.map string" - <| runIssueCase "CharArrayMapping" - testCase "#16 val mutable overloads render" - <| runIssueCase "ValMutableOverloads" - testCase "#18 indexed identifiers spread" - <| runIssueCase "IndexedPropSpreading" - testCase "#28 ThisArg is transformed" - <| runIssueCase "ThisArgTransforms" - testCase "#29 val mutable overloads render 2" - <| runIssueCase "InheritedProperty" - testCase "#37 passing objects as event handlers" - <| runIssueCase "ObjectEventHandler" ] - -[] -let SolidCases = - testList - "SolidCases" - [ runSolidCase "TagsNoChildren" "TagsNoChildren" - runSolidCase "Library Imports and User Imports" "LibraryImport" - "TagExtensions" - |> runSolidCase "Tag Extensions" - "ImportedTagsWithExtensions" - |> runSolidCase "Imported Tags with Extensions" - "ChildrenSimple" - |> runSolidCase "Tags with child elements" - "MergeProps" - |> runSolidCase "Default property setting" - "SplitProps" - |> runSolidCase "Property accessing with split props" - "CombinedSpread" - |> runSolidCase "mergeProps splitProps and property spreading" - "OperatorsInProps" - |> runSolidCase "Property getters mixed with Operands are transformed" - "FieldGettersInComputations" - |> runSolidCase "Field getters and records are transformed" - "TagsAsValuesSimple" - |> runSolidCase "Tags can be used as values" - "FieldGetExpressionsTransformed" - |> runSolidCase "FieldGets like props.words.Length are transformed" - "SignalSetterInvoke" - |> runSolidCase "Signal Setters can be invoked with a handler" - "ExperimentalBuilders" - |> runSolidCase "Experimental Builders compile correct output" - "CssStyles" - |> runSolidCase "CssStyle definitions compiles correct output" - "ChildLambdaProvider" - |> runSolidCase "ChildLambdaProvider interfaces" - "SolidComponentAsTagValues" - |> runSolidCase "SolidComponent let bindings as TagValues" - "ValueUnrollerDecisionTree" - |> runSolidCase "Decision Trees in arrays do not spawn singleton instructions" ] - -[] -let AttributeCases = - testList - "AttributeCases" - [ "PartasImportAttr" - |> runAttributeCase "PartasImport Attribute" - "Pojo" - |> runAttributeCase "Pojo Optimisation" ] - -let runTanStackStoreCase name caseName = - let runTanStackStoreCase' caseName = - fun _ -> - built.Value - let folderName = "TanStackStoreCases" - runCase folderName caseName - - testCase name - <| runTanStackStoreCase' caseName - -[] -let TanStackStoreCases = - testList - "TanStackStoreCases" - [ "BasicStore" |> runTanStackStoreCase "Basic Store Usage" ] +module Partas.Solid.Tests.Plugin.IssueTests + +open Expecto +open Partas.Solid.Tests.Plugin.Common + +let built = lazy buildCases () + +let runIssueCase caseName = + fun _ -> + built.Value + let folderName = "IssueCases" + runCase folderName caseName + +let runSolidCase name caseName = + let runSolidCase' caseName = + fun _ -> + built.Value + let folderName = "SolidCases" + runCase folderName caseName + + testCase name + <| runSolidCase' caseName + +let runAttributeCase name caseName = + let runAttributeCase' caseName = + fun _ -> + built.Value + let folderName = "AttributeCases" + runCase folderName caseName + + testCase name + <| runAttributeCase' caseName + + +[] +let IssueCases = + testList + "IssueCases" + [ testCase "#2 createSignal getting converted into Tag" + <| runIssueCase "CreateSignalTagConstructor" + testCase "#9 Index access is rendered" + <| runIssueCase "IndexAccess" + testCase "#11 String interpolation is transformed" + <| runIssueCase "TransformInsideStringInterpolation" + testCase "#13 Getter extensions are transformed" + <| runIssueCase "TransformGetterExtensions" + testCase "#14 Object Expressions are transformed" + <| runIssueCase "ObjectExpressions" + testCase "#15 props.words.ToCharArray() |> Array.map string" + <| runIssueCase "CharArrayMapping" + testCase "#16 val mutable overloads render" + <| runIssueCase "ValMutableOverloads" + testCase "#18 indexed identifiers spread" + <| runIssueCase "IndexedPropSpreading" + testCase "#28 ThisArg is transformed" + <| runIssueCase "ThisArgTransforms" + testCase "#29 val mutable overloads render 2" + <| runIssueCase "InheritedProperty" + testCase "#37 passing objects as event handlers" + <| runIssueCase "ObjectEventHandler" ] + +[] +let SolidCases = + testList + "SolidCases" + [ runSolidCase "TagsNoChildren" "TagsNoChildren" + runSolidCase "Library Imports and User Imports" "LibraryImport" + "TagExtensions" + |> runSolidCase "Tag Extensions" + "ImportedTagsWithExtensions" + |> runSolidCase "Imported Tags with Extensions" + "ChildrenSimple" + |> runSolidCase "Tags with child elements" + "MergeProps" + |> runSolidCase "Default property setting" + "SplitProps" + |> runSolidCase "Property accessing with split props" + "CombinedSpread" + |> runSolidCase "mergeProps splitProps and property spreading" + "OperatorsInProps" + |> runSolidCase "Property getters mixed with Operands are transformed" + "FieldGettersInComputations" + |> runSolidCase "Field getters and records are transformed" + "TagsAsValuesSimple" + |> runSolidCase "Tags can be used as values" + "FieldGetExpressionsTransformed" + |> runSolidCase "FieldGets like props.words.Length are transformed" + "SignalSetterInvoke" + |> runSolidCase "Signal Setters can be invoked with a handler" + "ExperimentalBuilders" + |> runSolidCase "Experimental Builders compile correct output" + "CssStyles" + |> runSolidCase "CssStyle definitions compiles correct output" + "ChildLambdaProvider" + |> runSolidCase "ChildLambdaProvider interfaces" + "SolidComponentAsTagValues" + |> runSolidCase "SolidComponent let bindings as TagValues" + "ValueUnrollerDecisionTree" + |> runSolidCase "Decision Trees in arrays do not spawn singleton instructions" ] + +[] +let AttributeCases = + testList + "AttributeCases" + [ "PartasImportAttr" + |> runAttributeCase "PartasImport Attribute" + "Pojo" + |> runAttributeCase "Pojo Optimisation" ] + +let runTanStackStoreCase name caseName = + let runTanStackStoreCase' caseName = + fun _ -> + built.Value + let folderName = "TanStackStoreCases" + runCase folderName caseName + + testCase name + <| runTanStackStoreCase' caseName + +[] +let TanStackStoreCases = + testList + "TanStackStoreCases" + [ "BasicStore" + |> runTanStackStoreCase "Basic Store Usage" + "UseStoreFull" + |> runTanStackStoreCase "useStoreFull hook" + "RecordState" + |> runTanStackStoreCase "Record type state" + "DerivedStore" + |> runTanStackStoreCase "Derived store" + "EffectStore" + |> runTanStackStoreCase "Effect store" ] From 2dc45ef6c6c584f5d949bd565498d8b19553643f Mon Sep 17 00:00:00 2001 From: houstonhaynes <8174976+houstonhaynes@users.noreply.github.com> Date: Wed, 7 Jan 2026 20:53:14 -0500 Subject: [PATCH 3/4] restored version --- Partas.Solid.FablePlugin/Partas.Solid.FablePlugin.fsproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Partas.Solid.FablePlugin/Partas.Solid.FablePlugin.fsproj b/Partas.Solid.FablePlugin/Partas.Solid.FablePlugin.fsproj index 843f0cd..990cdc1 100644 --- a/Partas.Solid.FablePlugin/Partas.Solid.FablePlugin.fsproj +++ b/Partas.Solid.FablePlugin/Partas.Solid.FablePlugin.fsproj @@ -1,7 +1,7 @@  - net8.0 + net6.0 false 3239;0025 Partas.Solid.FablePlugin From c75185185a704095b92ffc02467a39eaccad39a0 Mon Sep 17 00:00:00 2001 From: houstonhaynes <8174976+houstonhaynes@users.noreply.github.com> Date: Wed, 7 Jan 2026 21:19:49 -0500 Subject: [PATCH 4/4] round out tests --- Partas.Solid.TanStack.Store/Store.fs | 13 +++++++- .../Partas.Solid.Tests.Plugin.Compiled.fsproj | 14 ++++++++ .../BatchUpdates/BatchUpdates.expected | 13 ++++++++ .../BatchUpdates/BatchUpdates.fs | 11 +++++++ .../DerivedDepVals/DerivedDepVals.expected | 14 ++++++++ .../DerivedDepVals/DerivedDepVals.fs | 24 ++++++++++++++ .../DerivedPrevVal/DerivedPrevVal.expected | 16 ++++++++++ .../DerivedPrevVal/DerivedPrevVal.fs | 21 ++++++++++++ .../EffectEager/EffectEager.expected | 19 +++++++++++ .../EffectEager/EffectEager.fs | 15 +++++++++ .../MountUnmount/MountUnmount.expected | 32 +++++++++++++++++++ .../MountUnmount/MountUnmount.fs | 31 ++++++++++++++++++ .../StoreOptions/StoreOptions.expected | 14 ++++++++ .../StoreOptions/StoreOptions.fs | 17 ++++++++++ .../Subscribe/Subscribe.expected | 14 ++++++++ .../TanStackStoreCases/Subscribe/Subscribe.fs | 10 ++++++ Partas.Solid.Tests.Plugin/Tests.fs | 16 +++++++++- 17 files changed, 292 insertions(+), 2 deletions(-) create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BatchUpdates/BatchUpdates.expected create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BatchUpdates/BatchUpdates.fs create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedDepVals/DerivedDepVals.expected create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedDepVals/DerivedDepVals.fs create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedPrevVal/DerivedPrevVal.expected create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedPrevVal/DerivedPrevVal.fs create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/EffectEager/EffectEager.expected create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/EffectEager/EffectEager.fs create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/MountUnmount/MountUnmount.expected create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/MountUnmount/MountUnmount.fs create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/StoreOptions/StoreOptions.expected create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/StoreOptions/StoreOptions.fs create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/Subscribe/Subscribe.expected create mode 100644 Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/Subscribe/Subscribe.fs diff --git a/Partas.Solid.TanStack.Store/Store.fs b/Partas.Solid.TanStack.Store/Store.fs index 383e98b..0f7d4a1 100644 --- a/Partas.Solid.TanStack.Store/Store.fs +++ b/Partas.Solid.TanStack.Store/Store.fs @@ -8,11 +8,22 @@ open System [] module Bindings = + /// Store options - use anonymous record with !! operator: + /// new Store(0, !!{| updateFn = fun prev updater -> ... |}) + [] + type StoreOptions<'T> = + abstract member updateFn: ('T -> ('T -> 'T) -> 'T) option with get + abstract member onUpdate: (unit -> unit) option with get + [] - type Store<'T>(initialValue: 'T) = + type Store<'T>(initialValue: 'T, ?options: StoreOptions<'T>) = member _.state: 'T = jsNative member _.setState(updater: 'T -> 'T) : unit = jsNative member _.setState(newState: 'T) : unit = jsNative + member _.subscribe(callback: unit -> unit) : (unit -> unit) = jsNative + + [] + let batch (fn: unit -> unit) : unit = jsNative [] type DerivedFnProps<'T> = diff --git a/Partas.Solid.Tests.Plugin/Compiled/Partas.Solid.Tests.Plugin.Compiled.fsproj b/Partas.Solid.Tests.Plugin/Compiled/Partas.Solid.Tests.Plugin.Compiled.fsproj index c05246a..1c3f5e4 100644 --- a/Partas.Solid.Tests.Plugin/Compiled/Partas.Solid.Tests.Plugin.Compiled.fsproj +++ b/Partas.Solid.Tests.Plugin/Compiled/Partas.Solid.Tests.Plugin.Compiled.fsproj @@ -105,6 +105,20 @@ + + + + + + + + + + + + + + diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BatchUpdates/BatchUpdates.expected b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BatchUpdates/BatchUpdates.expected new file mode 100644 index 0000000..3f03886 --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BatchUpdates/BatchUpdates.expected @@ -0,0 +1,13 @@ + +import { batch, Store } from "@tanstack/solid-store"; + +export const store = new Store(0); + +export function performBatchUpdate() { + batch(() => { + store.setState((_arg) => 1); + store.setState((_arg_1) => 2); + store.setState((s) => ((s + 1) | 0)); + }); +} + diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BatchUpdates/BatchUpdates.fs b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BatchUpdates/BatchUpdates.fs new file mode 100644 index 0000000..32689fd --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/BatchUpdates/BatchUpdates.fs @@ -0,0 +1,11 @@ +module BatchUpdates + +open Partas.Solid.TanStack.Store + +let store = new Store (0) + +let performBatchUpdate () = + batch (fun () -> + store.setState (fun _ -> 1) + store.setState (fun _ -> 2) + store.setState (fun s -> s + 1)) diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedDepVals/DerivedDepVals.expected b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedDepVals/DerivedDepVals.expected new file mode 100644 index 0000000..6d933d7 --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedDepVals/DerivedDepVals.expected @@ -0,0 +1,14 @@ + +import { Derived, Store } from "@tanstack/solid-store"; +import { map, defaultArg } from "../../fable_modules/fable-library-js.5.0.0-alpha.14/Option.js"; +import { item } from "../../fable_modules/fable-library-js.5.0.0-alpha.14/Array.js"; + +export const count = new Store(1); + +export const sumWithPrev = new Derived({ + fn: (props) => ((defaultArg(map((arr) => (item(0, arr) | 0), props.prevDepVals), 0) + item(0, props.currDepVals)) | 0), + deps: [count], +}); + +export const unmount = sumWithPrev.mount(); + diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedDepVals/DerivedDepVals.fs b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedDepVals/DerivedDepVals.fs new file mode 100644 index 0000000..0259dea --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedDepVals/DerivedDepVals.fs @@ -0,0 +1,24 @@ +module DerivedDepVals + +open Partas.Solid.TanStack.Store + +let count = new Store (1) + +let sumWithPrev = + new Derived ( + DerivedOptions ( + (fun props -> + let prevDep = + props.prevDepVals + |> Option.map (fun arr -> unbox arr[0]) + |> Option.defaultValue 0 + + let currDep = unbox props.currDepVals[0] + + prevDep + + currDep), + [| count |] + ) + ) + +let unmount = sumWithPrev.mount () diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedPrevVal/DerivedPrevVal.expected b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedPrevVal/DerivedPrevVal.expected new file mode 100644 index 0000000..b0d88a7 --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedPrevVal/DerivedPrevVal.expected @@ -0,0 +1,16 @@ + +import { Derived, Store } from "@tanstack/solid-store"; +import { defaultArg } from "../../fable_modules/fable-library-js.5.0.0-alpha.14/Option.js"; + +export const count = new Store(1); + +export const runningTotal = new Derived({ + fn: (props) => { + const prev = defaultArg(props.prevVal, 0) | 0; + return (count.state + prev) | 0; + }, + deps: [count], +}); + +export const unmount = runningTotal.mount(); + diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedPrevVal/DerivedPrevVal.fs b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedPrevVal/DerivedPrevVal.fs new file mode 100644 index 0000000..e4f3830 --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/DerivedPrevVal/DerivedPrevVal.fs @@ -0,0 +1,21 @@ +module DerivedPrevVal + +open Partas.Solid.TanStack.Store + +let count = new Store (1) + +let runningTotal = + new Derived ( + DerivedOptions ( + (fun props -> + let prev = + props.prevVal + |> Option.defaultValue 0 + + count.state + + prev), + [| count |] + ) + ) + +let unmount = runningTotal.mount () diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/EffectEager/EffectEager.expected b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/EffectEager/EffectEager.expected new file mode 100644 index 0000000..4925a1c --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/EffectEager/EffectEager.expected @@ -0,0 +1,19 @@ + +import { Effect, Store } from "@tanstack/solid-store"; +import { some } from "../../fable_modules/fable-library-js.5.0.0-alpha.14/Option.js"; + +export const store = new Store(0); + +export const eagerEffect = (() => { + const opts = { + fn: () => { + console.log(some("Immediate effect:"), store.state); + }, + deps: [store], + }; + opts.eager = true; + return new Effect(opts); +})(); + +export const unmount = eagerEffect.mount(); + diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/EffectEager/EffectEager.fs b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/EffectEager/EffectEager.fs new file mode 100644 index 0000000..6765f50 --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/EffectEager/EffectEager.fs @@ -0,0 +1,15 @@ +module EffectEager + +open Partas.Solid.TanStack.Store +open Fable.Core.JS + +let store = new Store (0) + +let eagerEffect = + let opts = + EffectOptions ((fun () -> console.log ("Immediate effect:", store.state)), [| store |]) + + opts.eager <- true + new Effect (opts) + +let unmount = eagerEffect.mount () diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/MountUnmount/MountUnmount.expected b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/MountUnmount/MountUnmount.expected new file mode 100644 index 0000000..d0958d2 --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/MountUnmount/MountUnmount.expected @@ -0,0 +1,32 @@ + +import { Effect, Derived, Store } from "@tanstack/solid-store"; +import { some } from "../../fable_modules/fable-library-js.5.0.0-alpha.14/Option.js"; +import { onCleanup } from "solid-js"; +import { int32ToString } from "../../fable_modules/fable-library-js.5.0.0-alpha.14/Util.js"; + +export const store = new Store(0); + +export const derived = new Derived({ + fn: (_arg) => ((store.state * 2) | 0), + deps: [store], +}); + +export const effect = new Effect({ + fn: () => { + console.log(some(store.state)); + }, + deps: [store], +}); + +export function Component() { + const derivedUnmount = derived.mount(); + const effectUnmount = effect.mount(); + onCleanup(() => { + derivedUnmount(); + effectUnmount(); + }); + return
+ {int32ToString(derived.state)} +
; +} + diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/MountUnmount/MountUnmount.fs b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/MountUnmount/MountUnmount.fs new file mode 100644 index 0000000..8d61d67 --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/MountUnmount/MountUnmount.fs @@ -0,0 +1,31 @@ +module MountUnmount + +open Partas.Solid +open Partas.Solid.TanStack.Store +open Fable.Core.JS + +let store = new Store (0) + +let derived = + new Derived ( + DerivedOptions ( + (fun _ -> + store.state + * 2), + [| store |] + ) + ) + +let effect = + new Effect (EffectOptions ((fun () -> console.log (store.state)), [| store |])) + +[] +let Component () = + let derivedUnmount = derived.mount () + let effectUnmount = effect.mount () + + onCleanup (fun () -> + derivedUnmount () + effectUnmount ()) + + div () { string derived.state } diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/StoreOptions/StoreOptions.expected b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/StoreOptions/StoreOptions.expected new file mode 100644 index 0000000..d388956 --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/StoreOptions/StoreOptions.expected @@ -0,0 +1,14 @@ + +import { Store } from "@tanstack/solid-store"; +import { some } from "../../fable_modules/fable-library-js.5.0.0-alpha.14/Option.js"; + +export const store = new Store(12, { + updateFn: (prev, updater) => ((updater(prev) + prev) | 0), +}); + +export const storeWithOnUpdate = new Store(0, { + onUpdate: () => { + console.log(some("Updated!")); + }, +}); + diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/StoreOptions/StoreOptions.fs b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/StoreOptions/StoreOptions.fs new file mode 100644 index 0000000..d037ae0 --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/StoreOptions/StoreOptions.fs @@ -0,0 +1,17 @@ +module StoreOptions + +open Partas.Solid.TanStack.Store +open Fable.Core.JS +open Fable.Core.JsInterop + +let store = + new Store ( + 12, + !!{| updateFn = + fun prev updater -> + updater prev + + prev |} + ) + +let storeWithOnUpdate = + new Store (0, !!{| onUpdate = fun () -> console.log ("Updated!") |}) diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/Subscribe/Subscribe.expected b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/Subscribe/Subscribe.expected new file mode 100644 index 0000000..7814d15 --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/Subscribe/Subscribe.expected @@ -0,0 +1,14 @@ + +import { Store } from "@tanstack/solid-store"; +import { some } from "../../fable_modules/fable-library-js.5.0.0-alpha.14/Option.js"; + +export const store = new Store(0); + +export const unsub = store.subscribe(() => { + console.log(some("State changed:"), store.state); +}); + +export function cleanup() { + unsub(); +} + diff --git a/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/Subscribe/Subscribe.fs b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/Subscribe/Subscribe.fs new file mode 100644 index 0000000..f1ffd91 --- /dev/null +++ b/Partas.Solid.Tests.Plugin/Compiled/TanStackStoreCases/Subscribe/Subscribe.fs @@ -0,0 +1,10 @@ +module Subscribe + +open Partas.Solid.TanStack.Store +open Fable.Core.JS + +let store = new Store (0) + +let unsub = store.subscribe (fun () -> console.log ("State changed:", store.state)) + +let cleanup () = unsub () diff --git a/Partas.Solid.Tests.Plugin/Tests.fs b/Partas.Solid.Tests.Plugin/Tests.fs index 0c73858..27c20db 100644 --- a/Partas.Solid.Tests.Plugin/Tests.fs +++ b/Partas.Solid.Tests.Plugin/Tests.fs @@ -130,4 +130,18 @@ let TanStackStoreCases = "DerivedStore" |> runTanStackStoreCase "Derived store" "EffectStore" - |> runTanStackStoreCase "Effect store" ] + |> runTanStackStoreCase "Effect store" + "Subscribe" + |> runTanStackStoreCase "Store subscribe/unsubscribe" + "StoreOptions" + |> runTanStackStoreCase "Store with updateFn and onUpdate options" + "BatchUpdates" + |> runTanStackStoreCase "Batch updates" + "DerivedPrevVal" + |> runTanStackStoreCase "Derived with prevVal" + "DerivedDepVals" + |> runTanStackStoreCase "Derived with prevDepVals/currDepVals" + "EffectEager" + |> runTanStackStoreCase "Effect with eager option" + "MountUnmount" + |> runTanStackStoreCase "Mount/unmount with cleanup" ]