cluaupp 0.1.4 → 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 (113) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +12 -7
  3. package/bin/cluau.js +1 -1
  4. package/bin/cluaupp.js +1 -1
  5. package/docs/README.md +1 -1
  6. package/docs/architecture.md +52 -77
  7. package/docs/cli.md +29 -9
  8. package/docs/comparison.md +3 -3
  9. package/docs/config.md +6 -6
  10. package/docs/cpp-organization.md +2 -2
  11. package/docs/examples/index.md +2 -0
  12. package/docs/getting-started.md +7 -5
  13. package/docs/intellisense.md +49 -31
  14. package/docs/intro.md +2 -2
  15. package/docs/oop/file-tags.md +16 -17
  16. package/docs/oop/index.md +2 -2
  17. package/docs/oop/modules.md +3 -1
  18. package/docs/oop/services.md +7 -14
  19. package/docs/syntax.md +6 -5
  20. package/editors/vscode/extension.js +97 -146
  21. package/editors/vscode/package.json +6 -6
  22. package/examples/README.md +18 -0
  23. package/examples/game/.clangd +25 -0
  24. package/examples/game/.vscode/c_cpp_properties.json +27 -0
  25. package/examples/game/.vscode/extensions.json +5 -0
  26. package/examples/game/.vscode/settings.json +27 -0
  27. package/examples/game/Packages/.gitkeep +0 -0
  28. package/examples/game/cluaupp.config.json +6 -0
  29. package/examples/game/compile_flags.txt +6 -0
  30. package/examples/game/default.project.json +39 -0
  31. package/examples/game/package.json +9 -0
  32. package/examples/game/rokit.toml +5 -0
  33. package/examples/game/src/client/hud.client.cpp +12 -0
  34. package/{src → examples/game/src}/client/init.client.cpp +1 -0
  35. package/examples/game/src/server/combat.server.cpp +23 -0
  36. package/{src → examples/game/src}/server/leaderstats.server.cpp +1 -0
  37. package/examples/game/wally.toml +11 -0
  38. package/generated/api.js +51 -0
  39. package/generated/architecture.js +473 -0
  40. package/generated/ast.js +2 -0
  41. package/generated/cli.js +191 -0
  42. package/generated/compile.js +115 -0
  43. package/generated/editor-install.js +170 -0
  44. package/generated/emit.js +503 -0
  45. package/generated/emitter/luau-codegen.js +167 -0
  46. package/generated/emitter/translators.js +164 -0
  47. package/generated/headers.js +220 -0
  48. package/generated/index.html +49 -0
  49. package/generated/intellisense.js +277 -0
  50. package/generated/layout.js +112 -0
  51. package/generated/lex.js +146 -0
  52. package/generated/libs.js +484 -0
  53. package/generated/lsp.js +55 -0
  54. package/generated/package-info.js +11 -0
  55. package/generated/parse.js +607 -0
  56. package/generated/parser/collector.js +100 -0
  57. package/generated/parser/index.js +30 -0
  58. package/generated/preprocess.js +181 -0
  59. package/generated/system-understander.js +443 -0
  60. package/generated/transpile.js +66 -0
  61. package/generated/types.js +2 -0
  62. package/generated/understand.js +298 -0
  63. package/generated/utils/process-orchestrator.js +63 -0
  64. package/generated/utils/project.js +491 -0
  65. package/generated/utils/rojo-mapper.js +181 -0
  66. package/generated/utils/safe-paths.js +104 -0
  67. package/package.json +25 -8
  68. package/src/api.ts +53 -0
  69. package/src/{architecture.js → architecture.ts} +80 -33
  70. package/src/ast.ts +37 -0
  71. package/src/cli.ts +214 -0
  72. package/src/compile.ts +118 -0
  73. package/src/editor-install.ts +182 -0
  74. package/src/{emit.js → emit.ts} +5 -6
  75. package/src/emitter/luau-codegen.ts +187 -0
  76. package/src/emitter/translators.ts +168 -0
  77. package/src/headers.ts +233 -0
  78. package/src/intellisense.ts +286 -0
  79. package/src/{layout.js → layout.ts} +114 -129
  80. package/src/{lex.js → lex.ts} +4 -6
  81. package/src/{libs.js → libs.ts} +119 -46
  82. package/src/lsp.ts +60 -0
  83. package/src/package-info.ts +7 -0
  84. package/src/{parse.js → parse.ts} +3 -4
  85. package/src/parser/collector.ts +115 -0
  86. package/src/parser/index.ts +27 -0
  87. package/src/{preprocess.js → preprocess.ts} +64 -32
  88. package/src/system-understander.ts +501 -0
  89. package/src/transpile.ts +64 -0
  90. package/src/tree-sitter-cpp.d.ts +4 -0
  91. package/src/types.ts +81 -0
  92. package/src/{understand.js → understand.ts} +24 -213
  93. package/src/utils/process-orchestrator.ts +72 -0
  94. package/src/utils/project.ts +506 -0
  95. package/src/utils/rojo-mapper.ts +190 -0
  96. package/src/utils/safe-paths.ts +98 -0
  97. package/templates/game/.clangd +9 -0
  98. package/templates/game/.vscode/c_cpp_properties.json +1 -1
  99. package/templates/game/.vscode/extensions.json +5 -6
  100. package/templates/game/.vscode/settings.json +7 -6
  101. package/templates/game/cluaupp.config.json +2 -2
  102. package/templates/game/compile_flags.txt +1 -0
  103. package/templates/game/rokit.toml +5 -0
  104. package/templates/game/src/client/init.client.cpp +1 -0
  105. package/templates/game/src/server/leaderstats.server.cpp +1 -0
  106. package/src/api.js +0 -57
  107. package/src/cli.js +0 -481
  108. package/src/compile.js +0 -56
  109. package/src/editor-install.js +0 -218
  110. package/src/intellisense.js +0 -1368
  111. package/src/lsp.js +0 -227
  112. /package/{src → examples/game/src}/shared/config.cpp +0 -0
  113. /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,14 +284,17 @@ function analyze(ast, fileName) {
477
284
  consts,
478
285
  functions,
479
286
  fileName,
287
+ understand: report,
480
288
  reasoning,
481
289
  };
482
290
  }
483
291
 
484
292
  function modernScriptOutName(relativeName) {
485
- return String(relativeName)
486
- .replace(/\\/g, "/")
487
- .replace(/\.(server|client|plugin)\.(cpp|cc|cxx|c|h|hpp|hh)$/i, ".luau");
293
+ const rel = String(relativeName).replace(/\\/g, "/");
294
+ if (/\.plugin\.(cpp|cc|cxx|c|h|hpp|hh)$/i.test(rel)) {
295
+ return rel.replace(/\.plugin\.(cpp|cc|cxx|c|h|hpp|hh)$/i, ".luau");
296
+ }
297
+ return rel.replace(/\.(cpp|cc|cxx|c|h|hpp|hh)$/i, ".luau");
488
298
  }
489
299
 
490
300
  function legacyOutName(relativeName) {
@@ -496,7 +306,7 @@ function legacyOutName(relativeName) {
496
306
  .replace(new RegExp(`\\.${EXT}$`, "i"), ".luau");
497
307
  }
498
308
 
499
- module.exports = {
309
+ export {
500
310
  VALUE_CLASSES,
501
311
  INTENTS,
502
312
  walk,
@@ -508,4 +318,5 @@ module.exports = {
508
318
  legacyOutName,
509
319
  modernScriptOutName,
510
320
  looksLikeCacheSetup,
321
+ determineArchitecture,
511
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
+ }