Files
Roblox-Elevator-Game/include/RuntimeLib.lua
2024-04-04 21:22:31 -04:00

232 lines
5.1 KiB
Lua

local Promise = require(script.Parent.Promise)
local RunService = game:GetService("RunService")
local OUTPUT_PREFIX = "roblox-ts: "
local NODE_MODULES = "node_modules"
local DEFAULT_SCOPE = "@rbxts"
local TS = {}
TS.Promise = Promise
local function isPlugin(context)
return RunService:IsStudio() and context:FindFirstAncestorWhichIsA("Plugin") ~= nil
end
function TS.getModule(context, scope, moduleName)
-- legacy call signature
if moduleName == nil then
moduleName = scope
scope = DEFAULT_SCOPE
end
-- ensure modules have fully replicated
if RunService:IsRunning() and RunService:IsClient() and not isPlugin(context) and not game:IsLoaded() then
game.Loaded:Wait()
end
local object = context
repeat
local nodeModulesFolder = object:FindFirstChild(NODE_MODULES)
if nodeModulesFolder then
local scopeFolder = nodeModulesFolder:FindFirstChild(scope)
if scopeFolder then
local module = scopeFolder:FindFirstChild(moduleName)
if module then
return module
end
end
end
object = object.Parent
until object == nil
error(OUTPUT_PREFIX .. "Could not find module: " .. moduleName, 2)
end
-- This is a hash which TS.import uses as a kind of linked-list-like history of [Script who Loaded] -> Library
local currentlyLoading = {}
local registeredLibraries = {}
function TS.import(context, module, ...)
for i = 1, select("#", ...) do
module = module:WaitForChild((select(i, ...)))
end
if module.ClassName ~= "ModuleScript" then
error(OUTPUT_PREFIX .. "Failed to import! Expected ModuleScript, got " .. module.ClassName, 2)
end
currentlyLoading[context] = module
-- Check to see if a case like this occurs:
-- module -> Module1 -> Module2 -> module
-- WHERE currentlyLoading[module] is Module1
-- and currentlyLoading[Module1] is Module2
-- and currentlyLoading[Module2] is module
local currentModule = module
local depth = 0
while currentModule do
depth = depth + 1
currentModule = currentlyLoading[currentModule]
if currentModule == module then
local str = currentModule.Name -- Get the string traceback
for _ = 1, depth do
currentModule = currentlyLoading[currentModule]
str = str .. "" .. currentModule.Name
end
error(OUTPUT_PREFIX .. "Failed to import! Detected a circular dependency chain: " .. str, 2)
end
end
if not registeredLibraries[module] then
if _G[module] then
error(
OUTPUT_PREFIX
.. "Invalid module access! Do you have multiple TS runtimes trying to import this? "
.. module:GetFullName(),
2
)
end
_G[module] = TS
registeredLibraries[module] = true -- register as already loaded for subsequent calls
end
local data = require(module)
if currentlyLoading[context] == module then -- Thread-safe cleanup!
currentlyLoading[context] = nil
end
return data
end
function TS.instanceof(obj, class)
-- custom Class.instanceof() check
if type(class) == "table" and type(class.instanceof) == "function" then
return class.instanceof(obj)
end
-- metatable check
if type(obj) == "table" then
obj = getmetatable(obj)
while obj ~= nil do
if obj == class then
return true
end
local mt = getmetatable(obj)
if mt then
obj = mt.__index
else
obj = nil
end
end
end
return false
end
function TS.async(callback)
return function(...)
local n = select("#", ...)
local args = { ... }
return Promise.new(function(resolve, reject)
coroutine.wrap(function()
local ok, result = pcall(callback, unpack(args, 1, n))
if ok then
resolve(result)
else
reject(result)
end
end)()
end)
end
end
function TS.await(promise)
if not Promise.is(promise) then
return promise
end
local status, value = promise:awaitStatus()
if status == Promise.Status.Resolved then
return value
elseif status == Promise.Status.Rejected then
error(value, 2)
else
error("The awaited Promise was cancelled", 2)
end
end
local SIGN = 2 ^ 31
local COMPLEMENT = 2 ^ 32
local function bit_sign(num)
-- Restores the sign after an unsigned conversion according to 2s complement.
if bit32.btest(num, SIGN) then
return num - COMPLEMENT
else
return num
end
end
function TS.bit_lrsh(a, b)
return bit_sign(bit32.arshift(a, b))
end
TS.TRY_RETURN = 1
TS.TRY_BREAK = 2
TS.TRY_CONTINUE = 3
function TS.try(func, catch, finally)
local err, traceback
local success, exitType, returns = xpcall(
func,
function(errInner)
err = errInner
traceback = debug.traceback()
end
)
if not success and catch then
local newExitType, newReturns = catch(err, traceback)
if newExitType then
exitType, returns = newExitType, newReturns
end
end
if finally then
local newExitType, newReturns = finally()
if newExitType then
exitType, returns = newExitType, newReturns
end
end
return exitType, returns
end
function TS.generator(callback)
local co = coroutine.create(callback)
return {
next = function(...)
if coroutine.status(co) == "dead" then
return { done = true }
else
local success, value = coroutine.resume(co, ...)
if success == false then
error(value, 2)
end
return {
value = value,
done = coroutine.status(co) == "dead",
}
end
end,
}
end
return TS