Created
July 18, 2020 15:56
-
-
Save Validark/333c0d1d551ef6021bcc161067d4d1dd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const HttpService = game.GetService("HttpService"); | |
| export async function getRequest(...[url, nocache, headers]: Parameters<HttpService["GetAsync"]>) { | |
| return HttpService.GetAsync(url, nocache, headers); | |
| } | |
| export interface ReferenceData { | |
| readonly owner: string; | |
| readonly repository: string; | |
| readonly branch?: string; | |
| } | |
| export async function getRefs({ owner, repository, branch = "master" }: ReferenceData) { | |
| return getRequest(`https://api.github.com/repos/${owner}/${repository}/git/refs/heads/${branch}`).then( | |
| (str) => | |
| HttpService.JSONDecode(str) as { | |
| ref: string; | |
| node_id: string; | |
| url: string; | |
| object: { sha: string; type: string; url: string }; | |
| }, | |
| ); | |
| } | |
| interface TreeNodeMaster { | |
| /** The path to this node. | |
| * e.g. lib/init.lua | |
| */ | |
| path: string; | |
| /** The file mode: | |
| * 100644 for file (blob) | |
| * 100755 for executable (blob) | |
| * 040000 for subdirectory (tree) | |
| * 160000 for submodule (commit) | |
| * 120000 for a blob that specifies the path of a symlink | |
| */ | |
| mode: "100644" | "100755" | "040000" | "160000" | "120000"; | |
| /** The type of Node this is. */ | |
| type: "blob" | "tree" | "commit"; | |
| /** The SHA1 checksum ID of the node. */ | |
| sha: string; | |
| /** How much memory this node takes up. */ | |
| size: number; | |
| /** The URL of this node. */ | |
| url: string; | |
| } | |
| interface Blob extends TreeNodeMaster { | |
| type: "blob"; | |
| } | |
| interface Tree extends Omit<TreeNodeMaster, "size"> { | |
| type: "tree"; | |
| } | |
| interface Commit extends Omit<TreeNodeMaster, "size" | "url"> { | |
| type: "commit"; | |
| } | |
| type TreeNode = Blob | Tree | Commit; | |
| export async function getRepoTree(data: ReferenceData) { | |
| return getRefs(data) | |
| .then(({ object: { sha } }) => | |
| getRequest(`https://api.github.com/repos/${data.owner}/${data.repository}/git/trees/${sha}?recursive=true`), | |
| ) | |
| .then( | |
| (str) => | |
| HttpService.JSONDecode(str) as { | |
| sha: string; | |
| url: string; | |
| tree: Array<TreeNode>; | |
| truncated: boolean; | |
| }, | |
| ); | |
| } | |
| export type RepoMap = Map<string, string | RepoMap>; | |
| export async function getProcessedRepoTree(referenceData: ReferenceData) { | |
| return getRepoTree(referenceData).then(({ tree }) => { | |
| const { owner, repository, branch = "master" } = referenceData; | |
| const map: RepoMap = new Map(); | |
| for (const { path, type: myType } of tree) { | |
| const items = new Array<string>(); | |
| for (const [item] of path.gmatch("[^/]+")) { | |
| items.push(item); | |
| } | |
| const last = items.pop()!; | |
| let current: RepoMap = map; | |
| for (const item of items) { | |
| const nextItem = current.get(item); | |
| if (nextItem === undefined || typeIs(nextItem, "string")) error("Houston, we have a problem!"); | |
| current = nextItem; | |
| } | |
| switch (myType) { | |
| case "tree": | |
| current.set(last, new Map()); | |
| break; | |
| case "blob": | |
| current.set(last, `https://raw.githubusercontent.com/${owner}/${repository}/${branch}/${path}`); | |
| break; | |
| case "commit": | |
| // just ignore submodules for now? I don't really know how this should even be handled | |
| break; | |
| } | |
| } | |
| return map; | |
| }); | |
| } | |
| const scriptExtensions = [ | |
| [".spec.lua", ""], | |
| [".spec.server.lua", ""], | |
| [".spec.client.lua", ""], | |
| [".server.lua", "Script"], | |
| [".script.lua", "Script"], | |
| [".client.lua", "LocalScript"], | |
| [".loc.lua", "LocalScript"], | |
| [".local.lua", "LocalScript"], | |
| [".mod.lua", "ModuleScript"], | |
| [".module.lua", "ModuleScript"], | |
| [".lua", "ModuleScript"], | |
| ] as const; | |
| function instantiateScript( | |
| link: string, | |
| name: string, | |
| scriptType: "Script" | "LocalScript" | "ModuleScript", | |
| parent: Instance | undefined, | |
| ) { | |
| const script = new Instance(scriptType); | |
| script.Name = name; | |
| script.Parent = parent; | |
| getRequest(link).then((source) => { | |
| // script.Source = source; | |
| const str = new Instance("StringValue"); | |
| str.Name = "Source"; | |
| str.Value = source; | |
| str.Parent = script; | |
| }); | |
| return script; | |
| } | |
| function instantiateTree(tree: RepoMap, parent: Instance = new Instance("Folder")) { | |
| for (const [name, link] of tree) { | |
| // file | |
| if (typeIs(link, "string")) { | |
| for (const [scriptExtension, scriptType] of scriptExtensions) { | |
| if (link.endsWith(scriptExtension) && scriptType) { | |
| instantiateScript(link, name.slice(0, -scriptExtension.size()), scriptType, parent); | |
| break; | |
| } | |
| } | |
| } | |
| // folder | |
| else { | |
| const repoMap = link; | |
| let folder: Folder | Script | ModuleScript | undefined; | |
| for (const [scriptExtension, scriptType] of scriptExtensions) { | |
| const repoName = "init" + scriptExtension; | |
| const initFile = repoMap.get(repoName); | |
| if (typeIs(initFile, "string") && scriptType) { | |
| if (folder) error("Found multiple init files?"); | |
| repoMap.delete(repoName); | |
| folder = instantiateScript(initFile, name, scriptType, parent); | |
| } | |
| } | |
| if (folder === undefined) { | |
| folder = new Instance("Folder"); | |
| folder.Name = name; | |
| folder.Parent = parent; | |
| } | |
| instantiateTree(repoMap, folder); | |
| if (folder.IsA("Folder") && folder.GetChildren().size() === 0) { | |
| folder.Destroy(); | |
| } | |
| } | |
| } | |
| return parent; | |
| } | |
| getProcessedRepoTree({ | |
| owner: "Quenty", | |
| repository: "NevermoreEngine", | |
| branch: "version2", | |
| }).then((tree) => { | |
| let current = tree.get("loader"); | |
| if (current && !typeIs(current, "string")) { | |
| current = current.get("ReplicatedStorage"); | |
| if (current && !typeIs(current, "string")) { | |
| for (const child of instantiateTree(current).GetChildren()) { | |
| child.Parent = game.GetService("ReplicatedStorage"); | |
| } | |
| } | |
| } | |
| current = tree.get("Modules"); | |
| if (current && !typeIs(current, "string")) { | |
| const modules = instantiateTree(current); | |
| modules.Name = "Nevermore"; | |
| modules.Parent = game.GetService("Workspace"); | |
| } | |
| instantiateTree(tree).Parent = game.GetService("Workspace"); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment