Something went wrong. Try again.
A native-speed rewrite of the roblox-ts compiler in golang
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261local 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") ~= nilend
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] -> Librarylocal 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 dataend
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 falseend
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) endend
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) endend
local SIGN = 2 ^ 31local COMPLEMENT = 2 ^ 32local 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 endend
function TS.bit_lrsh(a, b) return bit_sign(bit32.arshift(a, b))end
TS.TRY_RETURN = 1TS.TRY_BREAK = 2TS.TRY_CONTINUE = 3
function TS.try(try, catch, finally) -- execute try local trySuccess, exitTypeOrTryError, returns = pcall(try) local exitType, tryError if trySuccess then exitType = exitTypeOrTryError else tryError = exitTypeOrTryError end
local catchSuccess = true local catchError
-- if try block failed, and catch block exists, execute catch if not trySuccess and catch then local newExitTypeOrCatchError, newReturns catchSuccess, newExitTypeOrCatchError, newReturns = pcall(catch, tryError) local newExitType if catchSuccess then newExitType = newExitTypeOrCatchError else catchError = newExitTypeOrCatchError end
if newExitType then exitType, returns = newExitType, newReturns end end
-- execute finally if finally then local newExitType, newReturns = finally() if newExitType then exitType, returns = newExitType, newReturns end end
-- if exit type is a control flow, do not rethrow errors if exitType ~= TS.TRY_RETURN and exitType ~= TS.TRY_BREAK and exitType ~= TS.TRY_CONTINUE then -- if catch block threw an error, rethrow it if not catchSuccess then error(catchError, 2) end
-- if try block threw an error and there was no catch block, rethrow it if not trySuccess and not catch then error(tryError, 2) end end
return exitType, returnsend
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