diff --git a/src/_parent.luau b/src/_parent.luau new file mode 100644 index 0000000..8f95b17 --- /dev/null +++ b/src/_parent.luau @@ -0,0 +1,26 @@ +if not game then script = require "test/relative-string" end + +local graph = require(script.Parent.graph) +type Node = graph.Node + +export type Source = (() -> T) & ((value: T) -> T) + +local function parent(fn: () -> T): T + local node = graph.get_scope() + if not node then error("") end + + local parent_node = node.owner + if not parent_node then error("") end + + graph.push_scope(parent_node :: Node) + + local ok, result = pcall(fn) + + graph.pop_scope() + + if not ok then error(result, 0) end + + return result +end + +return parent diff --git a/src/context.luau b/src/context.luau index 6c571d0..d9e6ef0 100644 --- a/src/context.luau +++ b/src/context.luau @@ -9,7 +9,7 @@ local push_scope = graph.push_scope local pop_scope = graph.pop_scope local set_context = graph.set_context -export type Context = (() -> T) & ((T, () -> U) -> U) +export type Context = (() -> T) & ((T, () -> U) -> U) & ((T, () -> ()) -> ()) local nil_symbol = newproxy() local count = 0 diff --git a/src/init.luau b/src/init.luau index 3bcc7ad..1a91ffb 100644 --- a/src/init.luau +++ b/src/init.luau @@ -15,6 +15,7 @@ local effect = require(script.effect) local derive = require(script.derive) local cleanup = require(script.cleanup) local untrack = require(script.untrack) +local parent = require(script._parent) local read = require(script.read) local batch = require(script.batch) local context = require(script.context) @@ -68,6 +69,7 @@ local vide = { -- util cleanup = cleanup, untrack = untrack, + parent = parent, read = read, batch = batch, context = context, diff --git a/test/tests.luau b/test/tests.luau index 45aa41f..be1c65a 100644 --- a/test/tests.luau +++ b/test/tests.luau @@ -1736,6 +1736,95 @@ TEST("untrack()", wrap_root(function() end end)) +TEST("parent()", function() + local root = vide.root + local source = vide.source + local effect = vide.effect + local parent = vide.parent + local context = vide.context + local cleanup = vide.cleanup + local untrack = vide.untrack + + local ctx = context(0) + + do CASE "respects contexts" + root(function() + ctx(1, function() + CHECK(parent(function() + return ctx() + end) == 0) + + ctx(2, function() + CHECK(parent(function() + return ctx() + end) == 1) + CHECK(ctx() == 2) + + effect(function() + ctx(3, function() + CHECK(parent(function() + return ctx() + end) == 2) + CHECK(ctx() == 3) + end) + end) + end) + end) + end) + end + + do CASE "proto indexes()" + local src = source(0) + local val = source(0) + + local component_cleanup_count = 0 + local component_effect_cleanup_count = 0 + + local destroy = root(function() + local function component() + CHECK(ctx() == 1) + + cleanup(function() + component_cleanup_count += 1 + end) + + effect(function() + val() + + cleanup(function() + component_effect_cleanup_count += 1 + end) + end) + + return nil + end + + ctx(1, function() + effect(function() + src() + parent(function() + untrack(component) + + return nil + end) + end) + end) + end) + + src(1) + CHECK(component_cleanup_count == 0) + CHECK(component_effect_cleanup_count == 0) + + val(2) + CHECK(component_cleanup_count == 1) -- todo: a way to independently destroy child scopes + CHECK(component_effect_cleanup_count == 2) + + destroy() + CHECK(component_cleanup_count == 2) + CHECK(component_effect_cleanup_count == 3) + end +end) + TEST("events", function() local create = vide.create