cluaupp 1.5.2 → 2.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +29 -41
  3. package/api/generated/bench-latest.json +43 -21
  4. package/api/generated/lsp-index.json +1 -1
  5. package/api/manifests/roblox-target.manifest.json +5 -5
  6. package/api/normalized/roblox-target.profile.json +8729 -267
  7. package/api/roblox/luau-builtins.d.luau +185 -0
  8. package/api/roblox/surface.json +1 -0
  9. package/api/roblox-api.lock.json +11 -2
  10. package/bin/cluaupp.js +38 -0
  11. package/editors/vscode/README.md +1 -1
  12. package/editors/vscode/package.json +50 -15
  13. package/editors/vscode/snippets/flare.json +1 -1
  14. package/editors/vscode/snippets/hive.json +1 -1
  15. package/editors/vscode/syntaxes/clpp.tmLanguage.json +211 -0
  16. package/examples/README.md +9 -9
  17. package/examples/game/cluaupp.config.json +2 -2
  18. package/examples/game/default.project.json +4 -15
  19. package/examples/game/out/ReplicatedStorage/Shared/GameInfo.luau +8 -0
  20. package/examples/game/out/ReplicatedStorage/Shared/GameInfo.luau.map.json +13 -0
  21. package/examples/game/out/ServerScriptService/Leaderstats.server.luau +26 -0
  22. package/examples/game/out/ServerScriptService/Leaderstats.server.luau.map.json +13 -0
  23. package/examples/game/out/ServerScriptService/Main.server.luau +15 -0
  24. package/examples/game/out/ServerScriptService/Main.server.luau.map.json +13 -0
  25. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Main.client.luau +10 -0
  26. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Main.client.luau.map.json +13 -0
  27. package/examples/game/src/ReplicatedStorage/Shared/GameInfo.clp +1 -0
  28. package/examples/game/src/ServerScriptService/Leaderstats.server.clp +18 -0
  29. package/examples/game/src/ServerScriptService/Main.server.clp +9 -0
  30. package/examples/game/src/StarterPlayer/StarterPlayerScripts/Main.client.clp +4 -0
  31. package/generated/api/build-profile.js +1 -1
  32. package/generated/api/loader.js +2 -2
  33. package/generated/api/surface/build.js +328 -0
  34. package/generated/api/surface/luau-defs.js +595 -0
  35. package/generated/api/surface/model.js +8 -0
  36. package/generated/api-cli.js +2 -2
  37. package/generated/cli.js +18 -17
  38. package/generated/clpp/contract.js +6 -3
  39. package/generated/clpp/handshake.js +36 -0
  40. package/generated/clpp/modules.js +20 -9
  41. package/generated/clpp/runner.js +62 -31
  42. package/generated/compile.js +18 -1
  43. package/generated/compiler/ast.js +3 -0
  44. package/generated/compiler/checker/calls.js +999 -0
  45. package/generated/compiler/checker/checker.js +1215 -0
  46. package/generated/compiler/checker/exprs.js +791 -0
  47. package/generated/compiler/checker/flow.js +168 -0
  48. package/generated/compiler/checker/members.js +763 -0
  49. package/generated/compiler/checker/statements.js +792 -0
  50. package/generated/compiler/checker/symbols.js +90 -0
  51. package/generated/compiler/checker/typeops.js +348 -0
  52. package/generated/compiler/checker/yields.js +115 -0
  53. package/generated/compiler/diagnostics.js +138 -0
  54. package/generated/compiler/emit/emitter.js +2356 -0
  55. package/generated/compiler/emit/luau-types.js +137 -0
  56. package/generated/compiler/emit/names.js +130 -0
  57. package/generated/compiler/emit/runtime.js +432 -0
  58. package/generated/compiler/lexer.js +403 -0
  59. package/generated/compiler/parser.js +1881 -0
  60. package/generated/compiler/project.js +332 -0
  61. package/generated/compiler/roblox/api.js +388 -0
  62. package/generated/compiler/roblox/context.js +100 -0
  63. package/generated/compiler/source.js +59 -0
  64. package/generated/compiler/stdlib.js +232 -0
  65. package/generated/compiler/types.js +250 -0
  66. package/generated/intellisense.js +1 -15
  67. package/generated/lsp.js +1 -1
  68. package/generated/target/build-cache.js +4 -4
  69. package/generated/target/datamodel-generate.js +12 -0
  70. package/generated/target/gen-rojo-tree.js +15 -0
  71. package/generated/target/source-map.js +61 -8
  72. package/generated/target/studio-bridge.js +1 -0
  73. package/generated/utils/project.js +104 -31
  74. package/include/README.md +5 -0
  75. package/native/bin/.gitkeep +1 -0
  76. package/package.json +17 -6
  77. package/plugins/CluauppNav/Plugin.luau +28 -4
  78. package/rust/cluaupp/Cargo.lock +795 -0
  79. package/rust/cluaupp/Cargo.toml +17 -0
  80. package/rust/cluaupp/src/main.rs +717 -0
  81. package/scripts/postinstall-native.js +56 -0
  82. package/skills/README.md +3 -3
  83. package/skills/clpp-hive-flare/SKILL.md +30 -26
  84. package/skills/clpp-language/SKILL.md +67 -74
  85. package/skills/clpp-language/compiler.md +24 -36
  86. package/skills/clpp-style/SKILL.md +31 -44
  87. package/src/api/build-profile.ts +1 -1
  88. package/src/api/loader.ts +2 -2
  89. package/src/api/surface/build.ts +374 -0
  90. package/src/api/surface/luau-defs.ts +635 -0
  91. package/src/api/surface/model.ts +96 -0
  92. package/src/api-cli.ts +2 -2
  93. package/src/cli.ts +19 -18
  94. package/src/clpp/contract.ts +20 -3
  95. package/src/clpp/handshake.ts +25 -0
  96. package/src/clpp/modules.ts +17 -10
  97. package/src/clpp/runner.ts +52 -34
  98. package/src/compile.ts +20 -2
  99. package/src/compiler/ast.ts +336 -0
  100. package/src/compiler/checker/calls.ts +1025 -0
  101. package/src/compiler/checker/checker.ts +1352 -0
  102. package/src/compiler/checker/exprs.ts +805 -0
  103. package/src/compiler/checker/flow.ts +174 -0
  104. package/src/compiler/checker/members.ts +793 -0
  105. package/src/compiler/checker/statements.ts +802 -0
  106. package/src/compiler/checker/symbols.ts +219 -0
  107. package/src/compiler/checker/typeops.ts +355 -0
  108. package/src/compiler/checker/yields.ts +120 -0
  109. package/src/compiler/diagnostics.ts +163 -0
  110. package/src/compiler/emit/emitter.ts +2486 -0
  111. package/src/compiler/emit/luau-types.ts +143 -0
  112. package/src/compiler/emit/names.ts +130 -0
  113. package/src/compiler/emit/runtime.ts +438 -0
  114. package/src/compiler/lexer.ts +444 -0
  115. package/src/compiler/parser.ts +1943 -0
  116. package/src/compiler/project.ts +354 -0
  117. package/src/compiler/roblox/api.ts +408 -0
  118. package/src/compiler/roblox/context.ts +108 -0
  119. package/src/compiler/source.ts +67 -0
  120. package/src/compiler/stdlib.ts +256 -0
  121. package/src/compiler/types.ts +313 -0
  122. package/src/intellisense.ts +1 -13
  123. package/src/lsp.ts +1 -1
  124. package/src/target/build-cache.ts +5 -8
  125. package/src/target/datamodel-generate.ts +12 -1
  126. package/src/target/gen-rojo-tree.ts +15 -0
  127. package/src/target/source-map.ts +65 -27
  128. package/src/target/studio-bridge.ts +3 -0
  129. package/src/types.ts +3 -0
  130. package/src/utils/project.ts +99 -37
  131. package/templates/README.md +7 -0
  132. package/templates/{game → game-modern}/cluaupp.config.json +2 -2
  133. package/templates/{game → game-modern}/package.json +1 -2
  134. package/templates/game-modern/rokit.toml +2 -0
  135. package/templates/game-modern/src/ReplicatedStorage/Shared/GameInfo.clp +1 -0
  136. package/templates/game-modern/src/ServerScriptService/Handlers/Guard.server.clp +4 -0
  137. package/templates/game-modern/src/ServerScriptService/Main.server.clp +8 -0
  138. package/templates/game-modern/src/StarterPlayer/StarterPlayerScripts/Main.client.clp +4 -0
  139. package/examples/game/out/ReplicatedStorage/Shared/Constants/Datas/TemplateData.luau +0 -29
  140. package/examples/game/out/ReplicatedStorage/Shared/Constants/Datas/TemplateData.luau.map.json +0 -13
  141. package/examples/game/out/ReplicatedStorage/Shared/Net/Net.luau +0 -83
  142. package/examples/game/out/ReplicatedStorage/Shared/Net/Net.luau.map.json +0 -13
  143. package/examples/game/out/ServerScriptService/Boot/DataBoot.server.luau +0 -18
  144. package/examples/game/out/ServerScriptService/Boot/DataBoot.server.luau.map.json +0 -13
  145. package/examples/game/out/ServerScriptService/Configuration/ServerConfig.luau +0 -14
  146. package/examples/game/out/ServerScriptService/Configuration/ServerConfig.luau.map.json +0 -13
  147. package/examples/game/out/ServerScriptService/Handlers/Guard.server.luau +0 -15
  148. package/examples/game/out/ServerScriptService/Handlers/Guard.server.luau.map.json +0 -13
  149. package/examples/game/out/ServerScriptService/Handlers/PlayerHandler.server.luau +0 -50
  150. package/examples/game/out/ServerScriptService/Handlers/PlayerHandler.server.luau.map.json +0 -13
  151. package/examples/game/out/StarterPlayer/StarterCharacterScripts/Character/CharacterReady.client.luau +0 -12
  152. package/examples/game/out/StarterPlayer/StarterCharacterScripts/Character/CharacterReady.client.luau.map.json +0 -13
  153. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Controllers/DataController.client.luau +0 -16
  154. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Controllers/DataController.client.luau.map.json +0 -13
  155. package/examples/game/src/Client/Controllers/DataController.client.clpp +0 -7
  156. package/examples/game/src/Client/Main.client.clpp +0 -4
  157. package/examples/game/src/Declarations/Net.flare +0 -5
  158. package/examples/game/src/Include/GameTypes.clh +0 -40
  159. package/examples/game/src/Modules/Core/Guard.server.clpp +0 -7
  160. package/examples/game/src/Modules/Core/PlayerHandler.server.clpp +0 -23
  161. package/examples/game/src/Server/Boot/DataBoot.server.clpp +0 -13
  162. package/examples/game/src/Server/Main.server.clpp +0 -4
  163. package/templates/game/.clangd +0 -25
  164. package/templates/game/.vscode/extensions.json +0 -8
  165. package/templates/game/.vscode/settings.json +0 -17
  166. package/templates/game/Packages/.gitkeep +0 -0
  167. package/templates/game/compile_flags.txt +0 -6
  168. package/templates/game/default.project.json +0 -41
  169. package/templates/game/include/clpp/datatypes.clh +0 -575
  170. package/templates/game/include/clpp/generated/enums.clh +0 -4920
  171. package/templates/game/include/clpp/generated/instances.clh +0 -87182
  172. package/templates/game/include/clpp/libs/arrayindexer.clh +0 -12
  173. package/templates/game/include/clpp/libs/axiom.clh +0 -154
  174. package/templates/game/include/clpp/libs/bloom.clh +0 -58
  175. package/templates/game/include/clpp/libs/coil.clh +0 -37
  176. package/templates/game/include/clpp/libs/crest.clh +0 -22
  177. package/templates/game/include/clpp/libs/echo.clh +0 -52
  178. package/templates/game/include/clpp/libs/ember.clh +0 -17
  179. package/templates/game/include/clpp/libs/flare.clh +0 -65
  180. package/templates/game/include/clpp/libs/gleam.clh +0 -111
  181. package/templates/game/include/clpp/libs/guide.clh +0 -42
  182. package/templates/game/include/clpp/libs/helm.clh +0 -31
  183. package/templates/game/include/clpp/libs/hive.clh +0 -20
  184. package/templates/game/include/clpp/libs/keep.clh +0 -158
  185. package/templates/game/include/clpp/libs/lens.clh +0 -24
  186. package/templates/game/include/clpp/libs/mint.clh +0 -28
  187. package/templates/game/include/clpp/libs/net.clh +0 -31
  188. package/templates/game/include/clpp/libs/occlude.clh +0 -15
  189. package/templates/game/include/clpp/libs/pin.clh +0 -28
  190. package/templates/game/include/clpp/libs/promise.clh +0 -24
  191. package/templates/game/include/clpp/libs/roster.clh +0 -123
  192. package/templates/game/include/clpp/libs/shift.clh +0 -22
  193. package/templates/game/include/clpp/libs/spark.clh +0 -28
  194. package/templates/game/include/clpp/libs/stage.clh +0 -23
  195. package/templates/game/include/clpp/libs/sweep.clh +0 -23
  196. package/templates/game/include/clpp/libs/trace.clh +0 -18
  197. package/templates/game/include/clpp/libs/ward.clh +0 -19
  198. package/templates/game/include/clpp/libs.clh +0 -31
  199. package/templates/game/include/clpp/roblox.clh +0 -41
  200. package/templates/game/rokit.toml +0 -5
  201. package/templates/game/src/Client/Controllers/DataController.client.clpp +0 -7
  202. package/templates/game/src/Client/Main.client.clpp +0 -4
  203. package/templates/game/src/Declarations/Net.flare +0 -5
  204. package/templates/game/src/Include/GameTypes.clh +0 -40
  205. package/templates/game/src/Modules/Core/Guard.server.clpp +0 -7
  206. package/templates/game/src/Modules/Core/PlayerHandler.server.clpp +0 -24
  207. package/templates/game/src/Server/Boot/DataBoot.server.clpp +0 -13
  208. package/templates/game/src/Server/Main.server.clpp +0 -4
  209. package/templates/game/stylua.toml +0 -9
  210. package/templates/game/wally.toml +0 -12
@@ -22,7 +22,7 @@ function loadJsonFile(id, filePath) {
22
22
  meta: {
23
23
  id,
24
24
  path: absolute,
25
- hash: (0, resolver_js_1.hashBuffer)(raw),
25
+ hash: (0, resolver_js_1.hashBuffer)(text.replace(/\r\n/g, "\n")),
26
26
  loadedAt: new Date().toISOString(),
27
27
  version: typeof data.Version !== "undefined"
28
28
  ? data.Version
@@ -51,7 +51,7 @@ function overridesHash() {
51
51
  if (!name.endsWith(".json")) {
52
52
  continue;
53
53
  }
54
- parts.push(name, node_fs_1.default.readFileSync(node_path_1.default.join(dir, name), "utf8"));
54
+ parts.push(name, node_fs_1.default.readFileSync(node_path_1.default.join(dir, name), "utf8").replace(/\r\n/g, "\n"));
55
55
  }
56
56
  return (0, resolver_js_1.hashBuffer)(parts.join("\n"));
57
57
  }
@@ -0,0 +1,328 @@
1
+ "use strict";
2
+ /**
3
+ * Builds `api/roblox/surface.json` from:
4
+ * 1. luau-lsp Roblox definitions (`globalTypes.*.d.luau`) — types, nullability, overloads, operators;
5
+ * 2. `api/roblox/luau-builtins.d.luau` — math, string, table, coroutine, bit32, buffer, os, vector, globals;
6
+ * 3. the Roblox API dump profile (`api/normalized/roblox-target.profile.json`) — security, read-only,
7
+ * creatable/service, deprecation tags and integer-typed members.
8
+ *
9
+ * Run with `cluaupp api surface --defs <globalTypes.d.luau>`.
10
+ */
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.buildSurface = buildSurface;
16
+ exports.writeSurface = writeSurface;
17
+ exports.findLuauLspDefinitions = findLuauLspDefinitions;
18
+ const node_fs_1 = __importDefault(require("node:fs"));
19
+ const node_path_1 = __importDefault(require("node:path"));
20
+ const luau_defs_js_1 = require("./luau-defs.js");
21
+ const package_info_js_1 = require("../../package-info.js");
22
+ const INT_PRIMITIVES = new Set(["int", "int64", "int32", "int16", "int8"]);
23
+ function hasTag(m, tag) {
24
+ return (m.tags || []).some((t) => t === tag);
25
+ }
26
+ function isSignal(t) {
27
+ return !!t && t.k === "name" && t.name === "RBXScriptSignal";
28
+ }
29
+ function intify(t) {
30
+ // keep the union wide: callers store the result back into an LType slot
31
+ if (t.k === "name" && t.name === "number") {
32
+ return { k: "name", name: "int" };
33
+ }
34
+ if (t.k === "opt") {
35
+ return { k: "opt", t: intify(t.t) };
36
+ }
37
+ return t;
38
+ }
39
+ function security(level) {
40
+ return level && level !== "None" ? level : undefined;
41
+ }
42
+ function convertMembers(ext, profileClass) {
43
+ const members = {};
44
+ const pProps = new Map((profileClass?.properties || []).map((p) => [p.name, p]));
45
+ const pMethods = new Map();
46
+ for (const m of profileClass?.methods || []) {
47
+ const list = pMethods.get(m.name) || [];
48
+ list.push(m);
49
+ pMethods.set(m.name, list);
50
+ }
51
+ const pEvents = new Map((profileClass?.events || []).map((p) => [p.name, p]));
52
+ const pCallbacks = new Map((profileClass?.callbacks || []).map((p) => [p.name, p]));
53
+ for (const m of ext.members) {
54
+ if (m.fn) {
55
+ if (m.name.startsWith("__")) {
56
+ continue;
57
+ }
58
+ const existing = members[m.name];
59
+ if (existing && existing.kind === "method") {
60
+ existing.overloads.push(m.fn);
61
+ continue;
62
+ }
63
+ const pm = pMethods.get(m.name)?.[0];
64
+ const member = { kind: "method", overloads: [m.fn] };
65
+ if (pm) {
66
+ const ret = pm.returnTypes?.[0];
67
+ if (ret && ret.kind === "Primitive" && INT_PRIMITIVES.has(ret.name || "") && m.fn.returns.length === 1) {
68
+ m.fn.returns[0] = intify(m.fn.returns[0]);
69
+ }
70
+ const sec = security(pm.security);
71
+ if (sec) {
72
+ member.security = sec;
73
+ }
74
+ if (pm.canYield || hasTag(pm, "Yields") || hasTag(pm, "CanYield")) {
75
+ member.yields = true;
76
+ }
77
+ if (hasTag(pm, "NotScriptable")) {
78
+ member.notScriptable = true;
79
+ }
80
+ }
81
+ else if (/Async$/.test(m.name)) {
82
+ member.yields = true;
83
+ }
84
+ if (m.deprecated) {
85
+ member.deprecated = m.deprecated;
86
+ }
87
+ members[m.name] = member;
88
+ continue;
89
+ }
90
+ const type = m.type;
91
+ if (isSignal(type)) {
92
+ const args = (type.args || []).filter((a) => !(a.k === "name" && a.name === "()"));
93
+ const member = { kind: "event", args };
94
+ const pe = pEvents.get(m.name);
95
+ const sec = security(pe?.security);
96
+ if (sec) {
97
+ member.security = sec;
98
+ }
99
+ if (m.deprecated) {
100
+ member.deprecated = m.deprecated;
101
+ }
102
+ members[m.name] = member;
103
+ continue;
104
+ }
105
+ const pc = pCallbacks.get(m.name);
106
+ if (pc || (type.k === "fn" && !pProps.has(m.name))) {
107
+ const member = { kind: "callback", type };
108
+ const sec = security(pc?.security);
109
+ if (sec) {
110
+ member.security = sec;
111
+ }
112
+ members[m.name] = member;
113
+ continue;
114
+ }
115
+ const pp = pProps.get(m.name);
116
+ let propType = type;
117
+ if (pp?.valueType?.kind === "Primitive" && INT_PRIMITIVES.has(pp.valueType.name || "")) {
118
+ propType = intify(propType);
119
+ }
120
+ const member = { kind: "property", type: propType };
121
+ if (pp) {
122
+ if (pp.writable === false || hasTag(pp, "ReadOnly")) {
123
+ member.readonly = true;
124
+ }
125
+ const rs = security(pp.readSecurity);
126
+ const ws = security(pp.writeSecurity);
127
+ if (rs) {
128
+ member.security = rs;
129
+ }
130
+ if (ws) {
131
+ member.writeSecurity = ws;
132
+ }
133
+ if (hasTag(pp, "NotScriptable")) {
134
+ member.notScriptable = true;
135
+ }
136
+ if (hasTag(pp, "NotReplicated")) {
137
+ member.notReplicated = true;
138
+ }
139
+ }
140
+ if (m.deprecated) {
141
+ member.deprecated = m.deprecated;
142
+ }
143
+ members[m.name] = member;
144
+ }
145
+ return members;
146
+ }
147
+ function staticsFrom(type) {
148
+ const out = {};
149
+ const tables = type.k === "inter" ? type.ts : [type];
150
+ for (const t of tables) {
151
+ if (t.k !== "table") {
152
+ continue;
153
+ }
154
+ for (const [name, value] of Object.entries(t.fields)) {
155
+ if (value.k === "fn") {
156
+ out[name] = { kind: "function", overloads: [value] };
157
+ }
158
+ else if (value.k === "inter" && value.ts.every((x) => x.k === "fn")) {
159
+ out[name] = { kind: "function", overloads: value.ts };
160
+ }
161
+ else {
162
+ out[name] = { kind: "value", type: value };
163
+ }
164
+ }
165
+ }
166
+ return out;
167
+ }
168
+ function datatypeFrom(ext, statics) {
169
+ const operators = {};
170
+ for (const m of ext.members) {
171
+ if (m.fn && m.name.startsWith("__")) {
172
+ (operators[m.name] ||= []).push(m.fn);
173
+ }
174
+ }
175
+ return {
176
+ name: ext.name,
177
+ members: convertMembers(ext, undefined),
178
+ operators,
179
+ statics: statics ? staticsFrom(statics) : {},
180
+ };
181
+ }
182
+ function buildSurface(options) {
183
+ const defsText = node_fs_1.default.readFileSync(options.definitions, "utf8");
184
+ const builtinsPath = options.builtins || node_path_1.default.join(package_info_js_1.projectRoot, "api", "roblox", "luau-builtins.d.luau");
185
+ const profilePath = options.profile || node_path_1.default.join(package_info_js_1.projectRoot, "api", "normalized", "roblox-target.profile.json");
186
+ const roblox = (0, luau_defs_js_1.parseDefinitions)(defsText);
187
+ const builtins = (0, luau_defs_js_1.parseDefinitions)(node_fs_1.default.readFileSync(builtinsPath, "utf8"));
188
+ const profile = node_fs_1.default.existsSync(profilePath) ? JSON.parse(node_fs_1.default.readFileSync(profilePath, "utf8")) : { classes: {}, enums: {} };
189
+ return assemble(roblox, builtins, profile, {
190
+ definitions: node_path_1.default.basename(options.definitions),
191
+ builtins: node_path_1.default.basename(builtinsPath),
192
+ profile: node_fs_1.default.existsSync(profilePath) ? node_path_1.default.basename(profilePath) : "none",
193
+ });
194
+ }
195
+ function assemble(roblox, builtins, profile, sources) {
196
+ const surface = {
197
+ schema: 1,
198
+ generatedAt: new Date().toISOString().slice(0, 10),
199
+ sources,
200
+ classes: {},
201
+ datatypes: {},
202
+ enums: {},
203
+ globals: {},
204
+ libraries: {},
205
+ aliases: {},
206
+ };
207
+ const declared = new Map();
208
+ for (const d of [...roblox.declares, ...builtins.declares]) {
209
+ declared.set(d.name, d);
210
+ }
211
+ const externByName = new Map();
212
+ for (const ext of roblox.externs) {
213
+ externByName.set(ext.name, ext);
214
+ }
215
+ const isClass = (name) => {
216
+ let cur = name;
217
+ let guard = 0;
218
+ while (cur && guard++ < 64) {
219
+ if (cur === "Object" || cur === "Instance") {
220
+ return true;
221
+ }
222
+ cur = externByName.get(cur)?.extends ?? null;
223
+ }
224
+ return false;
225
+ };
226
+ const services = new Set(roblox.metadata.SERVICES || []);
227
+ const creatable = new Set(roblox.metadata.CREATABLE_INSTANCES || []);
228
+ for (const ext of roblox.externs) {
229
+ if (ext.name.startsWith("Enum") && ext.name.endsWith("_INTERNAL") && ext.extends === "Enum") {
230
+ const enumName = ext.name.slice(4, -"_INTERNAL".length);
231
+ const items = [];
232
+ const deprecated = [];
233
+ for (const m of ext.members) {
234
+ if (m.type) {
235
+ items.push(m.name);
236
+ if (m.deprecated) {
237
+ deprecated.push(m.name);
238
+ }
239
+ }
240
+ }
241
+ surface.enums[enumName] = { name: enumName, items, ...(deprecated.length ? { deprecated } : {}) };
242
+ continue;
243
+ }
244
+ if (ext.extends === "EnumItem" || ext.name === "Enum" || ext.name === "EnumItem") {
245
+ continue;
246
+ }
247
+ if (ext.name === "Object" || isClass(ext.extends) || ext.name === "Instance") {
248
+ const pc = profile.classes[ext.name];
249
+ const cls = {
250
+ name: ext.name,
251
+ superclass: ext.extends,
252
+ members: convertMembers(ext, pc),
253
+ creatable: creatable.has(ext.name),
254
+ service: services.has(ext.name),
255
+ };
256
+ if (pc?.creation?.abstract) {
257
+ cls.abstract = true;
258
+ }
259
+ if (pc && hasTag(pc, "Deprecated")) {
260
+ cls.deprecated = true;
261
+ }
262
+ if (pc && hasTag(pc, "NotCreatable")) {
263
+ cls.notCreatable = true;
264
+ }
265
+ surface.classes[ext.name] = cls;
266
+ continue;
267
+ }
268
+ surface.datatypes[ext.name] = datatypeFrom(ext, declared.get(ext.name)?.type);
269
+ }
270
+ // Datatype-like statics without an extern body (e.g. `declare Instance: {…}` is handled by the checker).
271
+ for (const [name, d] of declared) {
272
+ if (surface.datatypes[name]) {
273
+ continue;
274
+ }
275
+ if (["Instance", "Enum", "game", "workspace", "script", "plugin", "shared", "task", "debug", "utf8"].includes(name)) {
276
+ continue;
277
+ }
278
+ if (/^[A-Z]/.test(name) && d.type.k === "table" && !surface.classes[name]) {
279
+ surface.datatypes[name] = { name, members: {}, operators: {}, statics: staticsFrom(d.type) };
280
+ }
281
+ }
282
+ for (const [name, d] of declared) {
283
+ if (d.type.k === "table" && /^[a-z]/.test(name)) {
284
+ surface.libraries[name] = staticsFrom(d.type);
285
+ continue;
286
+ }
287
+ if (d.type.k === "fn") {
288
+ surface.globals[name] = { overloads: [d.type], ...(d.deprecated ? { deprecated: d.deprecated } : {}) };
289
+ continue;
290
+ }
291
+ if (["game", "workspace", "script", "plugin", "shared", "Enum"].includes(name)) {
292
+ surface.globals[name] = { type: d.type };
293
+ }
294
+ }
295
+ surface.globals.Instance = { type: declared.get("Instance").type };
296
+ for (const [name, t] of Object.entries(roblox.aliases)) {
297
+ if (name === "ENUM_LIST" || name.startsWith("Enum")) {
298
+ continue;
299
+ }
300
+ surface.aliases[name] = t;
301
+ }
302
+ return surface;
303
+ }
304
+ function writeSurface(surface, dest = node_path_1.default.join(package_info_js_1.projectRoot, "api", "roblox", "surface.json")) {
305
+ node_fs_1.default.mkdirSync(node_path_1.default.dirname(dest), { recursive: true });
306
+ node_fs_1.default.writeFileSync(dest, `${JSON.stringify(surface)}\n`, "utf8");
307
+ return dest;
308
+ }
309
+ /** Default location of the luau-lsp definitions downloaded by the VS Code extension. */
310
+ function findLuauLspDefinitions() {
311
+ const roots = [
312
+ process.env.APPDATA && node_path_1.default.join(process.env.APPDATA, "Code", "User", "globalStorage", "johnnymorganz.luau-lsp"),
313
+ process.env.APPDATA && node_path_1.default.join(process.env.APPDATA, "Cursor", "User", "globalStorage", "johnnymorganz.luau-lsp"),
314
+ process.env.HOME && node_path_1.default.join(process.env.HOME, ".config", "Code", "User", "globalStorage", "johnnymorganz.luau-lsp"),
315
+ process.env.HOME && node_path_1.default.join(process.env.HOME, "Library", "Application Support", "Code", "User", "globalStorage", "johnnymorganz.luau-lsp"),
316
+ ].filter(Boolean);
317
+ for (const root of roots) {
318
+ if (!node_fs_1.default.existsSync(root)) {
319
+ continue;
320
+ }
321
+ const files = node_fs_1.default.readdirSync(root).filter((f) => /^globalTypes.*\.d\.luau$/.test(f));
322
+ const preferred = files.find((f) => /PluginSecurity/.test(f)) || files[0];
323
+ if (preferred) {
324
+ return node_path_1.default.join(root, preferred);
325
+ }
326
+ }
327
+ return null;
328
+ }