local I=game:GetService("\067\111\108\108\101\099\116\105\111\110\083\101\114\118\105\099\101")local j=script.Parent local b=require(script.Parent.Utils)local o=require(script.Parent.BounceTemplateUtils)local u={}u.DEPENDENCY_FOLDER_NAME="\110\111\100\101\095\109\111\100\117\108\101\115"u.ModuleReplicationTypes=b.readonly({CLIENT="\099\108\105\101\110\116",SERVER="\115\101\114\118\101\114",SHARED="\115\104\097\114\101\100";IGNORE="\105\103\110\111\114\101"})function u.createScriptInfo(I,j,o)assert(typeof(I)=="\073\110\115\116\097\110\099\101","\066\097\100 \105\110\115\116\097\110\099\101")assert(type(j)=="\115\116\114\105\110\103","\066\097\100 \110\097\109\101")assert(type(o)=="\115\116\114\105\110\103","\066\097\100 \114\101\112\108\105\099\097\116\105\111\110\077\111\100\101")return b.readonly({name=j;replicationMode=o;instance=I})end function u.createScriptInfoLookup()return b.readonly({[u.ModuleReplicationTypes.SERVER]={},[u.ModuleReplicationTypes.CLIENT]={};[u.ModuleReplicationTypes.SHARED]={}})end function u.getScriptInfoLookupForMode(I,j)assert(type(I)=="\116\097\098\108\101","\066\097\100 \115\099\114\105\112\116\073\110\102\111\076\111\111\107\117\112")assert(type(j)=="\115\116\114\105\110\103","\066\097\100 \114\101\112\108\105\099\097\116\105\111\110\077\111\100\101")return I[j]end function u.populateScriptInfoLookup(I,b,K)assert(typeof(I)=="\073\110\115\116\097\110\099\101","\066\097\100 \105\110\115\116\097\110\099\101")assert(type(b)=="\116\097\098\108\101","\066\097\100 \115\099\114\105\112\116\073\110\102\111\076\111\111\107\117\112")assert(type(K)=="\115\116\114\105\110\103","\066\097\100 \108\097\115\116\082\101\112\108\105\099\097\116\105\111\110\077\111\100\101")if I:IsA("\070\111\108\100\101\114")then local j=u.getFolderReplicationMode(I.Name,K)if j~=u.ModuleReplicationTypes.IGNORE then for I,K in pairs(I:GetChildren())do if not o.isBounceTemplate(K)then if K:IsA("\070\111\108\100\101\114")then u.populateScriptInfoLookup(K,b,j)elseif K:IsA("\077\111\100\117\108\101\083\099\114\105\112\116")then u.addToInfoMap(b,u.createScriptInfo(K,K.Name,j))end end end end elseif I:IsA("\077\111\100\117\108\101\083\099\114\105\112\116")then if not o.isBounceTemplate(I)then if I==j then u.addToInfoMap(b,u.createScriptInfo(I,"\108\111\097\100\101\114",K))else u.addToInfoMap(b,u.createScriptInfo(I,I.Name,K))end end elseif I:IsA("\079\098\106\101\099\116\086\097\108\117\101")then error("\079\098\106\101\099\116\086\097\108\117\101 \108\105\110\107\115 \097\114\101 \110\111\116 \115\117\112\112\111\114\116\101\100 \097\116 \116\104\105\115 \116\105\109\101 \102\111\114 \114\101\116\114\105\101\118\105\110\103 \105\110\108\105\110\101 \109\111\100\117\108\101 \115\099\114\105\112\116\115")end end local K={HoldingBindersServer=true;HoldingBindersClient=true,IKService=true,IKServiceClient=true}function u.isAvailableInShared(j)if I:HasTag(j.instance,"\076\105\110\107\084\111\083\104\097\114\101\100")then return true end return K[j.name]end function u.addToInfoMap(I,j)assert(type(I)=="\116\097\098\108\101","\066\097\100 \115\099\114\105\112\116\073\110\102\111\076\111\111\107\117\112")assert(type(j)=="\116\097\098\108\101","\066\097\100 \115\099\114\105\112\116\073\110\102\111")local b=assert(j.replicationMode,"\066\097\100 \114\101\112\108\105\099\097\116\105\111\110\077\111\100\101")local o=assert(I[b],"\066\097\100 \114\101\112\108\105\099\097\116\105\111\110\077\111\100\101")u.addToInfoMapForMode(o,j)if b==u.ModuleReplicationTypes.SHARED then u.addToInfoMapForMode(I[u.ModuleReplicationTypes.SERVER],j)u.addToInfoMapForMode(I[u.ModuleReplicationTypes.CLIENT],j)elseif u.isAvailableInShared(j)then u.addToInfoMapForMode(I[u.ModuleReplicationTypes.SHARED],j)end end function u.addToInfoMapForMode(I,j)if I[j.name]then warn(("\068\117\112\108\105\099\097\116\101 \109\111\100\117\108\101 \037\113 \105\110 \115\097\109\101 \112\097\099\107\097\103\101 \117\110\100\101\114 \115\097\109\101 \114\101\112\108\105\099\097\116\105\111\110 \115\099\111\112\101\046 \079\110\108\121 \117\115\105\110\103 \102\105\114\115\116 \111\110\101\046 \n- \037\113\n- \037\113"):format(j.name,j.instance:GetFullName(),I[j.name]:GetFullName()))return end I[j.name]=j end function u.getFolderReplicationMode(I,j)assert(type(I)=="\115\116\114\105\110\103","\066\097\100 \102\111\108\100\101\114\078\097\109\101")assert(type(j)=="\115\116\114\105\110\103","\066\097\100 \108\097\115\116\082\101\112\108\105\099\097\116\105\111\110\077\111\100\101")if I=="\083\104\097\114\101\100"then return u.ModuleReplicationTypes.SHARED elseif I=="\067\108\105\101\110\116"then return u.ModuleReplicationTypes.CLIENT elseif I=="\083\101\114\118\101\114"then return u.ModuleReplicationTypes.SERVER elseif I==u.DEPENDENCY_FOLDER_NAME then return u.ModuleReplicationTypes.IGNORE else return j end end return u
--[=[
	@private
	@class ScriptInfoUtils
]=]

local CollectionService = game:GetService("CollectionService")

local loader = script.Parent
local Utils = require(script.Parent.Utils)
local BounceTemplateUtils = require(script.Parent.BounceTemplateUtils)

local ScriptInfoUtils = {}

ScriptInfoUtils.DEPENDENCY_FOLDER_NAME = "node_modules"
ScriptInfoUtils.ModuleReplicationTypes = Utils.readonly({
	CLIENT = "client",
	SERVER = "server",
	SHARED = "shared",
	IGNORE = "ignore",
})

function ScriptInfoUtils.createScriptInfo(instance, name, replicationMode)
	assert(typeof(instance) == "Instance", "Bad instance")
	assert(type(name) == "string", "Bad name")
	assert(type(replicationMode) == "string", "Bad replicationMode")

	return Utils.readonly({
		name = name,
		replicationMode = replicationMode,
		instance = instance,
	})
end

function ScriptInfoUtils.createScriptInfoLookup()
	-- Server/client also contain shared entries
	return Utils.readonly({
		[ScriptInfoUtils.ModuleReplicationTypes.SERVER] = {}, -- [string name] = scriptInfo
		[ScriptInfoUtils.ModuleReplicationTypes.CLIENT] = {},
		[ScriptInfoUtils.ModuleReplicationTypes.SHARED] = {},
	})
end

function ScriptInfoUtils.getScriptInfoLookupForMode(scriptInfoLookup, replicationMode)
	assert(type(scriptInfoLookup) == "table", "Bad scriptInfoLookup")
	assert(type(replicationMode) == "string", "Bad replicationMode")

	return scriptInfoLookup[replicationMode]
end

function ScriptInfoUtils.populateScriptInfoLookup(instance, scriptInfoLookup, lastReplicationMode)
	assert(typeof(instance) == "Instance", "Bad instance")
	assert(type(scriptInfoLookup) == "table", "Bad scriptInfoLookup")
	assert(type(lastReplicationMode) == "string", "Bad lastReplicationMode")

	if instance:IsA("Folder") then
		local replicationMode = ScriptInfoUtils.getFolderReplicationMode(instance.Name, lastReplicationMode)
		if replicationMode ~= ScriptInfoUtils.ModuleReplicationTypes.IGNORE then
			for _, item in pairs(instance:GetChildren()) do
				if not BounceTemplateUtils.isBounceTemplate(item) then
					if item:IsA("Folder") then
						ScriptInfoUtils.populateScriptInfoLookup(item, scriptInfoLookup, replicationMode)
					elseif item:IsA("ModuleScript") then
						ScriptInfoUtils.addToInfoMap(
							scriptInfoLookup,
							ScriptInfoUtils.createScriptInfo(item, item.Name, replicationMode)
						)
					end
				end
			end
		end
	elseif instance:IsA("ModuleScript") then
		if not BounceTemplateUtils.isBounceTemplate(instance) then
			if instance == loader then
				-- STRICT hack to support this module script as "loader" over "Nevermore" in replicated scenario
				ScriptInfoUtils.addToInfoMap(
					scriptInfoLookup,
					ScriptInfoUtils.createScriptInfo(instance, "loader", lastReplicationMode)
				)
			else
				ScriptInfoUtils.addToInfoMap(
					scriptInfoLookup,
					ScriptInfoUtils.createScriptInfo(instance, instance.Name, lastReplicationMode)
				)
			end
		end
	elseif instance:IsA("ObjectValue") then
		error("ObjectValue links are not supported at this time for retrieving inline module scripts")
	end
end

local AVAILABLE_IN_SHARED = {
	["HoldingBindersServer"] = true,
	["HoldingBindersClient"] = true,
	["IKService"] = true,
	["IKServiceClient"] = true,
}

function ScriptInfoUtils.isAvailableInShared(scriptInfo)
	if CollectionService:HasTag(scriptInfo.instance, "LinkToShared") then
		return true
	end

	-- Hack because we can't tag things in Rojo yet
	return AVAILABLE_IN_SHARED[scriptInfo.name]
end

function ScriptInfoUtils.addToInfoMap(scriptInfoLookup, scriptInfo)
	assert(type(scriptInfoLookup) == "table", "Bad scriptInfoLookup")
	assert(type(scriptInfo) == "table", "Bad scriptInfo")

	local replicationMode = assert(scriptInfo.replicationMode, "Bad replicationMode")
	local replicationMap = assert(scriptInfoLookup[replicationMode], "Bad replicationMode")

	ScriptInfoUtils.addToInfoMapForMode(replicationMap, scriptInfo)

	if replicationMode == ScriptInfoUtils.ModuleReplicationTypes.SHARED then
		ScriptInfoUtils.addToInfoMapForMode(scriptInfoLookup[ScriptInfoUtils.ModuleReplicationTypes.SERVER], scriptInfo)
		ScriptInfoUtils.addToInfoMapForMode(scriptInfoLookup[ScriptInfoUtils.ModuleReplicationTypes.CLIENT], scriptInfo)
	elseif ScriptInfoUtils.isAvailableInShared(scriptInfo) then
		ScriptInfoUtils.addToInfoMapForMode(scriptInfoLookup[ScriptInfoUtils.ModuleReplicationTypes.SHARED], scriptInfo)
	end
end

function ScriptInfoUtils.addToInfoMapForMode(replicationMap, scriptInfo)
	if replicationMap[scriptInfo.name] then
		warn(
			("Duplicate module %q in same package under same replication scope. Only using first one. \n- %q\n- %q"):format(
				scriptInfo.name,
				scriptInfo.instance:GetFullName(),
				replicationMap[scriptInfo.name]:GetFullName()
			)
		)
		return
	end

	replicationMap[scriptInfo.name] = scriptInfo
end

function ScriptInfoUtils.getFolderReplicationMode(folderName, lastReplicationMode)
	assert(type(folderName) == "string", "Bad folderName")
	assert(type(lastReplicationMode) == "string", "Bad lastReplicationMode")

	if folderName == "Shared" then
		return ScriptInfoUtils.ModuleReplicationTypes.SHARED
	elseif folderName == "Client" then
		return ScriptInfoUtils.ModuleReplicationTypes.CLIENT
	elseif folderName == "Server" then
		return ScriptInfoUtils.ModuleReplicationTypes.SERVER
	elseif folderName == ScriptInfoUtils.DEPENDENCY_FOLDER_NAME then
		return ScriptInfoUtils.ModuleReplicationTypes.IGNORE
	else
		return lastReplicationMode
	end
end

return ScriptInfoUtils
