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.
- package/CHANGELOG.md +21 -0
- package/README.md +12 -7
- package/bin/cluau.js +1 -1
- package/bin/cluaupp.js +1 -1
- package/docs/README.md +1 -1
- package/docs/architecture.md +52 -77
- package/docs/cli.md +29 -9
- package/docs/comparison.md +3 -3
- package/docs/config.md +6 -6
- package/docs/cpp-organization.md +2 -2
- package/docs/examples/index.md +2 -0
- package/docs/getting-started.md +7 -5
- package/docs/intellisense.md +49 -31
- package/docs/intro.md +2 -2
- package/docs/oop/file-tags.md +16 -17
- package/docs/oop/index.md +2 -2
- package/docs/oop/modules.md +3 -1
- package/docs/oop/services.md +7 -14
- package/docs/syntax.md +6 -5
- package/editors/vscode/extension.js +97 -146
- package/editors/vscode/package.json +6 -6
- package/examples/README.md +18 -0
- package/examples/game/.clangd +25 -0
- package/examples/game/.vscode/c_cpp_properties.json +27 -0
- package/examples/game/.vscode/extensions.json +5 -0
- package/examples/game/.vscode/settings.json +27 -0
- package/examples/game/Packages/.gitkeep +0 -0
- package/examples/game/cluaupp.config.json +6 -0
- package/examples/game/compile_flags.txt +6 -0
- package/examples/game/default.project.json +39 -0
- package/examples/game/package.json +9 -0
- package/examples/game/rokit.toml +5 -0
- package/examples/game/src/client/hud.client.cpp +12 -0
- package/{src → examples/game/src}/client/init.client.cpp +1 -0
- package/examples/game/src/server/combat.server.cpp +23 -0
- package/{src → examples/game/src}/server/leaderstats.server.cpp +1 -0
- package/examples/game/wally.toml +11 -0
- package/generated/api.js +51 -0
- package/generated/architecture.js +473 -0
- package/generated/ast.js +2 -0
- package/generated/cli.js +191 -0
- package/generated/compile.js +115 -0
- package/generated/editor-install.js +170 -0
- package/generated/emit.js +503 -0
- package/generated/emitter/luau-codegen.js +167 -0
- package/generated/emitter/translators.js +164 -0
- package/generated/headers.js +220 -0
- package/generated/index.html +49 -0
- package/generated/intellisense.js +277 -0
- package/generated/layout.js +112 -0
- package/generated/lex.js +146 -0
- package/generated/libs.js +484 -0
- package/generated/lsp.js +55 -0
- package/generated/package-info.js +11 -0
- package/generated/parse.js +607 -0
- package/generated/parser/collector.js +100 -0
- package/generated/parser/index.js +30 -0
- package/generated/preprocess.js +181 -0
- package/generated/system-understander.js +443 -0
- package/generated/transpile.js +66 -0
- package/generated/types.js +2 -0
- package/generated/understand.js +298 -0
- package/generated/utils/process-orchestrator.js +63 -0
- package/generated/utils/project.js +491 -0
- package/generated/utils/rojo-mapper.js +181 -0
- package/generated/utils/safe-paths.js +104 -0
- package/package.json +25 -8
- package/src/api.ts +53 -0
- package/src/{architecture.js → architecture.ts} +80 -33
- package/src/ast.ts +37 -0
- package/src/cli.ts +214 -0
- package/src/compile.ts +118 -0
- package/src/editor-install.ts +182 -0
- package/src/{emit.js → emit.ts} +5 -6
- package/src/emitter/luau-codegen.ts +187 -0
- package/src/emitter/translators.ts +168 -0
- package/src/headers.ts +233 -0
- package/src/intellisense.ts +286 -0
- package/src/{layout.js → layout.ts} +114 -129
- package/src/{lex.js → lex.ts} +4 -6
- package/src/{libs.js → libs.ts} +119 -46
- package/src/lsp.ts +60 -0
- package/src/package-info.ts +7 -0
- package/src/{parse.js → parse.ts} +3 -4
- package/src/parser/collector.ts +115 -0
- package/src/parser/index.ts +27 -0
- package/src/{preprocess.js → preprocess.ts} +64 -32
- package/src/system-understander.ts +501 -0
- package/src/transpile.ts +64 -0
- package/src/tree-sitter-cpp.d.ts +4 -0
- package/src/types.ts +81 -0
- package/src/{understand.js → understand.ts} +24 -213
- package/src/utils/process-orchestrator.ts +72 -0
- package/src/utils/project.ts +506 -0
- package/src/utils/rojo-mapper.ts +190 -0
- package/src/utils/safe-paths.ts +98 -0
- package/templates/game/.clangd +9 -0
- package/templates/game/.vscode/c_cpp_properties.json +1 -1
- package/templates/game/.vscode/extensions.json +5 -6
- package/templates/game/.vscode/settings.json +7 -6
- package/templates/game/cluaupp.config.json +2 -2
- package/templates/game/compile_flags.txt +1 -0
- package/templates/game/rokit.toml +5 -0
- package/templates/game/src/client/init.client.cpp +1 -0
- package/templates/game/src/server/leaderstats.server.cpp +1 -0
- package/src/api.js +0 -57
- package/src/cli.js +0 -481
- package/src/compile.js +0 -56
- package/src/editor-install.js +0 -218
- package/src/intellisense.js +0 -1368
- package/src/lsp.js +0 -227
- /package/{src → examples/game/src}/shared/config.cpp +0 -0
- /package/{src → examples/game/src}/shared/config.h +0 -0
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
486
|
-
|
|
487
|
-
.replace(/\.
|
|
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
|
-
|
|
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
|
+
}
|