Files
rojo/plugin/src/Reconciler/diff.lua
Ayuka 6cc2e919c0 Update nil Ref check and property decode warning to new Rojo protocol (#466)
* Skip empty Refs in new Rojo protocol

* Update warning message for new Rojo protocol
2021-08-09 14:14:59 -04:00

151 lines
4.3 KiB
Lua

--[[
Defines the process for diffing a virtual DOM and the real DOM to compute a
patch that can be later applied.
]]
local Log = require(script.Parent.Parent.Parent.Log)
local invariant = require(script.Parent.Parent.invariant)
local getProperty = require(script.Parent.getProperty)
local Error = require(script.Parent.Error)
local decodeValue = require(script.Parent.decodeValue)
local function isEmpty(table)
return next(table) == nil
end
local function shouldDeleteUnknownInstances(virtualInstance)
if virtualInstance.Metadata ~= nil then
return not virtualInstance.Metadata.ignoreUnknownInstances
else
return true
end
end
local function diff(instanceMap, virtualInstances, rootId)
local patch = {
removed = {},
added = {},
updated = {},
}
-- Add a virtual instance and all of its descendants to the patch, marked as
-- being added.
local function markIdAdded(id)
local virtualInstance = virtualInstances[id]
patch.added[id] = virtualInstance
for _, childId in ipairs(virtualInstance.Children) do
markIdAdded(childId)
end
end
-- Internal recursive kernel for diffing an instance with the given ID.
local function diffInternal(id)
local virtualInstance = virtualInstances[id]
local instance = instanceMap.fromIds[id]
if virtualInstance == nil then
invariant("Cannot diff an instance not present in virtualInstances\nID: {}", id)
end
if instance == nil then
invariant("Cannot diff an instance not present in InstanceMap\nID: {}", id)
end
local changedClassName = nil
if virtualInstance.ClassName ~= instance.ClassName then
changedClassName = virtualInstance.ClassName
end
local changedName = nil
if virtualInstance.Name ~= instance.Name then
changedName = virtualInstance.Name
end
local changedProperties = {}
for propertyName, virtualValue in pairs(virtualInstance.Properties) do
local ok, existingValueOrErr = getProperty(instance, propertyName)
if ok then
local existingValue = existingValueOrErr
local ok, decodedValue = decodeValue(virtualValue, instanceMap)
if ok then
if existingValue ~= decodedValue then
changedProperties[propertyName] = virtualValue
end
else
-- virtualValue can be empty in certain cases, and this may print out nil to the user.
local propertyType = next(virtualValue)
Log.warn("Failed to decode property of type {}", propertyType)
end
else
local err = existingValueOrErr
if err.kind == Error.UnknownProperty then
Log.trace("Skipping unknown property {}.{}", err.details.className, err.details.propertyName)
elseif err.kind == Error.UnreadableProperty then
Log.trace("Skipping unreadable property {}.{}", err.details.className, err.details.propertyName)
else
return false, err
end
end
end
if changedName ~= nil or changedClassName ~= nil or not isEmpty(changedProperties) then
table.insert(patch.updated, {
id = id,
changedName = changedName,
changedClassName = changedClassName,
changedProperties = changedProperties,
changedMetadata = nil,
})
end
-- Traverse the list of children in the DOM. Any instance that has no
-- corresponding virtual instance should be removed. Any instance that
-- does have a corresponding virtual instance is recursively diffed.
for _, childInstance in ipairs(instance:GetChildren()) do
local childId = instanceMap.fromInstances[childInstance]
if childId == nil then
-- This is an existing instance not present in the virtual DOM.
-- We can mark it for deletion unless the user has asked us not
-- to delete unknown stuff.
if shouldDeleteUnknownInstances(virtualInstance) then
table.insert(patch.removed, childInstance)
end
else
local ok, err = diffInternal(childId)
if not ok then
return false, err
end
end
end
-- Traverse the list of children in the virtual DOM. Any virtual
-- instance that has no corresponding real instance should be created.
for _, childId in ipairs(virtualInstance.Children) do
local childInstance = instanceMap.fromIds[childId]
if childInstance == nil then
-- This instance is present in the virtual DOM, but doesn't
-- exist in the real DOM.
markIdAdded(childId)
end
end
return true
end
local ok, err = diffInternal(rootId)
if not ok then
return false, err
end
return true, patch
end
return diff