mirror of
https://github.com/littensy/charm.git
synced 2026-10-23 05:46:27 +00:00
refactor: code quality
This commit is contained in:
parent
f0a7938703
commit
8ca299f671
16 changed files with 82 additions and 101 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
local atom = require(script.Parent.Parent.atom)
|
local atom = require(script.Parent.Parent.atom)
|
||||||
local batch = require(script.Parent.Parent.batch)
|
|
||||||
local effect = require(script.Parent.Parent.effect)
|
local effect = require(script.Parent.Parent.effect)
|
||||||
|
local store = require(script.Parent.Parent.store)
|
||||||
local subscribe = require(script.Parent.Parent.subscribe)
|
local subscribe = require(script.Parent.Parent.subscribe)
|
||||||
|
|
||||||
return function()
|
return function()
|
||||||
|
|
@ -10,7 +10,7 @@ return function()
|
||||||
subscribe(source, function()
|
subscribe(source, function()
|
||||||
value += 1
|
value += 1
|
||||||
end)
|
end)
|
||||||
batch(function()
|
store.batch(function()
|
||||||
source(2)
|
source(2)
|
||||||
source(3)
|
source(3)
|
||||||
source(4)
|
source(4)
|
||||||
|
|
@ -28,7 +28,7 @@ return function()
|
||||||
b()
|
b()
|
||||||
end)
|
end)
|
||||||
value = 0 -- effect() will run once initially
|
value = 0 -- effect() will run once initially
|
||||||
batch(function()
|
store.batch(function()
|
||||||
a(2)
|
a(2)
|
||||||
b(2)
|
b(2)
|
||||||
a(3)
|
a(3)
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
local atom = require(script.Parent.Parent.atom)
|
local atom = require(script.Parent.Parent.atom)
|
||||||
local capture = require(script.Parent.Parent.capture)
|
local store = require(script.Parent.Parent.store)
|
||||||
|
|
||||||
return function()
|
return function()
|
||||||
it("captures atoms from a function", function()
|
it("captures atoms from a function", function()
|
||||||
local a, b, c = atom(1), atom(1), atom(2)
|
local a, b, c = atom(1), atom(1), atom(2)
|
||||||
local captured, result = capture(function()
|
local captured, result = store.capture(function()
|
||||||
return a() + b()
|
return a() + b()
|
||||||
end)
|
end)
|
||||||
expect(captured[a]).to.be.ok()
|
expect(captured[a]).to.be.ok()
|
||||||
|
|
@ -15,7 +15,7 @@ return function()
|
||||||
|
|
||||||
it("captures an atom by itself", function()
|
it("captures an atom by itself", function()
|
||||||
local a = atom(1)
|
local a = atom(1)
|
||||||
local captured, result = capture(a)
|
local captured, result = store.capture(a)
|
||||||
expect(captured[a]).to.be.ok()
|
expect(captured[a]).to.be.ok()
|
||||||
expect(result).to.equal(1)
|
expect(result).to.equal(1)
|
||||||
end)
|
end)
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ return function()
|
||||||
|
|
||||||
local function listenersDecreased()
|
local function listenersDecreased()
|
||||||
local count = 0
|
local count = 0
|
||||||
for _ in pairs(store.listeners[source]) do
|
for _ in next, store.listeners[source] do
|
||||||
count += 1
|
count += 1
|
||||||
end
|
end
|
||||||
max = math.max(max, count)
|
max = math.max(max, count)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ local validate = require(script.Parent.Parent.Parent.sync.validate)
|
||||||
|
|
||||||
return function()
|
return function()
|
||||||
it("validates inputs", function()
|
it("validates inputs", function()
|
||||||
local cases = {
|
local cases: { [string]: { value: any, expected: any } } = {
|
||||||
array = { value = { 1, 2, 3 }, expected = true },
|
array = { value = { 1, 2, 3 }, expected = true },
|
||||||
array_sparse = { value = { 1, nil :: any, 3 }, expected = true },
|
array_sparse = { value = { 1, nil :: any, 3 }, expected = true },
|
||||||
array_sparse_invalid = { value = { [1] = 0, [3] = 0 }, expected = false },
|
array_sparse_invalid = { value = { [1] = 0, [3] = 0 }, expected = false },
|
||||||
|
|
@ -15,7 +15,7 @@ return function()
|
||||||
number_nan = { value = 0 / 0, expected = false },
|
number_nan = { value = 0 / 0, expected = false },
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, case in pairs(cases) do
|
for key, case in cases do
|
||||||
local success, result = pcall(function()
|
local success, result = pcall(function()
|
||||||
validate(case.value, key)
|
validate(case.value, key)
|
||||||
end)
|
end)
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
local atom = require(script.Parent.Parent.atom)
|
local atom = require(script.Parent.Parent.atom)
|
||||||
local capture = require(script.Parent.Parent.capture)
|
local store = require(script.Parent.Parent.store)
|
||||||
local unwrap = require(script.Parent.Parent.unwrap)
|
local unwrap = require(script.Parent.Parent.unwrap)
|
||||||
|
|
||||||
return function()
|
return function()
|
||||||
it("calls an atom without capturing it", function()
|
it("calls an atom without capturing it", function()
|
||||||
local a, b, c = atom(1), atom(1), atom(1)
|
local a, b, c = atom(1), atom(1), atom(1)
|
||||||
local captured, result = capture(function()
|
local captured, result = store.capture(function()
|
||||||
return a() + unwrap(b) + c()
|
return a() + unwrap(b) + c()
|
||||||
end)
|
end)
|
||||||
expect(captured[a]).to.be.ok()
|
expect(captured[a]).to.be.ok()
|
||||||
|
|
|
||||||
|
|
@ -21,14 +21,7 @@ local function atom<T>(state: T, options: AtomOptions<T>?): Atom<T>
|
||||||
|
|
||||||
if state ~= nextState and not (equals and equals(state, nextState)) then
|
if state ~= nextState and not (equals and equals(state, nextState)) then
|
||||||
state = nextState
|
state = nextState
|
||||||
|
store.notify(atom)
|
||||||
if store.isBatching() then
|
|
||||||
store.batched[atom] = true
|
|
||||||
else
|
|
||||||
for listener in pairs(table.clone(store.listeners[atom])) do
|
|
||||||
listener()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return state
|
return state
|
||||||
|
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
local store = require(script.Parent.store)
|
|
||||||
|
|
||||||
local function batch(callback: () -> ())
|
|
||||||
if store.isBatching() then
|
|
||||||
return callback()
|
|
||||||
end
|
|
||||||
|
|
||||||
local visited: { [() -> ()]: true } = {}
|
|
||||||
|
|
||||||
store.setBatching(true)
|
|
||||||
callback()
|
|
||||||
store.setBatching(false)
|
|
||||||
|
|
||||||
for atom in store.batched do
|
|
||||||
for listener in pairs(store.listeners[atom]) do
|
|
||||||
if not visited[listener] then
|
|
||||||
visited[listener] = true
|
|
||||||
listener()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
table.clear(store.batched)
|
|
||||||
end
|
|
||||||
|
|
||||||
return batch
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
local store = require(script.Parent.store)
|
|
||||||
local types = require(script.Parent.types)
|
|
||||||
type Atom<T> = types.Atom<T>
|
|
||||||
|
|
||||||
local function capture<T>(callback: () -> T): ({ [Atom<any>]: true }, T)
|
|
||||||
if store.listeners[callback] then
|
|
||||||
return { [callback] = true }, callback()
|
|
||||||
end
|
|
||||||
|
|
||||||
store.setCapturing(true)
|
|
||||||
local result = callback()
|
|
||||||
store.setCapturing(false)
|
|
||||||
|
|
||||||
local dependencies = table.clone(store.captured)
|
|
||||||
table.clear(store.captured)
|
|
||||||
|
|
||||||
return dependencies, result
|
|
||||||
end
|
|
||||||
|
|
||||||
return capture
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
local atom = require(script.Parent.atom)
|
local atom = require(script.Parent.atom)
|
||||||
local capture = require(script.Parent.capture)
|
|
||||||
local store = require(script.Parent.store)
|
local store = require(script.Parent.store)
|
||||||
local types = require(script.Parent.types)
|
local types = require(script.Parent.types)
|
||||||
type Atom<T> = types.Atom<T>
|
type Atom<T> = types.Atom<T>
|
||||||
|
|
@ -7,7 +6,7 @@ type AtomOptions<T> = types.AtomOptions<T>
|
||||||
type Molecule<T> = types.Molecule<T>
|
type Molecule<T> = types.Molecule<T>
|
||||||
|
|
||||||
local function derive<T>(molecule: Molecule<T>, options: AtomOptions<T>?): Molecule<T>
|
local function derive<T>(molecule: Molecule<T>, options: AtomOptions<T>?): Molecule<T>
|
||||||
local atoms, state = capture(molecule)
|
local atoms, state = store.capture(molecule)
|
||||||
local derived = atom(state, options)
|
local derived = atom(state, options)
|
||||||
local derivedRef = setmetatable({ current = derived }, { __mode = "v" })
|
local derivedRef = setmetatable({ current = derived }, { __mode = "v" })
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
local capture = require(script.Parent.capture)
|
|
||||||
local store = require(script.Parent.store)
|
local store = require(script.Parent.store)
|
||||||
|
|
||||||
local function effect(molecule: () -> nil): () -> ()
|
local function effect(molecule: () -> nil): () -> ()
|
||||||
local atoms = capture(molecule)
|
local atoms = store.capture(molecule)
|
||||||
|
|
||||||
local function listener()
|
local function listener()
|
||||||
molecule()
|
molecule()
|
||||||
|
|
|
||||||
8
src/index.d.ts
vendored
8
src/index.d.ts
vendored
|
|
@ -22,8 +22,6 @@ declare namespace Charm {
|
||||||
|
|
||||||
function atom<State>(state: State, options?: AtomOptions<State>): Atom<State>;
|
function atom<State>(state: State, options?: AtomOptions<State>): Atom<State>;
|
||||||
|
|
||||||
function isAtom(value: unknown): value is Atom<any>;
|
|
||||||
|
|
||||||
function derive<State>(molecule: Molecule<State>, options?: AtomOptions<State>): Molecule<State>;
|
function derive<State>(molecule: Molecule<State>, options?: AtomOptions<State>): Molecule<State>;
|
||||||
|
|
||||||
function subscribe<State>(molecule: Molecule<State>, callback: (state: State, prev: State) => void): Cleanup;
|
function subscribe<State>(molecule: Molecule<State>, callback: (state: State, prev: State) => void): Cleanup;
|
||||||
|
|
@ -32,10 +30,14 @@ declare namespace Charm {
|
||||||
|
|
||||||
function unwrap<State, Args extends unknown[]>(molecule: State | ((...args: Args) => State), ...args: Args): State;
|
function unwrap<State, Args extends unknown[]>(molecule: State | ((...args: Args) => State), ...args: Args): State;
|
||||||
|
|
||||||
function capture<State>(molecule: Molecule<State>): LuaTuple<[captured: Set<Atom<unknown>>, state: State]>;
|
function isAtom(value: unknown): value is Atom<any>;
|
||||||
|
|
||||||
function batch(callback: () => void): void;
|
function batch(callback: () => void): void;
|
||||||
|
|
||||||
|
function capture<State>(molecule: Molecule<State>): LuaTuple<[captured: Set<Atom<unknown>>, state: State]>;
|
||||||
|
|
||||||
|
function notify(atom: Atom<unknown>): void;
|
||||||
|
|
||||||
function observe<Item>(
|
function observe<Item>(
|
||||||
molecule: Molecule<readonly Item[]>,
|
molecule: Molecule<readonly Item[]>,
|
||||||
factory: (item: Item, index: number) => Cleanup | void,
|
factory: (item: Item, index: number) => Cleanup | void,
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,14 @@
|
||||||
_G[script.modules.Promise] = _G[script]
|
_G[script.modules.Promise] = _G[script]
|
||||||
|
|
||||||
local atom = require(script.atom)
|
local atom = require(script.atom)
|
||||||
local batch = require(script.batch)
|
|
||||||
local capture = require(script.capture)
|
|
||||||
local derive = require(script.derive)
|
local derive = require(script.derive)
|
||||||
local effect = require(script.effect)
|
local effect = require(script.effect)
|
||||||
local isAtom = require(script.isAtom)
|
|
||||||
local map = require(script.map)
|
local map = require(script.map)
|
||||||
local observe = require(script.observe)
|
local observe = require(script.observe)
|
||||||
local useAtom = require(script.react.useAtom)
|
local useAtom = require(script.react.useAtom)
|
||||||
local useAtomState = require(script.react.useAtomState)
|
local useAtomState = require(script.react.useAtomState)
|
||||||
local useSetAtom = require(script.react.useSetAtom)
|
local useSetAtom = require(script.react.useSetAtom)
|
||||||
|
local store = require(script.store)
|
||||||
local subscribe = require(script.subscribe)
|
local subscribe = require(script.subscribe)
|
||||||
local sync = require(script.sync)
|
local sync = require(script.sync)
|
||||||
local types = require(script.types)
|
local types = require(script.types)
|
||||||
|
|
@ -22,11 +20,12 @@ export type SyncPayload = types.SyncPayload
|
||||||
|
|
||||||
return {
|
return {
|
||||||
atom = atom,
|
atom = atom,
|
||||||
batch = batch,
|
batch = store.batch,
|
||||||
capture = capture,
|
capture = store.capture,
|
||||||
|
notify = store.notify,
|
||||||
|
isAtom = store.isAtom,
|
||||||
derive = derive,
|
derive = derive,
|
||||||
effect = effect,
|
effect = effect,
|
||||||
isAtom = isAtom,
|
|
||||||
map = map,
|
map = map,
|
||||||
observe = observe,
|
observe = observe,
|
||||||
useAtom = useAtom,
|
useAtom = useAtom,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
local store = require(script.Parent.store)
|
|
||||||
|
|
||||||
local function isAtom(value: any)
|
|
||||||
return value ~= nil and store.listeners[value] :: any ~= nil
|
|
||||||
end
|
|
||||||
|
|
||||||
return isAtom
|
|
||||||
|
|
@ -3,11 +3,61 @@ type Atom<T> = types.Atom<T>
|
||||||
type WeakMap<K, V> = types.WeakMap<K, V>
|
type WeakMap<K, V> = types.WeakMap<K, V>
|
||||||
|
|
||||||
local listeners: WeakMap<Atom<any>, WeakMap<() -> (), unknown>> = setmetatable({}, { __mode = "k" })
|
local listeners: WeakMap<Atom<any>, WeakMap<() -> (), unknown>> = setmetatable({}, { __mode = "k" })
|
||||||
|
|
||||||
local captured: { [Atom<any>]: true } = {}
|
local captured: { [Atom<any>]: true } = {}
|
||||||
local batched: { [Atom<any>]: true } = {}
|
|
||||||
local capturing = false
|
local capturing = false
|
||||||
|
|
||||||
|
local batched: { [() -> ()]: true } = {}
|
||||||
local batching = false
|
local batching = false
|
||||||
|
|
||||||
|
local function isAtom(value: any)
|
||||||
|
return not not (value and listeners[value])
|
||||||
|
end
|
||||||
|
|
||||||
|
local function notify(atom: Atom<any>)
|
||||||
|
if batching then
|
||||||
|
for listener in next, listeners[atom] do
|
||||||
|
batched[listener] = true
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for listener in next, table.clone(listeners[atom]) do
|
||||||
|
listener()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function capture<T>(callback: () -> T): ({ [Atom<any>]: true }, T)
|
||||||
|
if listeners[callback] then
|
||||||
|
return { [callback] = true }, callback()
|
||||||
|
end
|
||||||
|
|
||||||
|
capturing = true
|
||||||
|
local result = callback()
|
||||||
|
capturing = false
|
||||||
|
|
||||||
|
local dependencies = table.clone(captured)
|
||||||
|
table.clear(captured)
|
||||||
|
|
||||||
|
return dependencies, result
|
||||||
|
end
|
||||||
|
|
||||||
|
local function batch(callback: () -> ())
|
||||||
|
if batching then
|
||||||
|
return callback()
|
||||||
|
end
|
||||||
|
|
||||||
|
batching = true
|
||||||
|
callback()
|
||||||
|
batching = false
|
||||||
|
|
||||||
|
for listener in batched do
|
||||||
|
listener()
|
||||||
|
end
|
||||||
|
|
||||||
|
table.clear(batched)
|
||||||
|
end
|
||||||
|
|
||||||
local function setCapturing(value: boolean)
|
local function setCapturing(value: boolean)
|
||||||
capturing = value
|
capturing = value
|
||||||
end
|
end
|
||||||
|
|
@ -16,20 +66,13 @@ local function isCapturing()
|
||||||
return capturing
|
return capturing
|
||||||
end
|
end
|
||||||
|
|
||||||
local function setBatching(value: boolean)
|
|
||||||
batching = value
|
|
||||||
end
|
|
||||||
|
|
||||||
local function isBatching()
|
|
||||||
return batching
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
listeners = listeners,
|
listeners = listeners,
|
||||||
captured = captured,
|
captured = captured,
|
||||||
batched = batched,
|
isAtom = isAtom,
|
||||||
|
notify = notify,
|
||||||
|
capture = capture,
|
||||||
|
batch = batch,
|
||||||
setCapturing = setCapturing,
|
setCapturing = setCapturing,
|
||||||
isCapturing = isCapturing,
|
isCapturing = isCapturing,
|
||||||
setBatching = setBatching,
|
|
||||||
isBatching = isBatching,
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
local capture = require(script.Parent.capture)
|
|
||||||
local store = require(script.Parent.store)
|
local store = require(script.Parent.store)
|
||||||
local types = require(script.Parent.types)
|
local types = require(script.Parent.types)
|
||||||
type Molecule<T> = types.Molecule<T>
|
type Molecule<T> = types.Molecule<T>
|
||||||
|
|
||||||
local function subscribe<T>(molecule: Molecule<T>, callback: (state: T, prev: T) -> ()): () -> ()
|
local function subscribe<T>(molecule: Molecule<T>, callback: (state: T, prev: T) -> ()): () -> ()
|
||||||
local atoms, state = capture(molecule)
|
local atoms, state = store.capture(molecule)
|
||||||
|
|
||||||
local function listener()
|
local function listener()
|
||||||
local prevState = state
|
local prevState = state
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
local isAtom = require(script.Parent.Parent.isAtom)
|
local store = require(script.Parent.Parent.store)
|
||||||
|
|
||||||
local SAFE_KEYS = { string = true, number = true }
|
local SAFE_KEYS = { string = true, number = true }
|
||||||
local SAFE_VALUES = { string = true, number = true, boolean = true, table = true }
|
local SAFE_VALUES = { string = true, number = true, boolean = true, table = true }
|
||||||
|
|
@ -40,7 +40,7 @@ local function validate(value: any, key: any)
|
||||||
elseif typeOfValue == "table" then
|
elseif typeOfValue == "table" then
|
||||||
if getmetatable(value) ~= nil then
|
if getmetatable(value) ~= nil then
|
||||||
error(`Cannot sync tables with metatables! Got {value} at key '{key}'`)
|
error(`Cannot sync tables with metatables! Got {value} at key '{key}'`)
|
||||||
elseif isAtom(value) then
|
elseif store.isAtom(value) then
|
||||||
error(`Cannot sync nested atoms! Got atom '{value.debugLabel}' at key '{key}'`)
|
error(`Cannot sync nested atoms! Got atom '{value.debugLabel}' at key '{key}'`)
|
||||||
elseif isUnsafeTable(value) then
|
elseif isUnsafeTable(value) then
|
||||||
error(
|
error(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue