mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-04-20 20:55:50 +00:00
117 lines
3.1 KiB
Lua
117 lines
3.1 KiB
Lua
--[[
|
|
This module defines the meat of the Rojo plugin and how it manages tracking
|
|
and mutating the Roblox DOM.
|
|
]]
|
|
|
|
local Rojo = script:FindFirstAncestor("Rojo")
|
|
local Plugin = Rojo.Plugin
|
|
local Packages = Rojo.Packages
|
|
|
|
local Log = require(Packages.Log)
|
|
|
|
local Timer = require(Plugin.Timer)
|
|
|
|
local applyPatch = require(script.applyPatch)
|
|
local hydrate = require(script.hydrate)
|
|
local diff = require(script.diff)
|
|
|
|
local Reconciler = {}
|
|
Reconciler.__index = Reconciler
|
|
|
|
function Reconciler.new(instanceMap)
|
|
local self = {
|
|
-- Tracks all of the instances known by the reconciler by ID.
|
|
__instanceMap = instanceMap,
|
|
__precommitCallbacks = {},
|
|
__postcommitCallbacks = {},
|
|
}
|
|
|
|
return setmetatable(self, Reconciler)
|
|
end
|
|
|
|
function Reconciler:hookPrecommit(callback: (patch: any, instanceMap: any) -> ()): () -> ()
|
|
table.insert(self.__precommitCallbacks, callback)
|
|
Log.trace("Added precommit callback: {}", callback)
|
|
|
|
return function()
|
|
-- Remove the callback from the list
|
|
for i, cb in self.__precommitCallbacks do
|
|
if cb == callback then
|
|
table.remove(self.__precommitCallbacks, i)
|
|
Log.trace("Removed precommit callback: {}", callback)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function Reconciler:hookPostcommit(callback: (patch: any, instanceMap: any, unappliedPatch: any) -> ()): () -> ()
|
|
table.insert(self.__postcommitCallbacks, callback)
|
|
Log.trace("Added postcommit callback: {}", callback)
|
|
|
|
return function()
|
|
-- Remove the callback from the list
|
|
for i, cb in self.__postcommitCallbacks do
|
|
if cb == callback then
|
|
table.remove(self.__postcommitCallbacks, i)
|
|
Log.trace("Removed postcommit callback: {}", callback)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function Reconciler:applyPatch(patch)
|
|
Timer.start("Reconciler:applyPatch")
|
|
|
|
Timer.start("precommitCallbacks")
|
|
-- Precommit callbacks must be serial in order to obey the contract that
|
|
-- they execute before commit
|
|
for _, callback in self.__precommitCallbacks do
|
|
local success, err = pcall(callback, patch, self.__instanceMap)
|
|
if not success then
|
|
Log.warn("Precommit hook errored: {}", err)
|
|
end
|
|
end
|
|
Timer.stop()
|
|
|
|
Timer.start("apply")
|
|
local unappliedPatch = applyPatch(self.__instanceMap, patch)
|
|
Timer.stop()
|
|
|
|
Timer.start("postcommitCallbacks")
|
|
-- Postcommit callbacks can be called with spawn since regardless of firing order, they are
|
|
-- guaranteed to be called after the commit
|
|
for _, callback in self.__postcommitCallbacks do
|
|
task.spawn(function()
|
|
local success, err = pcall(callback, patch, self.__instanceMap, unappliedPatch)
|
|
if not success then
|
|
Log.warn("Postcommit hook errored: {}", err)
|
|
end
|
|
end)
|
|
end
|
|
Timer.stop()
|
|
|
|
Timer.stop()
|
|
|
|
return unappliedPatch
|
|
end
|
|
|
|
function Reconciler:hydrate(virtualInstances, rootId, rootInstance)
|
|
Timer.start("Reconciler:hydrate")
|
|
local result = hydrate(self.__instanceMap, virtualInstances, rootId, rootInstance)
|
|
Timer.stop()
|
|
|
|
return result
|
|
end
|
|
|
|
function Reconciler:diff(virtualInstances, rootId)
|
|
Timer.start("Reconciler:diff")
|
|
local success, result = diff(self.__instanceMap, virtualInstances, rootId)
|
|
Timer.stop()
|
|
|
|
return success, result
|
|
end
|
|
|
|
return Reconciler
|