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
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.collectState = collectState;
7
+ exports.transpileSource = transpileSource;
8
+ exports.outputNameFor = outputNameFor;
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const collector_js_1 = require("./parser/collector.js");
11
+ const index_js_1 = require("./parser/index.js");
12
+ const luau_codegen_js_1 = require("./emitter/luau-codegen.js");
13
+ const compile_js_1 = require("./compile.js");
14
+ const system_understander_js_1 = require("./system-understander.js");
15
+ function shouldMerge(name, contents) {
16
+ const file = String(name || "").replace(/\\/g, "/");
17
+ if (/\.json$/i.test(file) || /\.meta\.json$/i.test(file)) {
18
+ return false;
19
+ }
20
+ const trimmed = String(contents || "").trim();
21
+ return !(trimmed.startsWith("{") || trimmed.startsWith("["));
22
+ }
23
+ function collectState(source, mapper, options) {
24
+ const tree = (0, index_js_1.parseCpp)(source);
25
+ if (!tree) {
26
+ const state = luau_codegen_js_1.LuauCodeEmitter.emptyState(options.strict === true);
27
+ return state;
28
+ }
29
+ const collector = new collector_js_1.ASTCollector(mapper, {
30
+ fileName: options.relativeName || options.filePath || "input.cpp",
31
+ sourcePath: options.filePath,
32
+ srcDir: options.srcDir,
33
+ outDir: options.outDir,
34
+ strict: options.strict,
35
+ });
36
+ const state = collector.collect(tree.rootNode);
37
+ const report = (0, system_understander_js_1.determineArchitecture)(options.relativeName || options.filePath || "input.cpp", null, tree.rootNode);
38
+ for (const service of report.injectedServices) {
39
+ state.robloxServices.add(service);
40
+ }
41
+ for (const line of report.reasoning) {
42
+ state.headerLines.push(`-- ${line}`);
43
+ }
44
+ return state;
45
+ }
46
+ function transpileSource(source, fileName, options, mapper) {
47
+ const state = collectState(source, mapper, options);
48
+ if (state.strict) {
49
+ options.strict = true;
50
+ }
51
+ const compiled = (0, compile_js_1.compileService)(source, fileName, options);
52
+ return {
53
+ ...compiled,
54
+ files: compiled.files.map((file) => ({
55
+ ...file,
56
+ contents: shouldMerge(file.name, file.contents) ? luau_codegen_js_1.LuauCodeEmitter.merge(state, file.contents) : file.contents,
57
+ })),
58
+ };
59
+ }
60
+ function outputNameFor(inputFile, outputPath) {
61
+ if (/\.luau$/i.test(outputPath)) {
62
+ return outputPath;
63
+ }
64
+ const base = node_path_1.default.basename(inputFile).replace(/\.(cpp|cc|cxx|c|h|hpp|hh)$/i, ".luau");
65
+ return node_path_1.default.join(outputPath, base);
66
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,298 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.determineArchitecture = exports.scoreIntents = exports.parseFileTag = exports.namesIn = exports.walk = exports.INTENTS = exports.VALUE_CLASSES = void 0;
7
+ exports.toPascalServiceName = toPascalServiceName;
8
+ exports.analyze = analyze;
9
+ exports.legacyOutName = legacyOutName;
10
+ exports.modernScriptOutName = modernScriptOutName;
11
+ exports.looksLikeCacheSetup = looksLikeCacheSetup;
12
+ // @ts-nocheck
13
+ const node_path_1 = __importDefault(require("node:path"));
14
+ const api_js_1 = require("./api.js");
15
+ const system_understander_js_1 = require("./system-understander.js");
16
+ Object.defineProperty(exports, "INTENTS", { enumerable: true, get: function () { return system_understander_js_1.INTENTS; } });
17
+ Object.defineProperty(exports, "walk", { enumerable: true, get: function () { return system_understander_js_1.walk; } });
18
+ Object.defineProperty(exports, "namesIn", { enumerable: true, get: function () { return system_understander_js_1.namesIn; } });
19
+ Object.defineProperty(exports, "parseFileTag", { enumerable: true, get: function () { return system_understander_js_1.parseFileTag; } });
20
+ Object.defineProperty(exports, "scoreIntents", { enumerable: true, get: function () { return system_understander_js_1.scoreIntents; } });
21
+ Object.defineProperty(exports, "determineArchitecture", { enumerable: true, get: function () { return system_understander_js_1.determineArchitecture; } });
22
+ const VALUE_CLASSES = {
23
+ IntValue: { luau: "number", instance: "IntValue", fallback: "0" },
24
+ NumberValue: { luau: "number", instance: "NumberValue", fallback: "0" },
25
+ StringValue: { luau: "string", instance: "StringValue", fallback: '""' },
26
+ BoolValue: { luau: "boolean", instance: "BoolValue", fallback: "false" },
27
+ };
28
+ exports.VALUE_CLASSES = VALUE_CLASSES;
29
+ const EXT = "(cpp|cc|cxx|c|h|hpp|hh)";
30
+ function capitalize(word) {
31
+ if (!word) {
32
+ return word;
33
+ }
34
+ return word.charAt(0).toUpperCase() + word.slice(1);
35
+ }
36
+ function toPascalServiceName(fileName) {
37
+ let base = node_path_1.default.basename(fileName).replace(new RegExp(`\\.${EXT}$`, "i"), "");
38
+ base = base.replace(/\.legacy\.(server|client)$/i, "");
39
+ base = base.replace(/\.(server|client)$/i, "");
40
+ if (/^init$/i.test(base)) {
41
+ const folder = node_path_1.default.basename(node_path_1.default.dirname(fileName) || "");
42
+ if (folder && folder !== "." && folder !== "src") {
43
+ return capitalize(folder);
44
+ }
45
+ return "Init";
46
+ }
47
+ if (/stats$/i.test(base) && !/Stats$/.test(base)) {
48
+ return capitalize(base.slice(0, -5)) + "Stats";
49
+ }
50
+ if (/[-_]/.test(base)) {
51
+ return base
52
+ .split(/[-_]+/)
53
+ .filter(Boolean)
54
+ .map(capitalize)
55
+ .join("");
56
+ }
57
+ return capitalize(base);
58
+ }
59
+ function literal(node) {
60
+ if (!node) {
61
+ return null;
62
+ }
63
+ if (node.type === "number") {
64
+ return node.value;
65
+ }
66
+ if (node.type === "string") {
67
+ return `"${node.value}"`;
68
+ }
69
+ if (node.type === "bool") {
70
+ return node.value ? "true" : "false";
71
+ }
72
+ if (node.type === "null") {
73
+ return "nil";
74
+ }
75
+ return null;
76
+ }
77
+ function collectStats(ast) {
78
+ const stats = [];
79
+ let folderName = null;
80
+ const seen = new Set();
81
+ for (const decl of ast.body || []) {
82
+ if (decl.type !== "function" || !decl.body) {
83
+ continue;
84
+ }
85
+ const locals = new Map();
86
+ for (const stmt of decl.body) {
87
+ if (stmt.type === "decl" && stmt.value && stmt.value.type === "new") {
88
+ locals.set(stmt.name, {
89
+ className: stmt.value.className,
90
+ statName: null,
91
+ defaultValue: null,
92
+ });
93
+ }
94
+ if (stmt.type !== "expr" || !stmt.expr || stmt.expr.type !== "assign") {
95
+ continue;
96
+ }
97
+ const left = stmt.expr.left;
98
+ if (!left || left.type !== "member" || !left.object || left.object.type !== "ident") {
99
+ continue;
100
+ }
101
+ const record = locals.get(left.object.name);
102
+ if (!record) {
103
+ continue;
104
+ }
105
+ if (left.name === "Name" && stmt.expr.right && stmt.expr.right.type === "string") {
106
+ record.statName = stmt.expr.right.value;
107
+ if (record.className === "Folder") {
108
+ folderName = record.statName;
109
+ }
110
+ }
111
+ if (left.name === "Value") {
112
+ record.defaultValue = literal(stmt.expr.right);
113
+ }
114
+ }
115
+ for (const record of locals.values()) {
116
+ const spec = VALUE_CLASSES[record.className];
117
+ if (!spec || !record.statName || seen.has(record.statName)) {
118
+ continue;
119
+ }
120
+ seen.add(record.statName);
121
+ stats.push({
122
+ name: record.statName,
123
+ instance: spec.instance,
124
+ luau: spec.luau,
125
+ defaultValue: record.defaultValue != null ? record.defaultValue : spec.fallback,
126
+ });
127
+ }
128
+ }
129
+ return { stats, folderName: folderName || (stats.length > 0 ? "leaderstats" : null) };
130
+ }
131
+ function collectConsts(ast) {
132
+ const consts = [];
133
+ for (const decl of ast.body || []) {
134
+ if (decl.type === "decl" && decl.isConst) {
135
+ consts.push({
136
+ name: decl.name,
137
+ luau: (0, api_js_1.luauType)(decl.valueType) || "number",
138
+ value: literal(decl.value) || "nil",
139
+ });
140
+ }
141
+ }
142
+ return consts;
143
+ }
144
+ function onlyConsts(ast) {
145
+ const body = ast.body || [];
146
+ if (body.length === 0) {
147
+ return false;
148
+ }
149
+ return body.every((node) => node.type === "decl" || node.type === "proto");
150
+ }
151
+ function looksLikeCacheSetup(fn) {
152
+ if (!fn || !fn.body) {
153
+ return false;
154
+ }
155
+ const named = (0, system_understander_js_1.namesIn)(fn);
156
+ return [...Object.keys(VALUE_CLASSES), "Folder"].some((name) => named.has(name));
157
+ }
158
+ function isTrivial(ast) {
159
+ const fns = (ast.body || []).filter((node) => node.type === "function");
160
+ if (fns.length === 0) {
161
+ return (ast.body || []).every((node) => node.type === "proto" || node.type === "decl");
162
+ }
163
+ if (fns.some((fn) => fn.name !== "init")) {
164
+ return false;
165
+ }
166
+ const init = fns.find((fn) => fn.name === "init");
167
+ const named = (0, system_understander_js_1.namesIn)(init || { body: [] }, { strings: false });
168
+ const interesting = [...named].filter((name) => !["print", "warn", "error"].includes(name) && !/^[0-9.]+$/.test(name));
169
+ return interesting.length === 0;
170
+ }
171
+ function classifyFunctions(ast, intents) {
172
+ const primary = intents.find((item) => item.name !== "players" && item.name !== "cache");
173
+ const classified = [];
174
+ for (const decl of ast.body || []) {
175
+ if (decl.type !== "function" || decl.name === "init") {
176
+ continue;
177
+ }
178
+ const named = (0, system_understander_js_1.namesIn)(decl);
179
+ const localIntents = (0, system_understander_js_1.scoreIntents)(named);
180
+ let absorb = "none";
181
+ if (looksLikeCacheSetup(decl)) {
182
+ absorb = "cache";
183
+ }
184
+ const top = localIntents[0];
185
+ classified.push({
186
+ name: decl.name,
187
+ decl,
188
+ intent: top ? top.name : primary ? primary.name : "domain",
189
+ absorb,
190
+ });
191
+ }
192
+ keepReferencedFunctions(ast, classified);
193
+ return classified;
194
+ }
195
+ function keepReferencedFunctions(ast, classified) {
196
+ const kept = new Set(["init"]);
197
+ for (const item of classified) {
198
+ if (item.absorb !== "cache") {
199
+ kept.add(item.name);
200
+ }
201
+ }
202
+ let changed = true;
203
+ while (changed) {
204
+ changed = false;
205
+ for (const decl of ast.body || []) {
206
+ if (decl.type !== "function" || !kept.has(decl.name)) {
207
+ continue;
208
+ }
209
+ const named = (0, system_understander_js_1.namesIn)(decl);
210
+ for (const item of classified) {
211
+ if (item.absorb === "cache" && named.has(item.name)) {
212
+ item.absorb = "none";
213
+ kept.add(item.name);
214
+ changed = true;
215
+ }
216
+ }
217
+ }
218
+ }
219
+ }
220
+ function analyze(ast, fileName) {
221
+ const report = (0, system_understander_js_1.determineArchitecture)(fileName, ast);
222
+ const tag = report.fileTag;
223
+ const named = (0, system_understander_js_1.namesIn)(ast);
224
+ const { stats, folderName } = collectStats(ast);
225
+ const consts = collectConsts(ast);
226
+ const intents = (0, system_understander_js_1.scoreIntents)(named, tag.runtime);
227
+ const functions = classifyFunctions(ast, intents);
228
+ const trivial = isTrivial(ast);
229
+ const constsOnly = onlyConsts(ast);
230
+ const hasPlayers = intents.some((item) => item.name === "players");
231
+ const hasCache = intents.some((item) => item.name === "cache") || stats.length > 0;
232
+ const primaryDomain = intents.find((item) => item.name !== "players" && item.name !== "cache") || null;
233
+ const serviceName = toPascalServiceName(fileName);
234
+ const configLike = tag.emit === "module" &&
235
+ (constsOnly || (consts.length > 0 && !hasPlayers && stats.length === 0 && !primaryDomain));
236
+ let kind = "flat";
237
+ if (tag.emit === "legacy") {
238
+ kind = "legacy";
239
+ }
240
+ else if (tag.emit === "module") {
241
+ kind = configLike ? "config" : "module";
242
+ }
243
+ else if (tag.emit === "service") {
244
+ kind = trivial ? "flat" : "service";
245
+ }
246
+ const roles = {
247
+ players: kind === "service" && hasPlayers,
248
+ cache: kind === "service" && (hasCache || stats.length > 0),
249
+ domain: null,
250
+ };
251
+ if (kind === "service" && primaryDomain) {
252
+ roles.domain = primaryDomain.module;
253
+ }
254
+ else if (kind === "service" && !roles.cache && !roles.players) {
255
+ roles.domain = `${serviceName}Controller`;
256
+ }
257
+ const reasoning = [
258
+ ...report.reasoning,
259
+ kind === "service"
260
+ ? `roles ${["Main", roles.players ? "PlayersManager" : null, roles.cache ? "CacheController" : null, roles.domain, `${serviceName}Types`].filter(Boolean).join(", ")}`
261
+ : `kind ${kind}`,
262
+ ];
263
+ return {
264
+ tag,
265
+ kind,
266
+ onlyConsts: constsOnly,
267
+ trivial,
268
+ serviceName,
269
+ typesName: `${serviceName}Types`,
270
+ isClient: tag.runtime === "client",
271
+ runContext: tag.runContext || null,
272
+ intents,
273
+ primaryDomain,
274
+ roles,
275
+ stats,
276
+ folderName,
277
+ consts,
278
+ functions,
279
+ fileName,
280
+ understand: report,
281
+ reasoning,
282
+ };
283
+ }
284
+ function modernScriptOutName(relativeName) {
285
+ const rel = String(relativeName).replace(/\\/g, "/");
286
+ if (/\.plugin\.(cpp|cc|cxx|c|h|hpp|hh)$/i.test(rel)) {
287
+ return rel.replace(/\.plugin\.(cpp|cc|cxx|c|h|hpp|hh)$/i, ".luau");
288
+ }
289
+ return rel.replace(/\.(cpp|cc|cxx|c|h|hpp|hh)$/i, ".luau");
290
+ }
291
+ function legacyOutName(relativeName) {
292
+ return String(relativeName)
293
+ .replace(/\\/g, "/")
294
+ .replace(/\.legacy\.plugin\./i, ".")
295
+ .replace(/\.legacy\.(server|client)\./i, ".$1.")
296
+ .replace(/\.legacy\./i, ".server.")
297
+ .replace(new RegExp(`\\.${EXT}$`, "i"), ".luau");
298
+ }
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ProcessOrchestrator = void 0;
4
+ const node_child_process_1 = require("node:child_process");
5
+ const safe_paths_js_1 = require("./safe-paths.js");
6
+ function runTool(bin, args, timeout = 120000) {
7
+ const result = (0, node_child_process_1.spawnSync)(bin, args, {
8
+ encoding: "utf8",
9
+ windowsHide: true,
10
+ shell: false,
11
+ env: (0, safe_paths_js_1.childEnv)(),
12
+ timeout,
13
+ });
14
+ return {
15
+ status: result.status,
16
+ stdout: String(result.stdout || ""),
17
+ stderr: String(result.stderr || ""),
18
+ error: result.error ? result.error.message : undefined,
19
+ };
20
+ }
21
+ function formatError(result) {
22
+ return (result.stderr || result.stdout || result.error || "").trim();
23
+ }
24
+ class ProcessOrchestrator {
25
+ static async formatWithStyLua(filePath) {
26
+ await new Promise((resolve) => {
27
+ const child = (0, node_child_process_1.spawn)("stylua", [filePath], {
28
+ windowsHide: true,
29
+ shell: false,
30
+ env: (0, safe_paths_js_1.childEnv)(),
31
+ });
32
+ child.on("error", (error) => {
33
+ console.warn(`[Aviso] Falha ao executar StyLua. Certifique-se de que está instalado. Detalhes: ${error.message}`);
34
+ resolve();
35
+ });
36
+ child.on("close", (status) => {
37
+ if (status !== 0) {
38
+ console.warn("[Aviso] Falha ao executar StyLua. Certifique-se de que está instalado.");
39
+ }
40
+ resolve();
41
+ });
42
+ });
43
+ }
44
+ static formatWithStyLuaSync(filePath) {
45
+ const result = runTool("stylua", [filePath]);
46
+ if (result.status !== 0) {
47
+ const details = formatError(result);
48
+ console.warn(`[Aviso] Falha ao executar StyLua. Certifique-se de que está instalado.${details ? ` Detalhes: ${details}` : ""}`);
49
+ }
50
+ }
51
+ static async analyzeWithLuau(filePath) {
52
+ return ProcessOrchestrator.analyzeWithLuauSync(filePath);
53
+ }
54
+ static analyzeWithLuauSync(filePath) {
55
+ const result = runTool("luau-analyze", [filePath]);
56
+ const details = formatError(result);
57
+ if (result.status === 0) {
58
+ return details || null;
59
+ }
60
+ return details || "Falha na análise estrutural ou erros encontrados pelo analisador Luau.";
61
+ }
62
+ }
63
+ exports.ProcessOrchestrator = ProcessOrchestrator;