Last active
February 4, 2026 06:19
-
-
Save nommiin/292bb3d26e1a4629bea98c6a6632d158 to your computer and use it in GitHub Desktop.
spec.ts - Convert GmlSpec.xml into JSON using bun.sh
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
| /* | |
| title: spec.ts | |
| author: nommiin | |
| description: uses HTMLRewriter to convert GmlSpec.xml file to output.json | |
| note: reads GmlSpec.xml from runtime-2024.1400.3.948, ensure this is installed | |
| */ | |
| class GmlElement { | |
| public Name: string = ""; | |
| toJSON(): Record<string, unknown> { | |
| const { Name, ...ext } = this; | |
| return ext; | |
| } | |
| } | |
| class GmlParameter extends GmlElement { | |
| public Type: string; | |
| public Optional: boolean; | |
| public Description: string = ""; | |
| constructor(_element: HTMLRewriterTypes.Element) { | |
| super(); | |
| this.Name = (_element.getAttribute("Name") as string); | |
| this.Type = (_element.getAttribute("Type") as string); | |
| this.Optional = (_element.getAttribute("Optional") ?? "false") == "true"; | |
| } | |
| override toJSON(): Record<string, unknown> { | |
| return (this as Record<string, unknown>); | |
| } | |
| } | |
| class GmlFunction extends GmlElement { | |
| public Deprecated: boolean; | |
| public ReturnType: string; | |
| public Pure: boolean; | |
| public Description: string = ""; | |
| public Parameters: GmlParameter[] = []; | |
| constructor(_element: HTMLRewriterTypes.Element) { | |
| super(); | |
| this.Name = (_element.getAttribute("Name") as string); | |
| this.Deprecated = (_element.getAttribute("Deprecated") ?? "false") == "true"; | |
| this.ReturnType = _element.getAttribute("ReturnType") as string; | |
| this.Pure = (_element.getAttribute("Pure") ?? "false") === "true"; | |
| } | |
| } | |
| class GmlVariable extends GmlElement { | |
| public Type: string; | |
| public Deprecated: boolean; | |
| public Get: boolean; | |
| public Set: boolean; | |
| public Instance: boolean; | |
| public Locale: string | undefined; | |
| public Description: string = ""; | |
| constructor(_element: HTMLRewriterTypes.Element) { | |
| super(); | |
| this.Name = (_element.getAttribute("Name") as string); | |
| this.Type = (_element.getAttribute("Type") as string); | |
| this.Deprecated = (_element.getAttribute("Deprecated") ?? "false") == "true"; | |
| this.Get = (_element.getAttribute("Get") ?? "false") == "true"; | |
| this.Set = (_element.getAttribute("Set") ?? "false") == "true"; | |
| this.Instance = (_element.getAttribute("Instance") ?? "false") == "true"; | |
| this.Locale = _element.getAttribute("Locale") ?? undefined; | |
| } | |
| } | |
| class GmlConstant extends GmlElement { | |
| public Type: string; | |
| public Deprecated: boolean; | |
| public Description: string = ""; | |
| constructor(_element: HTMLRewriterTypes.Element) { | |
| super(); | |
| this.Name = (_element.getAttribute("Name") as string); | |
| this.Type = (_element.getAttribute("Type") as string); | |
| this.Deprecated = (_element.getAttribute("Deprecated") ?? "false") == "true"; | |
| } | |
| } | |
| class GmlStructure extends GmlElement { | |
| public Fields: GmlField[] = []; | |
| constructor(_element: HTMLRewriterTypes.Element) { | |
| super(); | |
| this.Name = (_element.getAttribute("Name") as string); | |
| } | |
| } | |
| class GmlField extends GmlVariable { | |
| override toJSON(): Record<string, unknown> { | |
| return (this as Record<string, unknown>); | |
| } | |
| } | |
| class GmlSpec { | |
| public Functions: Record<string, GmlFunction>; | |
| public Variables: Record<string, GmlVariable>; | |
| public Constants: Record<string, GmlConstant>; | |
| public Structures: Record<string, GmlStructure>; | |
| constructor(_content: string) { | |
| this.Functions = this.parse_functions(_content); | |
| this.Variables = this.parse_variables(_content); | |
| this.Constants = this.parse_constants(_content); | |
| this.Structures = this.parse_structures(_content); | |
| } | |
| private parse_functions(_content: string): Record<string, GmlFunction> { | |
| let output: Record<string, GmlFunction> = {}, recent: GmlFunction | undefined = undefined; | |
| new HTMLRewriter().on("Function", { | |
| element(element) { | |
| recent = new GmlFunction(element); | |
| output[recent.Name] = recent; | |
| }, | |
| }).on("Description", { | |
| text(text) { | |
| const inner = text.text.trim(); | |
| if (recent == undefined || inner.length == 0) return; | |
| recent.Description = inner; | |
| }, | |
| }).on("Parameter", { | |
| element(element) { | |
| if (recent == undefined) return; | |
| recent.Parameters.push(new GmlParameter(element)); | |
| }, | |
| text(text) { | |
| const inner = text.text.trim(); | |
| if (recent == undefined || inner.length == 0) return; | |
| const param = recent.Parameters.at(-1); | |
| if (param == undefined) return; | |
| param.Description = inner; | |
| } | |
| }).transform(_content); | |
| return output; | |
| } | |
| private parse_variables(_content: string): Record<string, GmlVariable> { | |
| let output: Record<string, GmlVariable> = {}, recent: GmlVariable | undefined = undefined; | |
| new HTMLRewriter().on("Variable", { | |
| element(element) { | |
| recent = new GmlVariable(element); | |
| output[recent.Name] = recent; | |
| }, | |
| text(text) { | |
| const inner = text.text.trim(); | |
| if (recent == undefined || inner.length == 0) return; | |
| recent.Description = inner; | |
| } | |
| }).transform(_content); | |
| return output; | |
| } | |
| private parse_constants(_content: string): Record<string, GmlConstant> { | |
| let output: Record<string, GmlConstant> = {}, recent: GmlConstant | undefined = undefined; | |
| new HTMLRewriter().on("Constant", { | |
| element(element) { | |
| recent = new GmlConstant(element); | |
| output[recent.Name] = recent; | |
| }, | |
| text(text) { | |
| const inner = text.text.trim(); | |
| if (recent == undefined || inner.length == 0) return; | |
| recent.Description = inner; | |
| } | |
| }).transform(_content); | |
| return output; | |
| } | |
| private parse_structures(_content: string): Record<string, GmlStructure> { | |
| let output: Record<string, GmlStructure> = {}, recent: GmlStructure | undefined = undefined; | |
| new HTMLRewriter().on("Structure", { | |
| element(element) { | |
| recent = new GmlStructure(element); | |
| output[recent.Name] = recent; | |
| } | |
| }).on("Field", { | |
| element(element) { | |
| if (recent == undefined) return; | |
| recent.Fields.push(new GmlField(element)); | |
| }, | |
| text(text) { | |
| const inner = text.text.trim(); | |
| if (recent == undefined || inner.length == 0) return; | |
| const field = recent.Fields.at(-1); | |
| if (field == undefined) return; | |
| field.Description = inner; | |
| }, | |
| }).transform(_content); | |
| return output; | |
| } | |
| } | |
| const path: string = "C:\\ProgramData\\GameMakerStudio2-Beta\\Cache\\runtimes\\runtime-2024.1400.3.948\\GmlSpec.xml"; | |
| const spec = new GmlSpec(await Bun.file(path).text()); | |
| await Bun.write("output.json", JSON.stringify(spec, undefined, 4)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment