1414LUAU_TYPES_URL = "https://raw.githubusercontent.com/MaximumADHD/Roblox-Client-Tracker/roblox/LuauTypes.d.luau"
1515BRICK_COLORS_URL = "https://gist.githubusercontent.com/Anaminus/49ac255a68e7a7bc3cdd72b602d5071f/raw/f1534dcae312dbfda716b7677f8ac338b565afc3/BrickColor.json"
1616
17- # Whether to include deprecated members that cannot be assigned the @deprecated attribute (i.e. deprecated non-functions).
17+ # Whether to include deprecated members that cannot be assigned the @deprecated attribute (i.e. deprecated non-functions).
1818# Deprecated functions will always be defined, with their corresponding @deprecated attribute.
1919INCLUDE_DEPRECATED_MEMBERS = False
2020
4141LUAU_SNIPPET_PATCHES = {
4242 "declare function delay(delay: number?, callback: () -> ())" :
4343 "@[deprecated{ use = \" task.delay\" }]\n declare function delay(delay: number?, callback: (dt: number, gt: number) -> ())" ,
44-
44+
4545 "declare function collectgarbage(mode: string): number" :
4646 "@[deprecated{ use = \" gcinfo\" }]\n declare function collectgarbage(mode: \" count\" ): number" ,
47-
47+
4848 "declare function stats()" :
4949 "@[deprecated{ use = 'game:GetService(\" Stats\" )' }]\n declare function stats(): Stats" ,
50-
51- "declare function wait(delay: number?): (number, number)" :
50+
51+ "declare function wait(delay: number?): (number, number)" :
5252 "@[deprecated{ use = \" task.wait\" }]\n declare function wait(delay: number?): (number, number)" ,
53-
53+
5454 "declare function printidentity(prefix: string?)" :
5555 "@deprecated declare function printidentity(prefix: string?)" ,
5656
5757 "declare function version(): string" :
5858 "@deprecated declare function version(): string" ,
59-
59+
6060 "declare game: any" : "declare game: DataModel" ,
6161 "declare workspace: any" : "declare workspace: Workspace" ,
6262 "declare script: any" : "declare script: LuaSourceContainer" ,
6363
64- "declare Delay: typeof(delay)" :
64+ "declare Delay: typeof(delay)" :
6565 "@[deprecated{ use = \" task.delay\" }]\n declare function Delay(delay: number?, callback: (dt: number, gt: number) -> ())" ,
66-
67- "declare Wait: typeof(wait)" :
66+
67+ "declare Wait: typeof(wait)" :
6868 "@[deprecated{ use = \" task.wait\" }]\n declare function Wait(delay: number?): (number, number)" ,
69-
70- "declare ElapsedTime: typeof(elapsedTime)" :
69+
70+ "declare ElapsedTime: typeof(elapsedTime)" :
7171 "@[deprecated{ use = \" elapsedTime\" }]\n declare function ElapsedTime(): number" ,
72-
72+
7373 "declare Stats: typeof(stats)" :
7474 "@[deprecated{ use = 'game:GetService(\" Stats\" )' }]\n declare function Stats(): Stats" ,
75-
76- "declare Version: typeof(version)" :
75+
76+ "declare Version: typeof(version)" :
7777 "@[deprecated{ use = \" version\" }]\n declare function Version(): string" ,
78-
78+
7979 "declare Workspace: any" : "" ,
8080 "declare Game: any" : "" ,
8181}
430430type BinaryString = string
431431type QDir = string
432432type QFont = string
433- type FloatCurveKey = any
434- type RotationCurveKey = any
435- type Secret = any
436- type Path2DControlPoint = any
437- type UniqueId = any
438- type SecurityCapabilities = any
439433type TeleportData = boolean | buffer | number | string | {[number]: TeleportData} | {[string]: TeleportData}
440- type SystemAddress = any
441- type AdReward = any
442434
443435declare class Enum
444436 function GetEnumItems(self): { any }
487479 function __iter(self): (any, number) -> (number, any)
488480end
489481
490- export type OpenCloudModel = any
491- export type ClipEvaluator = any
492-
493482export type RBXScriptSignal<T... = ...any> = {
494483 Wait: (self: RBXScriptSignal<T...>) -> T...,
495484 Connect: (self: RBXScriptSignal<T...>, callback: (T...) -> ()) -> RBXScriptConnection,
668657)
669658
670659ApiPropertySecurityLevel = TypedDict (
671- "ApiPropertySecurityLevel" ,
660+ "ApiPropertySecurityLevel" ,
672661 {
673662 "Read" : ApiSecurityLevel ,
674663 "Write" : ApiSecurityLevel
788777# Cache for looking up members by name when resolving deprecations
789778classesWithMemberName : dict [str , List [ApiClass ]] = {}
790779
791- # Keep track of declared Luau types as a failsafe if we need to declare them.
792- # This needs to be kept in-sync with any Luau type declarations
793- # added in EXTRA_MEMBERS
794-
795- declaredLuauTypes : Set [str ] = {
780+ # Types referenced in the API dump / EXTRA_MEMBERS / Corrections that we need to track for auto-declaration.
781+ # Seeded with types from EXTRA_MEMBERS that bypass resolveType().
782+ referenced_types : Set [str ] = {
796783 "ReviewableContentEvent" ,
797784 "AutoSetupParams" ,
798785 "CaptureParams" ,
799786 "VideoSample" ,
800787}
801788
789+ LUAU_PRIMITIVES = {"string" , "number" , "boolean" , "nil" , "any" , "unknown" , "never" , "buffer" , "thread" , "vector" }
790+
791+ # All types that are defined (populated incrementally during generation).
792+ # Seeded with static sources; classes, datatypes, and enums are added during printing.
793+ def extractDefinedNames (text : str ) -> Set [str ]:
794+ """Extract type/class/declare names from Luau definition text."""
795+ names : Set [str ] = set ()
796+ for m in re .finditer (r'(?:export\s+)?type\s+(\w+)' , text ):
797+ names .add (m .group (1 ))
798+ for m in re .finditer (r'declare\s+class\s+(\w+)' , text ):
799+ names .add (m .group (1 ))
800+ for m in re .finditer (r'declare\s+(\w+)\s*:' , text ):
801+ names .add (m .group (1 ))
802+ return names
803+
804+ def _seed_defined_types () -> Set [str ]:
805+ types : Set [str ] = set ()
806+ types .update (LUAU_PRIMITIVES )
807+ types .update (TYPE_INDEX .values ())
808+ types .update (IGNORED_INSTANCES )
809+ for base in [START_BASE , POST_DATATYPES_BASE , END_BASE ]:
810+ types .update (extractDefinedNames (base ))
811+ return types
812+
813+ defined_types : Set [str ] = _seed_defined_types ()
814+
802815def isIdentifier (name : str ):
803- return re .match (r"^[a-zA-Z_]+[a-zA-Z_0-9] *$" , name ) # TODO: 'function'
816+ return re .match (r"^[A-Za-z_]\w *$" , name )
804817
805818def escapeName (name : str ):
806819 """Escape a name string to be property-compatible"""
@@ -845,7 +858,10 @@ def resolveType(type: Union[ApiValueType, CorrectionsValueType]) -> str:
845858 if category == "Enum" :
846859 return "Enum" + name
847860 else :
848- return TYPE_INDEX [name ] if name in TYPE_INDEX else name
861+ resolved = TYPE_INDEX [name ] if name in TYPE_INDEX else name
862+ if resolved not in LUAU_PRIMITIVES and isIdentifier (resolved ):
863+ referenced_types .add (resolved )
864+ return resolved
849865
850866
851867def resolveParameter (param : ApiParameter ):
@@ -874,15 +890,15 @@ def resolveReturnType(member: Union[ApiFunction, ApiCallback]) -> str:
874890 return "(" + ", " .join (types ) + ")"
875891 elif member ["ReturnType" ] is not None :
876892 return resolveType (member ["ReturnType" ])
877-
893+
878894 return "nil"
879895
880896def resolveDeprecation (member : ApiMember , klass : ApiClass | DataType ) -> str :
881897 tags : Optional [List [Union [str , ApiDeprecatedInfo ]]] = None
882-
898+
883899 if "Tags" in member :
884900 tags = member ["Tags" ]
885-
901+
886902 result = ""
887903
888904 if tags is not None :
@@ -915,7 +931,7 @@ def resolveDeprecation(member: ApiMember, klass: ApiClass | DataType) -> str:
915931 if member ["Name" ] == preferred :
916932 bestMember = member
917933 break
918-
934+
919935 if bestMember is not None :
920936 # Use the classname and member name, we found a different class to point to!
921937 result = f"@[deprecated {{use = \" { bestClass ['Name' ]} { ':' if bestMember ['MemberType' ] == 'Function' else '.' } { preferred } \" }}]\n \t \t "
@@ -951,15 +967,15 @@ def filterMember(klassName: str, member: ApiMember):
951967 and member ["MemberType" ] != "Function"
952968 ):
953969 return False
954-
970+
955971 if ("Tags" in member and
956972 member ["Tags" ] is not None
957973 and "NotScriptable" in member ["Tags" ]):
958974 return False
959-
975+
960976 if member ["Name" ] in classIgnoredMembers (klassName ):
961977 return False
962-
978+
963979
964980 if "Security" in member :
965981 if isinstance (member ["Security" ], str ):
@@ -981,6 +997,8 @@ def declareClass(klass: Union[ApiClass, DataType]) -> str:
981997 if klass ["Name" ] in IGNORED_INSTANCES :
982998 return ""
983999
1000+ defined_types .add (klass ["Name" ])
1001+
9841002 out = "declare class " + klass ["Name" ]
9851003 if "Superclass" in klass and klass ["Superclass" ] != "<<<ROOT>>>" :
9861004 out += " extends " + klass ["Superclass" ]
@@ -1028,6 +1046,7 @@ def printEnums(dump: ApiDump):
10281046 # Declare each enum individually
10291047 out = ""
10301048 for enum , items in enums .items ():
1049+ defined_types .add ("Enum" + enum )
10311050 # Declare an atom for the enum
10321051 out += f"declare class Enum{ enum } extends EnumItem end\n "
10331052 out += f"declare class Enum{ enum } _INTERNAL extends Enum\n "
@@ -1071,6 +1090,7 @@ def printDataTypeConstructors(types: DataTypesDump):
10711090 if klass ["Name" ] in IGNORED_INSTANCES :
10721091 continue
10731092 name = klass ["Name" ]
1093+ defined_types .add (name )
10741094 members = klass ["Members" ]
10751095
10761096 isBrickColorNew = False
@@ -1127,6 +1147,14 @@ def printDataTypeConstructors(types: DataTypesDump):
11271147 print ()
11281148
11291149
1150+ def printUndefinedTypeStubs ():
1151+ undefined = sorted (referenced_types - defined_types )
1152+ if undefined :
1153+ for name in undefined :
1154+ print (f"type { name } = any" )
1155+ print ()
1156+
1157+
11301158def applyCorrections (dump : ApiDump , corrections : CorrectionsDump ):
11311159 for klass in corrections ["Classes" ]:
11321160 for otherClass in dump ["Classes" ]:
@@ -1194,7 +1222,7 @@ def loadMembersIntoStructures(klass: ApiClass):
11941222 classesWithMemberName [name ] = [klass ]
11951223 else :
11961224 classesWithMemberName [name ].append (klass )
1197-
1225+
11981226
11991227def loadClassesIntoStructures (dump : ApiDump ):
12001228 for klass in dump ["Classes" ]:
@@ -1216,22 +1244,22 @@ def loadClassesIntoStructures(dump: ApiDump):
12161244def registerDeclaredInType (type : CorrectionsValueType | None ):
12171245 if type is not None :
12181246 if "Declared" in type and type ["Declared" ] is not None :
1219- if type ["Declared" ] not in declaredLuauTypes :
1247+ if type ["Declared" ] not in referenced_types :
12201248 declaredType = type ["Declared" ]
12211249
12221250 if declaredType .endswith ("?" ):
12231251 declaredType = declaredType [:- 1 ]
12241252
1225- if re . match ( "^[A-z0-9_]+$" , declaredType ):
1226- declaredLuauTypes .add (declaredType )
1253+ if isIdentifier ( declaredType ):
1254+ referenced_types .add (declaredType )
12271255
12281256def registerDeclared (dump : CorrectionsDump ):
12291257 for klass in dump ["Classes" ]:
12301258 for member in klass ["Members" ]:
12311259 if "TupleReturns" in member and member ["TupleReturns" ] is not None :
12321260 for ret in member ["TupleReturns" ]:
12331261 registerDeclaredInType (ret )
1234-
1262+
12351263 if "ReturnType" in member :
12361264 if isinstance (member ["ReturnType" ], list ):
12371265 for ret in member ["ReturnType" ]:
@@ -1243,7 +1271,7 @@ def registerDeclared(dump: CorrectionsDump):
12431271 if "ValueType" in member :
12441272 value = member ["ValueType" ]
12451273 registerDeclaredInType (value )
1246-
1274+
12471275 if "Parameters" in member and member ["Parameters" ] is not None :
12481276 for param in member ["Parameters" ]:
12491277 if "Type" in param :
@@ -1268,7 +1296,7 @@ def printLuauTypes():
12681296
12691297 while not luauLines [0 ].startswith ("-- SECTION BEGIN:" ):
12701298 luauLines .pop (0 )
1271-
1299+
12721300 # Begin capturing sections from SECTION BEGIN to SECTION END
12731301 luauSections : dict [str , list [str ]] = dict ()
12741302 sectionNames : list [str ] = []
@@ -1283,12 +1311,12 @@ def printLuauTypes():
12831311 if currentSection is not None :
12841312 luauSections [sectionName ] = currentSection
12851313 currentSection = None
1286-
1314+
12871315 sectionNames .append (sectionName )
12881316 elif currentSection is not None :
12891317 if line in LUAU_SNIPPET_PATCHES .keys ():
12901318 line = LUAU_SNIPPET_PATCHES [line ]
1291-
1319+
12921320 line = line .replace ("Enum." , "Enum" )
12931321 currentSection .append (line )
12941322
@@ -1302,27 +1330,17 @@ def printLuauTypes():
13021330
13031331 for line in sectionLines :
13041332 writtenLines .add (line )
1305-
1333+
13061334 luauTypes += "\n " .join (sectionLines ) + "\n "
1307-
1335+
13081336 # Fail-safe: Append any patches that were not written
13091337 for patch in LUAU_SNIPPET_PATCHES .values ():
13101338 if patch not in writtenLines :
13111339 luauTypes += patch + "\n "
13121340
1313- # Fail-safe: Declare any missing types that were marked as declared
1314- for declaredType in declaredLuauTypes :
1315- declaration = f"type { declaredType } = any"
1316- found = False
1317-
1318- for line in writtenLines :
1319- if line .find (declaredType ) != - 1 :
1320- found = True
1321- break
1341+ # Extract type/class names defined in LuauTypes for use by printUndefinedTypeStubs
1342+ defined_types .update (extractDefinedNames (luauTypes ))
13221343
1323- if not found :
1324- luauTypes += declaration + "\n "
1325-
13261344 print (luauTypes )
13271345
13281346# Load BrickColors
@@ -1349,4 +1367,5 @@ def printLuauTypes():
13491367printLuauTypes ()
13501368printClasses (dump )
13511369printDataTypeConstructors (dataTypes )
1370+ printUndefinedTypeStubs ()
13521371print (END_BASE )
0 commit comments