mirror of
https://github.com/centau/vide.git
synced 2026-08-20 14:41:37 +00:00
Add flag to disable deferral of nested properties
This commit is contained in:
parent
ccaeb030f3
commit
4c639f8388
4 changed files with 95 additions and 68 deletions
|
|
@ -13,11 +13,11 @@ type Array<V> = { V }
|
||||||
type ArrayOrV<V> = {ArrayOrV<V>} | V
|
type ArrayOrV<V> = {ArrayOrV<V>} | V
|
||||||
type Map<K, V> = { [K]: V }
|
type Map<K, V> = { [K]: V }
|
||||||
|
|
||||||
local free_caches: {
|
type Cache = {
|
||||||
-- event listeners to connect after properties are set
|
-- event listeners to connect after properties are set
|
||||||
events: Map<
|
events: Array<
|
||||||
string, -- event name
|
| string -- 1. event name
|
||||||
() -> () -- listener
|
| () -> () -- 2. listener
|
||||||
>,
|
>,
|
||||||
|
|
||||||
-- actions to run after events are connected
|
-- actions to run after events are connected
|
||||||
|
|
@ -32,18 +32,18 @@ local free_caches: {
|
||||||
Map<string, true> -- set of property names
|
Map<string, true> -- set of property names
|
||||||
>,
|
>,
|
||||||
|
|
||||||
-- use stack instead of recursive function to process nesting layers one at time
|
|
||||||
-- deeper-nested properties take precedence over shallower-nested ones
|
|
||||||
-- each nested layer occupies two indexes: 1. table ref 2. nested depth
|
-- each nested layer occupies two indexes: 1. table ref 2. nested depth
|
||||||
-- e.g. { t1 = { t3 = {} }, t2 = {} } -> { t1, 1, t2, 1, t3, 2 }
|
-- e.g. { t1 = { t3 = {} }, t2 = {} } -> { t1, 1, t2, 1, t3, 2 }
|
||||||
nested_stack: { {} | number }
|
nested_stack: { {} | number }
|
||||||
}?
|
}
|
||||||
|
|
||||||
local function borrow_caches(): typeof(assert(free_caches))
|
local free_cache: Cache?
|
||||||
if free_caches then
|
|
||||||
local caches = free_caches :: typeof(assert(free_caches))
|
local function borrow_cache(): Cache
|
||||||
free_caches = nil
|
if free_cache then
|
||||||
return caches
|
local cache = free_cache
|
||||||
|
free_cache = nil
|
||||||
|
return cache
|
||||||
else
|
else
|
||||||
return {
|
return {
|
||||||
events = {},
|
events = {},
|
||||||
|
|
@ -58,46 +58,28 @@ local function borrow_caches(): typeof(assert(free_caches))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function return_caches(caches: typeof(free_caches) )
|
local function return_cache(cache: Cache )
|
||||||
free_caches = caches
|
free_cache = cache
|
||||||
end
|
end
|
||||||
|
|
||||||
-- applies table of nested properties to an instance using full vide semantics
|
local function process_properties(properties: Map<unknown, unknown>, instance: Instance, cache: Cache, depth: number)
|
||||||
local function apply<T>(instance: T & Instance, properties: { [unknown]: unknown }): T
|
|
||||||
if not properties then
|
|
||||||
throw("attempt to call a constructor returned by create() with no properties")
|
|
||||||
end
|
|
||||||
|
|
||||||
local strict = flags.strict
|
|
||||||
|
|
||||||
-- queue parent assignment if any for last
|
|
||||||
local parent: unknown = properties.Parent
|
|
||||||
|
|
||||||
local caches = borrow_caches()
|
|
||||||
local events = caches.events
|
|
||||||
local actions = caches.actions
|
|
||||||
local nested_debug = caches.nested_debug
|
|
||||||
local nested_stack = caches.nested_stack
|
|
||||||
|
|
||||||
-- process all properties
|
|
||||||
local depth = 1
|
|
||||||
repeat
|
|
||||||
for property, value in properties do
|
for property, value in properties do
|
||||||
if property == "Parent" then continue end
|
if property == "Parent" then continue end
|
||||||
|
|
||||||
if type(property) == "string" then
|
if type(property) == "string" then
|
||||||
if strict then -- check for duplicate prop assignment at nesting depth
|
if flags.strict then -- check for duplicate property assignment at nesting depth
|
||||||
if nested_debug[depth][property] then
|
if cache.nested_debug[depth][property] then
|
||||||
throw(`duplicate property {property} at depth {depth}`)
|
throw(`duplicate property {property} at depth {depth}`)
|
||||||
end
|
end
|
||||||
nested_debug[depth][property] = true
|
cache.nested_debug[depth][property] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if type(value) == "function" then
|
if type(value) == "function" then
|
||||||
if typeof((instance :: any)[property]) == "RBXScriptSignal" then
|
if typeof((instance :: any)[property]) == "RBXScriptSignal" then
|
||||||
events[property] = value :: () -> () -- add event to buffer
|
table.insert(cache.events, property) -- add event name to buffer
|
||||||
|
table.insert(cache.events, value :: () -> ()) -- add event listener to buffer
|
||||||
else
|
else
|
||||||
bind.property(instance, property, value :: () -> ()) -- bind property
|
bind.property(instance, property, value :: () -> ()) -- create implicit effect for property
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
(instance :: any)[property] = value -- set property
|
(instance :: any)[property] = value -- set property
|
||||||
|
|
@ -107,24 +89,47 @@ local function apply<T>(instance: T & Instance, properties: { [unknown]: unknown
|
||||||
bind.children(instance, value :: () -> ArrayOrV<Instance>) -- bind children
|
bind.children(instance, value :: () -> ArrayOrV<Instance>) -- bind children
|
||||||
elseif type(value) == "table" then
|
elseif type(value) == "table" then
|
||||||
if is_action(value) then
|
if is_action(value) then
|
||||||
table.insert(actions[(value :: any).priority], (value :: any).callback :: () -> ()) -- add action to buffer
|
table.insert(cache.actions[(value :: any).priority], (value :: any).callback :: () -> ()) -- add action to buffer
|
||||||
|
elseif flags.defer_nested_properties then
|
||||||
|
table.insert(cache.nested_stack, value :: {})
|
||||||
|
table.insert(cache.nested_stack, depth + 1) -- push table to stack for later processing
|
||||||
else
|
else
|
||||||
table.insert(nested_stack, value :: {})
|
process_properties(value :: Map<unknown, unknown>, instance, cache, depth + 1)
|
||||||
table.insert(nested_stack, depth + 1) -- push table to stack for later processing
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
(value :: Instance).Parent = instance -- parent child
|
(value :: Instance).Parent = instance -- parent child
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- applies table of nested properties to an instance using full vide semantics
|
||||||
|
local function apply<T>(instance: T & Instance, properties: { [unknown]: unknown }): T
|
||||||
|
if not properties then
|
||||||
|
throw("attempt to call a constructor returned by create() with no properties")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- queue parent assignment if any for last
|
||||||
|
local parent: unknown = properties.Parent
|
||||||
|
|
||||||
|
local caches = borrow_cache()
|
||||||
|
local events = caches.events
|
||||||
|
local actions = caches.actions
|
||||||
|
local nested_debug = caches.nested_debug
|
||||||
|
local nested_stack = caches.nested_stack
|
||||||
|
|
||||||
|
-- process all properties
|
||||||
|
local depth = 1
|
||||||
|
repeat
|
||||||
|
process_properties(properties, instance, caches, depth)
|
||||||
depth = table.remove(nested_stack) :: number
|
depth = table.remove(nested_stack) :: number
|
||||||
properties = table.remove(nested_stack) :: {}
|
properties = table.remove(nested_stack) :: {}
|
||||||
|
|
||||||
until not properties
|
until not properties
|
||||||
|
|
||||||
for event, listener in next, events do
|
for i = 1, #events, 2 do
|
||||||
(instance :: any)[event]:Connect(listener)
|
local event_name = events[i]
|
||||||
|
local event_listener = events[i + 1]
|
||||||
|
;(instance :: any)[event_name]:Connect(event_listener)
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, queued in next, actions do
|
for _, queued in next, actions do
|
||||||
|
|
@ -145,10 +150,10 @@ local function apply<T>(instance: T & Instance, properties: { [unknown]: unknown
|
||||||
-- clear caches
|
-- clear caches
|
||||||
table.clear(events)
|
table.clear(events)
|
||||||
for _, queued in next, actions do table.clear(queued) end
|
for _, queued in next, actions do table.clear(queued) end
|
||||||
if strict then table.clear(nested_debug) end
|
if flags.strict then table.clear(nested_debug) end
|
||||||
table.clear(nested_stack)
|
table.clear(nested_stack)
|
||||||
|
|
||||||
return_caches(caches)
|
return_cache(caches)
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,8 @@ end
|
||||||
|
|
||||||
local is_O2 = inline_test() ~= "inline_test"
|
local is_O2 = inline_test() ~= "inline_test"
|
||||||
|
|
||||||
return { strict = not is_O2, batch = false }
|
return {
|
||||||
|
strict = not is_O2,
|
||||||
|
batch = false,
|
||||||
|
defer_nested_properties = true
|
||||||
|
}
|
||||||
|
|
|
||||||
13
src/lib.luau
13
src/lib.luau
|
|
@ -75,6 +75,7 @@ local vide = {
|
||||||
|
|
||||||
-- flags
|
-- flags
|
||||||
strict = (nil :: any) :: boolean,
|
strict = (nil :: any) :: boolean,
|
||||||
|
defer_nested_properties = (nil :: any) :: boolean,
|
||||||
|
|
||||||
-- temporary
|
-- temporary
|
||||||
apply = function(instance: Instance)
|
apply = function(instance: Instance)
|
||||||
|
|
@ -96,18 +97,18 @@ local vide = {
|
||||||
|
|
||||||
setmetatable(vide :: any, {
|
setmetatable(vide :: any, {
|
||||||
__index = function(_, index: unknown): ()
|
__index = function(_, index: unknown): ()
|
||||||
if index == "strict" then
|
if flags[index] == nil then
|
||||||
return flags.strict
|
|
||||||
else
|
|
||||||
throw(`{tostring(index)} is not a valid member of vide`)
|
throw(`{tostring(index)} is not a valid member of vide`)
|
||||||
|
else
|
||||||
|
return flags[index]
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
__newindex = function(_, index: unknown, value: unknown)
|
__newindex = function(_, index: unknown, value: unknown)
|
||||||
if index == "strict" then
|
if flags[index] == nil then
|
||||||
flags.strict = value :: boolean
|
|
||||||
else
|
|
||||||
throw(`{tostring(index)} is not a valid member of vide`)
|
throw(`{tostring(index)} is not a valid member of vide`)
|
||||||
|
else
|
||||||
|
flags[index] = value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -759,18 +759,35 @@ TEST("create()", wrap_root(function()
|
||||||
CHECK(text.Text == "test")
|
CHECK(text.Text == "test")
|
||||||
end
|
end
|
||||||
|
|
||||||
do CASE "nested precedence"
|
do CASE "nested deferred"
|
||||||
local text = create "TextLabel" {
|
local text = create "TextLabel" {
|
||||||
{
|
{
|
||||||
|
{ Text = "2" },
|
||||||
Text = "1",
|
Text = "1",
|
||||||
|
|
||||||
{ Text = "2" }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECK(text.Text == "2")
|
CHECK(text.Text == "2")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
do CASE "nested not deferred"
|
||||||
|
vide.defer_nested_properties = false
|
||||||
|
|
||||||
|
local t = {}
|
||||||
|
|
||||||
|
create "TextLabel" {
|
||||||
|
{
|
||||||
|
{ function() table.insert(t, 1) end } :: any,
|
||||||
|
function() table.insert(t, 2) end,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(t[1] == 1)
|
||||||
|
CHECK(t[2] == 2)
|
||||||
|
|
||||||
|
vide.defer_nested_properties = true
|
||||||
|
end
|
||||||
|
|
||||||
do CASE "independent"
|
do CASE "independent"
|
||||||
local frame = create "Frame"
|
local frame = create "Frame"
|
||||||
CHECK(frame {} ~= frame {})
|
CHECK(frame {} ~= frame {})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue