cluaupp 0.1.5 → 0.2.0

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 (101) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +6 -1
  3. package/bin/cluau.js +1 -1
  4. package/bin/cluaupp.js +1 -1
  5. package/docs/architecture.md +26 -1
  6. package/docs/cli.md +27 -6
  7. package/docs/examples/index.md +2 -0
  8. package/docs/getting-started.md +5 -4
  9. package/docs/intellisense.md +49 -31
  10. package/docs/intro.md +1 -1
  11. package/editors/vscode/extension.js +97 -146
  12. package/editors/vscode/package.json +6 -6
  13. package/examples/README.md +18 -0
  14. package/examples/game/.clangd +25 -0
  15. package/examples/game/.vscode/c_cpp_properties.json +27 -0
  16. package/examples/game/.vscode/extensions.json +5 -0
  17. package/examples/game/.vscode/settings.json +27 -0
  18. package/examples/game/Packages/.gitkeep +0 -0
  19. package/examples/game/cluaupp.config.json +6 -0
  20. package/examples/game/compile_flags.txt +6 -0
  21. package/examples/game/default.project.json +39 -0
  22. package/examples/game/package.json +9 -0
  23. package/examples/game/rokit.toml +5 -0
  24. package/examples/game/src/client/hud.client.cpp +12 -0
  25. package/{src → examples/game/src}/client/init.client.cpp +1 -0
  26. package/examples/game/src/server/combat.server.cpp +23 -0
  27. package/{src → examples/game/src}/server/leaderstats.server.cpp +1 -0
  28. package/examples/game/wally.toml +11 -0
  29. package/generated/api.js +51 -0
  30. package/generated/architecture.js +473 -0
  31. package/generated/ast.js +2 -0
  32. package/generated/cli.js +191 -0
  33. package/generated/compile.js +115 -0
  34. package/generated/editor-install.js +170 -0
  35. package/generated/emit.js +503 -0
  36. package/generated/emitter/luau-codegen.js +167 -0
  37. package/generated/emitter/translators.js +164 -0
  38. package/generated/headers.js +220 -0
  39. package/generated/index.html +49 -0
  40. package/generated/intellisense.js +277 -0
  41. package/generated/layout.js +112 -0
  42. package/generated/lex.js +146 -0
  43. package/generated/libs.js +484 -0
  44. package/generated/lsp.js +55 -0
  45. package/generated/package-info.js +11 -0
  46. package/generated/parse.js +607 -0
  47. package/generated/parser/collector.js +100 -0
  48. package/generated/parser/index.js +30 -0
  49. package/generated/preprocess.js +181 -0
  50. package/generated/system-understander.js +443 -0
  51. package/generated/transpile.js +66 -0
  52. package/generated/types.js +2 -0
  53. package/generated/understand.js +298 -0
  54. package/generated/utils/process-orchestrator.js +63 -0
  55. package/generated/utils/project.js +491 -0
  56. package/generated/utils/rojo-mapper.js +181 -0
  57. package/generated/utils/safe-paths.js +104 -0
  58. package/package.json +25 -8
  59. package/src/api.ts +53 -0
  60. package/src/{architecture.js → architecture.ts} +9 -10
  61. package/src/ast.ts +37 -0
  62. package/src/cli.ts +214 -0
  63. package/src/compile.ts +118 -0
  64. package/src/editor-install.ts +182 -0
  65. package/src/{emit.js → emit.ts} +4 -5
  66. package/src/emitter/luau-codegen.ts +187 -0
  67. package/src/emitter/translators.ts +168 -0
  68. package/src/{headers.js → headers.ts} +4 -5
  69. package/src/intellisense.ts +286 -0
  70. package/src/{layout.js → layout.ts} +114 -129
  71. package/src/{lex.js → lex.ts} +4 -6
  72. package/src/{libs.js → libs.ts} +9 -4
  73. package/src/lsp.ts +60 -0
  74. package/src/package-info.ts +7 -0
  75. package/src/{parse.js → parse.ts} +3 -4
  76. package/src/parser/collector.ts +115 -0
  77. package/src/parser/index.ts +27 -0
  78. package/src/{preprocess.js → preprocess.ts} +22 -37
  79. package/src/system-understander.ts +501 -0
  80. package/src/transpile.ts +64 -0
  81. package/src/tree-sitter-cpp.d.ts +4 -0
  82. package/src/types.ts +81 -0
  83. package/src/{understand.js → understand.ts} +19 -210
  84. package/src/utils/process-orchestrator.ts +72 -0
  85. package/src/utils/project.ts +506 -0
  86. package/src/utils/rojo-mapper.ts +190 -0
  87. package/src/utils/safe-paths.ts +98 -0
  88. package/templates/game/.clangd +9 -0
  89. package/templates/game/.vscode/c_cpp_properties.json +1 -1
  90. package/templates/game/.vscode/extensions.json +5 -6
  91. package/templates/game/.vscode/settings.json +7 -6
  92. package/templates/game/compile_flags.txt +1 -0
  93. package/templates/game/rokit.toml +5 -0
  94. package/src/api.js +0 -57
  95. package/src/cli.js +0 -473
  96. package/src/compile.js +0 -110
  97. package/src/editor-install.js +0 -218
  98. package/src/intellisense.js +0 -1368
  99. package/src/lsp.js +0 -227
  100. /package/{src → examples/game/src}/shared/config.cpp +0 -0
  101. /package/{src → examples/game/src}/shared/config.h +0 -0
@@ -1,9 +1,14 @@
1
- "use strict";
2
-
3
- const path = require("path");
4
- const { luauType } = require("./api");
5
-
6
- const EXT = "(cpp|cc|cxx|c|h|hpp|hh)";
1
+ // @ts-nocheck
2
+ import path from "node:path";
3
+ import { luauType } from "./api.js";
4
+ import {
5
+ INTENTS,
6
+ walk,
7
+ namesIn,
8
+ parseFileTag,
9
+ scoreIntents,
10
+ determineArchitecture,
11
+ } from "./system-understander.js";
7
12
 
8
13
  const VALUE_CLASSES = {
9
14
  IntValue: { luau: "number", instance: "IntValue", fallback: "0" },
@@ -12,158 +17,7 @@ const VALUE_CLASSES = {
12
17
  BoolValue: { luau: "boolean", instance: "BoolValue", fallback: "false" },
13
18
  };
14
19
 
15
- // roblox-ts ScriptType is a filename key. Flamework then stamps
16
- // @Service / @Controller identifiers. Cluaupp uses the same first key
17
- // (.server / .client / .legacy.* / none) then scores AST intents.
18
- const INTENTS = {
19
- players: {
20
- role: "manager",
21
- module: "PlayersManager",
22
- tokens: ["GetPlayers", "PlayerAdded", "PlayerRemoving", "Players"],
23
- },
24
- cache: {
25
- role: "controller",
26
- module: "CacheController",
27
- tokens: ["Folder", "IntValue", "StringValue", "BoolValue", "NumberValue", "leaderstats"],
28
- },
29
- combat: {
30
- role: "controller",
31
- module: "CombatController",
32
- tokens: [
33
- "TakeDamage",
34
- "Humanoid",
35
- "Raycast",
36
- "RaycastParams",
37
- "Hitbox",
38
- "Health",
39
- "Damage",
40
- "Weapon",
41
- "Tool",
42
- "Activated",
43
- "Combat",
44
- "Hurt",
45
- "Attack",
46
- ],
47
- },
48
- input: {
49
- role: "controller",
50
- module: "InputController",
51
- tokens: [
52
- "UserInputService",
53
- "ContextActionService",
54
- "InputBegan",
55
- "InputEnded",
56
- "InputChanged",
57
- "Mouse",
58
- "GetMouse",
59
- "PreferredInput",
60
- ],
61
- },
62
- ui: {
63
- role: "controller",
64
- module: "ViewController",
65
- tokens: [
66
- "PlayerGui",
67
- "ScreenGui",
68
- "BillboardGui",
69
- "SurfaceGui",
70
- "TextLabel",
71
- "TextButton",
72
- "Frame",
73
- "ImageLabel",
74
- "TweenService",
75
- "Twinkle",
76
- ],
77
- },
78
- net: {
79
- role: "controller",
80
- module: "NetController",
81
- tokens: [
82
- "RemoteEvent",
83
- "RemoteFunction",
84
- "UnreliableRemoteEvent",
85
- "FireServer",
86
- "FireAllClients",
87
- "FireClient",
88
- "OnServerEvent",
89
- "OnClientEvent",
90
- "Net",
91
- "NetEvent",
92
- ],
93
- },
94
- data: {
95
- role: "controller",
96
- module: "DataController",
97
- tokens: [
98
- "DataStore",
99
- "DataStoreService",
100
- "DataService",
101
- "SetAsync",
102
- "GetAsync",
103
- "UpdateAsync",
104
- "ProfileStore",
105
- "GetChangedSignal",
106
- "WaitFor",
107
- "Paths",
108
- ],
109
- },
110
- character: {
111
- role: "controller",
112
- module: "CharacterController",
113
- tokens: ["CharacterAdded", "CharacterRemoving", "HumanoidRootPart", "GetPivot", "PivotTo", "Character"],
114
- },
115
- animation: {
116
- role: "controller",
117
- module: "AnimationController",
118
- tokens: ["Animator", "Animation", "AnimationTrack", "LoadAnimation"],
119
- },
120
- inventory: {
121
- role: "controller",
122
- module: "InventoryController",
123
- tokens: ["Backpack", "Inventory"],
124
- },
125
- };
126
-
127
- function walk(node, visit) {
128
- if (!node || typeof node !== "object") {
129
- return;
130
- }
131
- visit(node);
132
- for (const value of Object.values(node)) {
133
- if (Array.isArray(value)) {
134
- for (const item of value) {
135
- walk(item, visit);
136
- }
137
- } else if (value && typeof value === "object") {
138
- walk(value, visit);
139
- }
140
- }
141
- }
142
-
143
- function namesIn(ast, options = {}) {
144
- const found = new Set();
145
- walk(ast, (node) => {
146
- if (node.type === "call" && node.name) {
147
- found.add(node.name);
148
- }
149
- if (node.type === "member" && node.name) {
150
- found.add(node.name);
151
- }
152
- if (node.type === "ident" && node.name) {
153
- found.add(node.name);
154
- }
155
- if (node.type === "new" && node.className) {
156
- found.add(node.className);
157
- }
158
- if (node.type === "getService" && node.service) {
159
- found.add(node.service);
160
- }
161
- if (options.strings !== false && node.type === "string" && node.value) {
162
- found.add(node.value);
163
- }
164
- });
165
- return found;
166
- }
20
+ const EXT = "(cpp|cc|cxx|c|h|hpp|hh)";
167
21
 
168
22
  function capitalize(word) {
169
23
  if (!word) {
@@ -196,51 +50,6 @@ function toPascalServiceName(fileName) {
196
50
  return capitalize(base);
197
51
  }
198
52
 
199
- function parseFileTag(fileName) {
200
- const base = path.basename(fileName);
201
- if (new RegExp(`\\.legacy\\.plugin\\.${EXT}$`, "i").test(base)) {
202
- return { key: "legacy.plugin", emit: "legacy", runtime: "plugin", rojo: "Script", runContext: "Plugin" };
203
- }
204
- if (new RegExp(`\\.legacy\\.server\\.${EXT}$`, "i").test(base)) {
205
- return { key: "legacy.server", emit: "legacy", runtime: "server", rojo: "Script", runContext: null };
206
- }
207
- if (new RegExp(`\\.legacy\\.client\\.${EXT}$`, "i").test(base)) {
208
- return { key: "legacy.client", emit: "legacy", runtime: "client", rojo: "LocalScript", runContext: null };
209
- }
210
- if (new RegExp(`\\.legacy\\.${EXT}$`, "i").test(base)) {
211
- return { key: "legacy", emit: "legacy", runtime: "server", rojo: "Script", runContext: null };
212
- }
213
- if (new RegExp(`\\.plugin\\.${EXT}$`, "i").test(base)) {
214
- return { key: "plugin", emit: "service", runtime: "plugin", rojo: "Script", runContext: "Plugin" };
215
- }
216
- if (new RegExp(`\\.server\\.${EXT}$`, "i").test(base)) {
217
- return { key: "server", emit: "service", runtime: "server", rojo: "Script", runContext: "Server" };
218
- }
219
- if (new RegExp(`\\.client\\.${EXT}$`, "i").test(base)) {
220
- return { key: "client", emit: "service", runtime: "client", rojo: "LocalScript", runContext: "Client" };
221
- }
222
- return { key: "module", emit: "module", runtime: "shared", rojo: "ModuleScript", runContext: null };
223
- }
224
-
225
- function scoreIntents(named) {
226
- const scored = [];
227
- for (const [name, spec] of Object.entries(INTENTS)) {
228
- const evidence = spec.tokens.filter((token) => named.has(token));
229
- if (evidence.length === 0) {
230
- continue;
231
- }
232
- scored.push({
233
- name,
234
- score: evidence.length,
235
- evidence,
236
- role: spec.role,
237
- module: spec.module,
238
- });
239
- }
240
- scored.sort((a, b) => b.score - a.score);
241
- return scored;
242
- }
243
-
244
53
  function literal(node) {
245
54
  if (!node) {
246
55
  return null;
@@ -414,11 +223,12 @@ function keepReferencedFunctions(ast, classified) {
414
223
  }
415
224
 
416
225
  function analyze(ast, fileName) {
417
- const tag = parseFileTag(fileName);
226
+ const report = determineArchitecture(fileName, ast);
227
+ const tag = report.fileTag;
418
228
  const named = namesIn(ast);
419
229
  const { stats, folderName } = collectStats(ast);
420
230
  const consts = collectConsts(ast);
421
- const intents = scoreIntents(named);
231
+ const intents = scoreIntents(named, tag.runtime);
422
232
  const functions = classifyFunctions(ast, intents);
423
233
  const trivial = isTrivial(ast);
424
234
  const constsOnly = onlyConsts(ast);
@@ -451,10 +261,7 @@ function analyze(ast, fileName) {
451
261
  }
452
262
 
453
263
  const reasoning = [
454
- `tag ${tag.key} → ${tag.rojo} (${tag.emit})`,
455
- intents.length > 0
456
- ? `intents ${intents.map((item) => `${item.name}:${item.score}`).join(", ")}`
457
- : "intents (none — entry or module)",
264
+ ...report.reasoning,
458
265
  kind === "service"
459
266
  ? `roles ${["Main", roles.players ? "PlayersManager" : null, roles.cache ? "CacheController" : null, roles.domain, `${serviceName}Types`].filter(Boolean).join(", ")}`
460
267
  : `kind ${kind}`,
@@ -477,6 +284,7 @@ function analyze(ast, fileName) {
477
284
  consts,
478
285
  functions,
479
286
  fileName,
287
+ understand: report,
480
288
  reasoning,
481
289
  };
482
290
  }
@@ -498,7 +306,7 @@ function legacyOutName(relativeName) {
498
306
  .replace(new RegExp(`\\.${EXT}$`, "i"), ".luau");
499
307
  }
500
308
 
501
- module.exports = {
309
+ export {
502
310
  VALUE_CLASSES,
503
311
  INTENTS,
504
312
  walk,
@@ -510,4 +318,5 @@ module.exports = {
510
318
  legacyOutName,
511
319
  modernScriptOutName,
512
320
  looksLikeCacheSetup,
321
+ determineArchitecture,
513
322
  };
@@ -0,0 +1,72 @@
1
+ import { spawn, spawnSync } from "node:child_process";
2
+ import { childEnv } from "./safe-paths.js";
3
+
4
+ type ToolResult = {
5
+ status: number | null;
6
+ stdout: string;
7
+ stderr: string;
8
+ error?: string;
9
+ };
10
+
11
+ function runTool(bin: string, args: string[], timeout = 120000): ToolResult {
12
+ const result = spawnSync(bin, args, {
13
+ encoding: "utf8",
14
+ windowsHide: true,
15
+ shell: false,
16
+ env: childEnv(),
17
+ timeout,
18
+ });
19
+ return {
20
+ status: result.status,
21
+ stdout: String(result.stdout || ""),
22
+ stderr: String(result.stderr || ""),
23
+ error: result.error ? result.error.message : undefined,
24
+ };
25
+ }
26
+
27
+ function formatError(result: ToolResult): string {
28
+ return (result.stderr || result.stdout || result.error || "").trim();
29
+ }
30
+
31
+ export class ProcessOrchestrator {
32
+ public static async formatWithStyLua(filePath: string): Promise<void> {
33
+ await new Promise<void>((resolve) => {
34
+ const child = spawn("stylua", [filePath], {
35
+ windowsHide: true,
36
+ shell: false,
37
+ env: childEnv(),
38
+ });
39
+ child.on("error", (error) => {
40
+ console.warn(`[Aviso] Falha ao executar StyLua. Certifique-se de que está instalado. Detalhes: ${error.message}`);
41
+ resolve();
42
+ });
43
+ child.on("close", (status) => {
44
+ if (status !== 0) {
45
+ console.warn("[Aviso] Falha ao executar StyLua. Certifique-se de que está instalado.");
46
+ }
47
+ resolve();
48
+ });
49
+ });
50
+ }
51
+
52
+ public static formatWithStyLuaSync(filePath: string): void {
53
+ const result = runTool("stylua", [filePath]);
54
+ if (result.status !== 0) {
55
+ const details = formatError(result);
56
+ console.warn(`[Aviso] Falha ao executar StyLua. Certifique-se de que está instalado.${details ? ` Detalhes: ${details}` : ""}`);
57
+ }
58
+ }
59
+
60
+ public static async analyzeWithLuau(filePath: string): Promise<string | null> {
61
+ return ProcessOrchestrator.analyzeWithLuauSync(filePath);
62
+ }
63
+
64
+ public static analyzeWithLuauSync(filePath: string): string | null {
65
+ const result = runTool("luau-analyze", [filePath]);
66
+ const details = formatError(result);
67
+ if (result.status === 0) {
68
+ return details || null;
69
+ }
70
+ return details || "Falha na análise estrutural ou erros encontrados pelo analisador Luau.";
71
+ }
72
+ }